18

I'm experimenting with C++ to understand how class/structures and their respective objects are laid out in memory and I understood that each field of a class/structure is an offset into their respective object (so I can have a member variable pointer).

I don't understand why, even if I can have member function pointers, the following code doesn't work:

struct mystruct
{
    void function()
    {
        cout << "hello world";
    }
    int c;
};

int main() 
{ 
    unsigned int offset_from_start_structure = (unsigned int)(&((mystruct*)0)->c);
    unsigned int offset_from_start_structure2 = (unsigned int)(&((mystruct*)0)->function); // ERROR - error C2276: '&' : illegal operation on bound member function expression



    return 0;
}

My question is: why does the line

unsigned int offset_from_start_structure = (unsigned int)(&((mystruct*)0)->c);

compile and returns me the offset of the "c" field from the start of the structure and the line

unsigned int offset_from_start_structure2 = (unsigned int)(&((mystruct*)0)->function);

doesn't even compile?

Johnny Pauling
  • 12,701
  • 18
  • 65
  • 108
  • 7
    Member functions are not stored in objects (why would they? They are the same for all objects of that type). And if it helps (maybe not; maybe it only adds confusion but I'll try anyway) member function pointers are *not* pointers. – R. Martinho Fernandes Mar 22 '13 at 14:10
  • What were you expecting the contents of the _function_ to be, in memory? Functions are not data. They are code. – Lightness Races in Orbit Mar 22 '13 at 14:12
  • 1
    "I'm experimenting with C++ to understand how class/structures and their respective objects are laid out in memory" implementation details, has nothing to with the language – Cat Plus Plus Mar 22 '13 at 14:12
  • @Johnny Pauling Have a look at [this](http://stackoverflow.com/questions/12660829/how-does-c-store-functions-and-objects-in-memory) as well. – Suvarna Pattayil Mar 22 '13 at 14:14

2 Answers2

23

Member functions or pointers to them aren't stored in the object. (virtual functions are typically called through a pointer stored in a table to which an object has a single pointer to) This would be a huge waste of memory. They're typically stored in a code memory section, and are known to the compiler. The object (*this) is typically passed as an invisible parameter so the functions know on which object to operate when they are called.

So, in layman terms, you'd have

 0x10001000    void A::foo
 ..........    {code for A::foo}

and

 push a;
 call A::foo (0x10001000)
 pop a;

where a is the object you're calling foo on.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • Thank you for your clarification but why doesn't the line work? It should return a fixed address at least – Johnny Pauling Mar 22 '13 at 14:17
  • @JohnnyPauling no, it shouldn't. The `&((mystruct*)0)->c` is undefined behavior, you can't just dereference a null pointer. Google for `pointer to member function` and you'll find the proper way of doing it. – Luchian Grigore Mar 22 '13 at 14:18
  • I don't think it's an undefined behavior, it returns the offset of the c field every time I call it and it works for other consecutive fields too so I was wondering why it won't work with a function – Johnny Pauling Mar 22 '13 at 14:20
  • @JohnnyPauling regardless of whether you think so or not, it is undefined behavior. The `->` attempts to dereference an invalid pointer. – Luchian Grigore Mar 22 '13 at 14:21
  • I believe you, but why won't the other line compile? It should be the same undefined behaviour as well – Johnny Pauling Mar 22 '13 at 14:22
  • 1
    @JohnnyPauling the compiler checks for valid syntax (not so much for UB). The second line is simply invalid syntax. That's not how you "get" a pointer to a member function, and I already told you how you can find out how to do it. – Luchian Grigore Mar 22 '13 at 14:23
  • @JohnnyPauling: The `p->memberFunc` expression is a funny beast, e.g. it has no type. All you can do with it is call it. If C++ had been a designed instead of an evolved language, then this would have been something like a C# delegate, but no, it just isn't something you can pass around. – Cheers and hth. - Alf Mar 22 '13 at 14:27
  • @LuchianGrigore: What is the *huge* memory waste to referred to? Is it not just sizeof(ptr) * num_of_virtual_functions_in_the_scope? Did you mean that overhead itself as huge or is there any other memory wastage as well? Thanks in advance. – syam Jun 14 '20 at 07:41
6

Member function pointers are in practice not stored in objects: there's no need. The C++ standard doesn't specify exactly how e.g. virtual functions are to be implemented, but the common practice for virtual member functions is that each object contains a pointer to a table of function pointers; this pointer is called a vtable pointer.

You might try to get hold of “Inside the C++ object model” by Stanley Lippman.

Or, you might just try to get hold of my old pointers tutorial, which was once referenced from Wikipedia's pointers article, before my then homepage site disappeared.


Regarding the second question, why taking the address of p->memberFunc makes the compiler choke a little, well that expression has no type, it's just a syntactical entity, which you can apply an argument list to in order to call the function.

To wit,

struct S
{
    void foo() {}
};

#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
    S* p = 0;
    typeid( &p->foo );
} 

Compilation:

[W:\dev\test]
> g++ foo.cpp
foo.cpp: In function 'int main()':
foo.cpp:12:17: error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function.  Say '&S::foo' [-fpermissive]
foo.cpp:12:22: warning: value computed is not used [-Wunused-value]
foo.cpp:12:22: warning: statement has no effect [-Wunused-value]

[W:\dev\test]
> _
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331