Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to understand "((size_t) &((TYPE *)0)->MEMBER)"?

Tags:

c

kernel

the codes in linux-2.6.16/include/linux/stddef.h is:

 #undef offsetof
 #ifdef __compiler_offsetof
 #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
 #else
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 #endif

#define DNAME_INLINE_LEN (sizeof(struct dentry)-offsetof(struct dentry,d_iname))

How to understand "((size_t) &((TYPE *)0)->MEMBER)"?
Thank you

like image 499
lxgeek Avatar asked Sep 01 '13 02:09

lxgeek


2 Answers

The sequence of operations can be illustrated in the following way:

    1.                         0
    2.                ((TYPE *)0)
    3.              ( ((TYPE *)0)->MEMBER )
    4.             &( ((TYPE *)0)->MEMBER )
    5.  ( (size_t) &( ((TYPE *)0)->MEMBER )
  1. Everything starts with number 0.
  2. 0 is cast to a pointer to struct TYPE. IOW, we are making the compiler believe that there is such a struct at the beginning of the program's data segment (which is generally dangerous, but I'll mention later why not in this particular case).
  3. Then, member MEMBER within this struct is referenced.
  4. Its contents are not used, but its address taken (with &),
  5. And this address converted back to a numeric type size_t.

As the address for the "start" of the struct was specified as 0, the address of MEMBER (when converted to a number) is its offset within the struct.

The reason why this particular code is innocuous is that no memory position is ever written, not even accessed. Everything involves just pointers to those positions (but not their contents) and numbers. All of them held in machine registers or the usual local stack.

The following expression:

      ( (size_t) &( ((TYPE *)3264)->MEMBER ) - 3264 )

would also work. 3264 stands for any number of your choosing. Added a parentheses set to make it more legible.

like image 141
Mario Rossi Avatar answered Oct 06 '22 05:10

Mario Rossi


This is a hack that lets you emulate the functionality of offsetof when there is no support by the compiler: it exploits the fact that the address of the MEMBER equals its offset when the base address is set to zero.

Consider this example:

struct Test {
    char text[32];
    int count;
}

If struct Test is allocated at the address 0xC000, then the address of text would be 0xC000, and the address of count would be 0xC020. However, if the base address is zero (which is not allowed by the standard), then the address of text would be zero, and the address of the count would be 0x20. Casting these addresses to size_t gives you the offset of the corresponding members.

like image 34
Sergey Kalinichenko Avatar answered Oct 06 '22 05:10

Sergey Kalinichenko