Skip to content

Instantly share code, notes, and snippets.

@emandret
Created October 21, 2018 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emandret/f71cf6f5d6b9ae06c807e66c24a583ca to your computer and use it in GitHub Desktop.
Save emandret/f71cf6f5d6b9ae06c807e66c24a583ca to your computer and use it in GitHub Desktop.
The linux kernel `offsetof` macro explained and implemented in pure C
#include <stdio.h>
/*
* In the expression `&ptr[5]` which is equivalent to `&(*(ptr + 5))`, we take
* the address of the fifth element which means an offset in bytes equal to `5 *
* sizeof(*ptr)` from the `ptr` base address. The address is simply retrieved as
* `ptr + 5` by the compiler and no memory access (i.e. dereferencing) is
* usually involved here.
*
* For a structure, the variable name represents the whole memory area from the
* base address to the base address plus the size of the structure. A pointer to
* a structure is the base address the compiler sees instead of variable name at
* compile time, since variable names are just labels.
*
* Member variable names in structures are not addresses but offsets. The dot
* operator `.` simply applies an offset from the base address of the structure
* and then access the value located at that address, dereferencing `n` bytes
* where `n` is the size of the member type. The arrow operator `->` is the
* shorthand for dereferencing a structure pointer and accessing its member
* variables, so `ptr->member` is equivalent to `(*ptr).member` in syntax.
*
* Retrieving a member variable address does not usually mean memory access,
* thus the expression `&ptr->member` or `&(*ptr).member` even if `ptr` is a
* null pointer will normally not fail since `ptr` will not be dereferenced. The
* resulting address is internally computed at compile time by incrementing the
* base address `ptr` over the member variable offset in bytes.
*
* The `offsetof` macro is implemented this way by retrieving the address of a
* structure member from a null pointer, considering the compiler will usually
* not dereference it, attempting memory access at an invalid address, but
* rather increment the null pointer over the offset in bytes in the structure
* declaration. This assumption is however not compliant with the C standard,
* while this behavior is implemented by most compilers, an undefined behavior
* is still possible if a compiler attempt memory access at the null pointer
* address.
*/
#define offsetof(type, member) \
((unsigned int)((unsigned char*)&((type*)0)->member - (unsigned char*)0))
struct s_point {
int x, y, z;
};
int main(void)
{
printf("%d\n", offsetof(struct s_point, y));
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment