Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difficulty in understanding the offsetof MACRO

I have been searching very long and hard (links at the very end) for an explanation of the implementation of the offsetof MACRO :

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

Particularly, the dereferencing of NULL to obtain the offset of the member in the structure. Many articles gloss over the reason by saying that the NULL pointer is actually never really dereferenced, but that doesn't make sense to me.

Here are some of the links I have tried understanding:

  1. http://www.viva64.com/en/b/0301/
  2. http://www.embedded.com/design/prototyping-and-development/4024941/Learn-a-new-trick-with-the-offsetof--macro
  3. http://www.geeksforgeeks.org/the-offsetof-macro/
  4. How does the C offsetof macro work?
  5. Does dereference a NULL pointer guarantee to crash a program in C/C++?

What I'm looking for and trying to understand is a step by step , broken down understanding of how the compiler interprets the MACRO definition, which will eventually explain how a NULL pointer is not actually being dereferenced.

EDIT: Even though other questions answered my query, they dindt make sense to me, as pointed out in the original post. The answer by @dasblinkenlight sheds light on the exact problem I had with ansers to other questions i.e how is it that we're not actually dereferencing the pointer .

like image 288
maverick Avatar asked Feb 08 '23 07:02

maverick


1 Answers

Particularly, the dereferencing of NULL to obtain the offset of the member in the structure.

There is no dereferencing of the pointer going on, because the effect of operator -> are undone by the operator &:

  • This would be a dereference of NULL: ((TYPE *)0)->MEMBER
  • Adding & in front of it makes it an address computation: &((TYPE *)0)->MEMBER

Many articles gloss over the reason by saying that the NULL pointer is actually never really dereferenced, but that doesn't make sense to me.

Consider this example as an illustration:

int a;
int *p = &a;
int b = *p;     // <<== This is a dereference
int *q = &(*p); // <<== This is not a dereference

The two operators, * and &, undo the effect of each other. Operator -> is a "syntactic sugar" on top of * and ., so & can undo its effects as well.

When a compiler sees an expression somePointer->a it takes the numeric value of the pointer somePointer, adds the offset of member a, and learns the place in memory on which you can operate. There are three things you could do to a place in memory:

  1. Read it
  2. Write it
  3. Learn its address

Items 1 and 2 (reading and writing) constitute dereferencing. Item 3, however, is not dereferencing, because the memory at the specific address is not accessed.

The macro essentially asks the compiler to compute the address of member a assuming that the base address is zero. The address of a returned would naturally be equal to a's offset. All you need to do is take its address, which is what you do with the operator &.

like image 112
Sergey Kalinichenko Avatar answered Feb 15 '23 03:02

Sergey Kalinichenko