0

The following code and its output:

#include <stdio.h>
int x = 0;
     #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)x)->MEMBER)
     #define offsetof_1(TYPE, MEMBER) ((size_t) &((TYPE *)1)->MEMBER)
     #define offsetof_2(TYPE, MEMBER) ((size_t) &((TYPE *)2)->MEMBER)
     #define offsetof_3(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
struct test{};
struct m {
     int b;
     char w;
     struct test t;
     int c;
    };
int main(void) {

    printf("Checking:%x\n",offsetof(struct m,b));
    printf("Checking:%x\n",offsetof_1(struct m,b));
    printf("Checking:%x\n",offsetof_2(struct m,b));
    printf("Checking:%x\n",offsetof_3(struct m,b));
    return 0;
}
Output:
Checking:0
Checking:1
Checking:2
Checking:0

I am trying to understanding the typecasting used here. I think, how compiler does is it treats (type *)(value) as that type starting at address (value). Hence for the given structure the expected value is 0,i.e. offsetof is b, but as we have used different values for typecasting we get different offset. Please let me know if my understanding is correct. I am just trying to understand how literals are typecasted and its implications. This is used in Linux kernel. Hence tagging them as well.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
Anup Buchke
  • 5,210
  • 5
  • 22
  • 38
  • 1
    It's unusual what you are doing, but the results are expected. The (TYPE *)%d just casts that number to a pointer, and the offset is from that pointer. Obviously, this only works (properly) with 0, since the offset to the start of the struct is 0. – Charlie Burns Nov 10 '13 at 02:31
  • 1
    How does this relate to C++? And is your paragraph of text at the end a quote of somethihg? Removing the tag and formatting. – Potatoswatter Nov 10 '13 at 02:34
  • @CharlieBurns Somehow the value which I pass, is getting treated as starting address, and the offset is calculated accordingly. Just want to understand how it works. – Anup Buchke Nov 10 '13 at 02:35
  • There is no `offsetof_2` macro in the kernel according to http://livegrep.com so the kernel tag may also be disingenuous. – Potatoswatter Nov 10 '13 at 02:38
  • @Potatoswatter that is just my code , trying to learn how the macro behaves for different values. – Anup Buchke Nov 10 '13 at 02:40
  • @anup.stackoverflow Not exactly, you're seeing how your macro behaves when you provide a definition altered from your compiler's implementation of `offsetof`. – Potatoswatter Nov 10 '13 at 02:41
  • @Potatoswatter Not exactly, to get proper understanding of how it works, I am just experimenting with different values. May be compiler guy can help me understanding how it is treating it. – Anup Buchke Nov 10 '13 at 02:44
  • @anup.stackoverflow I'm as much a "compiler guy" as you're gonna get. – Potatoswatter Nov 10 '13 at 02:46

1 Answers1

2

offsetof is a standard facility. The C standard defines it to behave a certain way, but does not define how the compiler implements it. Most compilers rely on undefined behavior, defining a special case for the sake of offsetof implementation.

What you have done is nonsense in terms of the language semantics. Treating the value 2 as a pointer is a classic example of undefined behavior.

It is not the design intent of the language that the user be able to define their own offsetof, so it is better not to try.

"Under the hood", yes pointers are typically scalars held in machine registers, and scalar arithmetic is used to obtain the address of a struct member. Performing the same arithmetic to get the address of an object at "location zero" yields its generic offset within any object. Using integers besides zero as locations may yield an arithmetic result or it may not. (It usually will but the idea is nonsense.)

Experimenting with programs that are conceptual nonsense is a risky way to discover how the system works, because you run the risk that it will flag an error or take a shortcut having detected that something is wrong.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421