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.
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.