&((struct name *)NULL -> b) in printf statement

436 views Asked by At

I found this code sample in a book, but I am unable to understand the expression in printf statement. and this program compiles successfully giving output as 4. kindly advise...

void main(){
    unsigned char c;

    typedef struct name {
      long a;
      int b;
      long c;
    }r;

    r re = {3,4,5};
    r *na=&re;

    printf("%d",*(int*)((char*)na + (unsigned int ) & (((struct name  *)NULL)->b)));
}
3

There are 3 answers

4
Mark Segal On BEST ANSWER

Lets start from the last line:

printf("%d",*(int*)((char*)na + (unsigned int ) & (((struct name  *)NULL)->b)));

Lets interpret:

(unsigned int ) & ((    (struct name  *)NULL)->b )

Is actually casting & (( (struct name *)NULL)->b ) into a unsigned int.

& (( (struct name *)NULL)->b ) is the address (i.e it gives a pointer to):

((  (struct name  *)NULL)->b )

Which is actually the offset of b (as name.b) from NULL (0), which is 4 bytes (assuming a long is 4 bytes) and converted to a pointer of int, gives you 2 (assuming int is 2 bytes).

If instead of NULL it would have been a pointer to 0xFFFF0000, then &(ptr->b) would have been 0xFFFF0002. But it more like &(0 -> b) so its 0x00000002.

So, (unsigned int ) & (( (struct name *)NULL)->b ) == 2 (or maybe 1, or maybe 4, depending on the machine).

The rest is simple: *(int*)((char*)na + 2 will point to re->b. So it should print 4 (what have been initialized in the code, r re ={3,4,5};).

P.S: even if (unsigned int ) & (( (struct name *)NULL)->b ) != 2 (maybe it's 1, 4 or 8) - it should still print 4 because it then uses the same offset to get the value.

2
M.M On

The code:

(unsigned int ) & (((struct name  *)NULL)->b))

is intended to obtain the count, in bytes, of how far from the start of a struct name the variable b is.

There is a standard way to do this: offsetof(struct name, b); . The person who wrote this code either was not aware of offsetof, or was attempting to teach something (although that may be a case of the blind leading the blind).

The code causes undefined behavoiur by dereferencing a null pointer, however common compilers may accept it without triggering an error, probably because compiler developers know that there is existing code out there like this.


The rest of the code is straightforward; it points to the start of the struct; advances by that many bytes, and reads an int from that location; which is of course the same as just reading b directly.

4
Basile Starynkevitch On

re is a local variable of type r, i.e. a struct name; it is usually allocated on the call stack.

na is a pointer to re.

(unsigned int) & (((struct name *)NULL)->b) might be undefined behavior (but I am not sure), but most compilers would compile that to the offset -in bytes- of field b (like offsetof does, see offsetof(3)). On my machine that might be 8.

(char*)na + the above offset is often the same address as &re.b

You dereference that pointer, which practically is &re.b

I feel that your code might not be standard compliant (see this answer for some argumentation; there might be hypothetical machines & C implementations where NULL is not an all-zero-bits word, I know no such implementations), but on all machines I know about, it should print the value of field re.b