Checking sizeof or the offset of a member

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.

8 thoughts on “Checking sizeof or the offset of a member

      1. 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

        1. 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.

    1. 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

  1. 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;
    }
    
    1. 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?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>