Checking sizeof or the offset of a member
October 29th, 2009 § 8 Comments
Suppose we wanted to check the sizeof or the offset of a certain member within our struct (or class), without actually having an instantiated object to run the needed operations on. How would you do that?
Here’s the scenario:
struct S {
int x;
int y;
}
int main () {
// for sizeof: sizeof(S::y) obviously doesn't work..
// for offset: &S::y is a pointer-to-member, which is wrong..
// how do we compute these two values?
}
Granted we’re not interested in instantiating a new object to check these values on, we’re in a bit of a pickle here. Luckily, there’s a trick to overcome this.
The following will work:
int main () {
using std::cout;
using std::endl;
cout << "offset: " << &reinterpret_cast<S*>(0)->y << endl;
cout << "sizeof: " << sizeof(reinterpret_cast<S*>(0)->y) << endl;
}
This is how its done within Linux kernel implementation.
As pointed out in the comments below, we’re actually dereferencing a NULL value, which makes the code ill-formed according to the standard – although it works on nearly any interesting compiler. To avoid this for the sizeof operation we can use the next neat trick:
S* dummy (); // just define a dummy function sizeof(dummy()->y); // sizeof only needs the type of the expression
I’m not sure if it could be avoided for the offset-of operation. Looks like the presented implementation is the only way, even according to wikipedia. For a portable version, the offsetof() macro from stddef.h should be used, as it is implementation specific.
An important side note regarding the member offset calculation: since C++ allows overloading of operator& (address-of operator), it could possibly act differently and return something unexpected. Using a work-around such as boost::addressof will completely solve this issue.
In C++0x you can do sizeof(MyClass::Member)
http://en.wikipedia.org/wiki/C%2B%2B0x#Allow_sizeof_to_work_on_members_of_classes_without_an_explicit_object
Btw, NULL dereference is undefined behaviour according to the standard.
So you’d better use offsetof macro instead.
According to wikipedia, the macro offsetof() actually uses the same trick: http://en.wikipedia.org/wiki/Offsetof
#define offsetof(st, m) ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))That’s right.
But this is a private implementation.
The compiler can do this because it knows better about the implementation, but you shall not do it
Although you can do smth like:
#if (defined(GCC) && VERSION > 2) && ( defined(MSVC) && VERSION > 6 ) and ( defined(INTEL) && VERSION > 5)
#define SIZEOF_MEMBER(class ,member) ….
#else
#error Undefined behaviour for unknown compilers.
#endif
I agree, although if you look close enough – your list contains nearly every popular compiler. I tried googling and couldn’t find a different implementation.
The post has been slightly updated.
Hi,
You can avoid the deref of a null pointer by using some other suitably aligned constant (like 16384) for the pointer and then subtracting it out again. Ugly, but it works and doesn’t require a dummy object.
Jon
You’re over engineering the ‘sizeof member without instance’ scenario. Remember, ‘sizeof’ is a compile-time construct. No instance of ‘X’ in the following example will be created.
struct X { int member; X() { std::cout << "X::X()" << std::endl; } ~X() { std::cout << "X::~X()" << std::endl; } }; int main() { std::cout << sizeof( X().member ); return 0; }This is indeed nice and easy for the given case, but it isn’t guaranteed to work in the general case; What would you do if there was no default constructor? Or no public one, at all?
Yes, you’re right. Although I think it was still worth mentioning.