In OpenGL 3+ core profile, the last argument to glDrawElements() is an offset into the buffer currently bound to GL_ELEMENT_ARRAY_BUFFER (which, unlike other buffer bindings, is stored in the current VAO). It is an error to call glDrawElements() if no buffer is bound to that target.
In earlier versions of OpenGL, and in the compatibility profile, if no buffer is bound to that target then the last argument is treated as a pointer to client memory.
A similar principle applies to most other buffer bindings: OpenGL 3+ core profile requires a buffer to be bound, while the compatibility profile sources data from client memory if no buffer is bound. If you're planning on transitioning legacy code to OpenGL 3+ core profile, check the descriptions of the various targets in the glBindBuffer() documentation. Most of the functions which used to take a pointer to "bulk" data now require that data to be copied to a buffer first. The main exception is that texture data can still be sourced directly from client memory, even in the core profile; you don't have to use GL_PIXEL_UNPACK_BUFFER.