Is the obvious dereferencing of a NULL pointer in C the actual pointer arithmetic?

I have this piece of code. Apparently, the null pointer is dereferenced here, but then the bitwise one - And the result is with unsigned int . I really don't understand this whole part. What is he going to do? Is this a form of pointer arithmetic?

 struct hi { long a; int b; long c; }; int main() { struct hi ob={3,4,5}; struct hi *ptr=&ob; int num= (unsigned int) & (((struct hi *)0)->b); printf("%d",num); printf("%d",*(int *)((char *)ptr + (unsigned int) & (((struct hi *)0)->b))); } 

The output I get is 44. But how does it work?

+6
c struct pointer-arithmetic offsetof null-pointer
source share
5 answers

It is not an β€œand,” it takes the address of the argument to the right.
This is a standard hack to get member offset at runtime. You drop 0 into the pointer to the hi structure, then refer to the element "b" and get its address. Then you add this offset to the "ptr" pointer and get the real address of the "b" field of the structure pointed to by ptr, which is ob. Then you return that pointer back to the int pointer (because b is int) and output it. This is the second seal. The first print output of num, which is 4, is not because the value of b is 4, but because 4 is the offset of the field b in the hi structure. This is sizeof (int), because b follows a, and a is int ... I hope this makes sense :)

+3
source share

This does not cause the dereference of the null pointer. You should look at all the code. What the code says: take the number 0 , consider it as struct hi * , select the b element in the structure it points to, and take the address of that element. The result of this operation will be the displacement of the element b from the beginning of the structure. When you add it to the pointer, you get the element b , which is 4 .

+8
source share

This gives an offset in bytes of the field b inside the hi struct

((struct hi *)0) is a pointer to a hi structure, starting at address 0 .

(((struct hi *)0)->b) is the field b above structure

& (((struct hi *)0)->b) is the address of the specified field. Since the structure hi is located at address 0 , this is the offset b inside the structure.

(unsigned int) & (((struct hi *)0)->b) is the conversion of this address from the address type to unsigned int , so it can be used as a number.

You are not actually casting a NULL pointer. You just do pointer arithmetic.


Accessing (((struct hi *)0)->b) will give you a segmentation error because you are trying to access a forbidden memory location.

Using & (((struct hi *)0)->b) does not give you a segmentation error, because you only accept the address of this forbidden memory location, but you are not trying to access the specified location.

+6
source share

You must use the 32-bit compiler (or the 64-bit compiler on Windows).

The first expression β€” for num β€” is the general implementation of the offsetof macro from <stddef.h> ; it is not tolerated, but it often works.

The second expression adds that to 0 (null pointer) and gives you the same answer - 4. The second expression adds 4 to the base address of the object pointed to by ptr , and this value is 4 in the structure.

Your output does not include a new line - it probably should (the behavior is not fully portable, because this implementation is defined if you do not include a new line: C99 Β§7.19.2: "Is the last line required to complete a new -line character determined by implementation. "). In a Unix window, this is dirty because the next prompt will appear immediately after 44.

+3
source share

Just to make it clear that you need to understand the difference between dereferencing a NULL pointer and when it is not considered a dereference. The specification actually dictates that de-link does not occur, and is actually optimized when there is an and (address-of) operator in the expression.

So, & ((struct T *) 0) β†’ b) actually optimizes out β†’ and just jumps the number of bytes from offset 0 and takes its T * structure. It really confuses things for beginners. However, it is widely used in the Linux kernel - and provides a real sense of list_entry, list_head, and various arithmetic pointer magic that beginners cannot understand.

In any case, this is a programmatic way of finding the offset 'b' inside a struct T. It is used in offsetof, as well as other list_head operations, such as list_entry.

For more information, you can read about it in Robert Love’s book entitled Linux Kernel Development.

0
source share

All Articles