&((struct name *)NULL -> b) in printf statement [duplicate]
Asked Answered
E

3

9

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)));
}
Ex answered 13/11, 2014 at 9:10 Comment(2)
You know main has a return-type of int in C (and C++)?Clinquant
This exact program was posted 3 years ago, I guess it is a common book!Grampositive
F
9

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.

Folberth answered 13/11, 2014 at 9:22 Comment(4)
Be aware that this construct invokes UB though.Clinquant
@Clinquant Is (( (struct name *)NULL)->b ) undefined behavior? It actually seems pretty well defined for me. Can you point me to a link that specifies it?Folberth
@MarkSegal: It is doing pointer-arithmetic on a null pointer, which is simply illegal. Pointer arithmetic is only defined for pointers to a valid object, and only if it does not stray beyond the bounds of the underlying object (the pointer past an object is explicitly allowed though).Clinquant
@MarkSegal: it might be unspecified undefined behavior, even if it practically works everywhere.Hugo
H
4

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

Hugo answered 13/11, 2014 at 9:18 Comment(4)
thanks @Basile .... lets stick to machines we know about then... :)Ex
@HimanshuSourav: c-faq.com/null/machexamp.html Just so you know some more machines ;-)Clinquant
@Deduplicator: but these old XX-th century machines exist only in museums!Hugo
Just about. Which is why one can disregard a not-all-bits-zero null-pointer in practice, mostly. The UB in the pointer-arithmetic though, that can still bite you on modern machines, depending (mostly) on your implementation.Clinquant
G
4

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.

Grampositive answered 13/11, 2014 at 9:38 Comment(2)
Technically the code is not derefenrencing the NULL pointer. But I still feel it is undefined behavior.Hugo
@BasileStarynkevitch I have started a new question for whether this is actually UB although on first reading of C11 it's not looking goodGrampositive

© 2022 - 2024 — McMap. All rights reserved.