In common implementations the vtable pointer is always the first word inside the object. Given an arbitrary pointer to an object, the offset of this vtable pointer relative to what you're pointing to is always computed statically at compile time. Unless you're using multiple inheritance, this offset is usually zero because derived object pointers in a single-inheritance hierarchy actually always point to their base.
If you're using multiple inheritance then an object can have multiple vtable pointers, but again which one you need to use is known at compile-time based on which class the virtual function you're calling is declared within, and the type of pointer you have.
Once you have the vtable you then have to locate the function pointer for the function you're calling. Again, this is usually a compile-time constant offset from the start of the vtable. This ceases to be true when you have 'virtual inheritance' (not to be confused with virtual functions), when another indirection to find this function pointer is required.
Here are some examples:
https://godbolt.org/g/N2XcV7
You'll notice that the get_square() function, which returns a member function pointer to the virtual square function, doesn't even return any memory addresses, just metadata and an offset