Created
October 21, 2018 14:39
-
-
Save emandret/f71cf6f5d6b9ae06c807e66c24a583ca to your computer and use it in GitHub Desktop.
The linux kernel `offsetof` macro explained and implemented in pure C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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