offsetof
C's offsetof() macro is an ANSI C library feature found in stddef.h. It evaluates to the offset (in bytes) of a given member within a struct or union type, an expression of type size_t. The offsetof() macro takes two parameters, the first being a structure name, and the second being the name of a member within the structure. It cannot be described as a C prototype.[1]
Implementation
The "traditional" implementation of the macro relied on the compiler being not especially picky about pointers; it obtained the offset of a member by specifying a hypothetical structure that begins at address zero:
#define offsetof(st, m) ((size_t)&(((st *)0)->m))
This works by casting a null pointer into a pointer to structure st, and then obtaining the address of member m within said structure. While this implementation works correctly in many compilers, it has undefined behavior according to the C standard,[2] since it involves a dereference of a null pointer (although, one might argue that no dereferencing takes place, because the whole expression is calculated at compile time). It also tends to produce confusing compiler diagnostics if one of the arguments is misspelled. Some modern compilers (such as GCC) define the macro using a special form instead, e.g.[3]
#define offsetof(st, m) __builtin_offsetof(st, m)
This builtin is especially useful with C++ classes or structs that declare a custom unary operator &.[4]
Usage
It is useful when implementing generic data structures in C. For example, the Linux kernel uses offsetof() to implement container_of(), which allows something like a mixin type to find the structure that contains it:[5]
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
This macro is used to retrieve an enclosing structure from a pointer to a nested element, such as this iteration of a linked list of my_struct objects:
struct my_struct {
const char *name;
struct list_node list;
};
extern struct list_node * list_next(struct list_node *);
struct list_node *current = /* ... */
while(current != NULL){
struct my_struct *element = container_of(current, struct my_struct, list);
printf("%s\n", element->name);
current = list_next(&element->list);
}
The linux kernel implementation of container_of uses a GNU C extension called statement expressions.[6] It's possible a statement expression was used to ensure type safety and therefore eliminate potential accidental bugs. There is, however, a way to implement the same behaviour without using statement expressions while still ensuring type safety:
#define container_of(ptr, type, member) (type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))
Limitations
Usage of offsetof is limited to POD types in C++98, and standard-layout classes in more recent versions (such as C++11),[7] otherwise it has an undefined behavior. While most compilers will generate a correct result even in cases that don't respect the standard, there are edge cases when offsetof will either yield an incorrect value, generate a compile-time warning or error, or outright crash the program. This is especially the case for virtual inheritance.[8] The following program will generate several warnings and print obviously suspicious results when compiled with gcc 4.7.3 on an amd64 architecture:
#include <stddef.h>
#include <stdio.h>
struct A {int a;virtual void dummy() {}};
struct B: public virtual A {int b;};
int main()
{
printf("offsetof(A,a) : %lu\n",(unsigned long)offsetof(A,a));
printf("offsetof(B,b) : %lu\n",(unsigned long)offsetof(B,b));
return 0;
}
Output is:
offsetof(A,a) : 8
offsetof(B,b) : 8
References
- ↑ "offsetof reference". MSDN. Retrieved 2010-09-19.
- ↑ "Does &((struct name *)NULL -> b) cause undefined behaviour in C11?". Retrieved 2015-02-07.
- ↑ "GCC offsetof reference". Free Software Foundation. Retrieved 2010-09-19.
- ↑ "what is the purpose and return type of the __builtin_offsetof operator?". Retrieved 2012-10-20.
- ↑ Greg Kroah-Hartman (June 2003). "container_of()". Linux Journal. Retrieved 2010-09-19.
- ↑ "Statements and Declarations in Expressions". Free Software Foundation. Retrieved 2016-01-01.
- ↑ "offsetof reference". cplusplus.com. Retrieved 2016-04-01.
- ↑ Steve Jessop (July 2009). "Why can't you use offsetof on non-POD structures in C". Stack Overflow. Retrieved 2016-04-01.