HOME

TheInfoList



OR:

C's macro is an
ANSI C ANSI C, ISO C, and Standard C are successive standards for the C programming language published by the American National Standards Institute (ANSI) and ISO/IEC JTC 1/SC 22/WG 14 of the International Organization for Standardization (ISO) and th ...
library feature found in . It evaluates to the offset (in bytes) of a given member within a
struct In computer science, a record (also called a structure, struct, or compound data) is a basic data structure. Records in a database or spreadsheet are usually called "rows". A record is a collection of '' fields'', possibly of different data typ ...
or
union Union commonly refers to: * Trade union, an organization of workers * Union (set theory), in mathematics, a fundamental operation on sets Union may also refer to: Arts and entertainment Music * Union (band), an American rock group ** ''Un ...
type, an expression of type . The offsetof() macro takes two
parameters A parameter (), generally, is any characteristic that can help in defining or classifying a particular system (meaning an event, project, object, situation, etc.). That is, a parameter is an element of a system that is useful, or critical, when ...
, 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.


Implementation

The "traditional" implementation of the macro relied on the compiler obtaining 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 can be understood as taking a null pointer of type structure , and then obtaining the address of member within said structure. While this implementation works correctly in many compilers, it has generated some debate regarding whether this is
undefined behavior In computer programming, undefined behavior (UB) is the result of executing a program whose behavior is prescribed to be unpredictable, in the language specification to which the computer code adheres. This is different from unspecified behavior, ...
according to the C standard, since it appears to involve a
dereference In computer programming, the dereference operator or indirection operator, sometimes denoted by "*" (i.e. an asterisk), is a unary operator (i.e. one with a single operand) found in C-like languages that include pointer variables. It operates ...
of a
null pointer In computing, a null pointer or null reference is a value saved for indicating that the pointer or reference does not refer to a valid object. Programs routinely use null pointers to represent conditions such as the end of a list of unknown lengt ...
(although, according to the standard, section 6.6 Constant Expressions, Paragraph 9, the value of the object is not accessed by the operation). It also tends to produce confusing compiler diagnostics if one of the arguments is misspelled. An alternative is: #define offsetof(st, m) \ ((size_t)((char *)&((st *)0)->m - (char *)0)) It may be specified this way because the standard does not specify that the internal representation of the null pointer is at address zero. Therefore the difference between the member address and the base address needs to be made. Again, since these are constant expressions it can be calculated at compile time and not necessarily at run-time. Some modern compilers (such as GCC) define the macro using a special form (as a language extension) instead, e.g. #define offsetof(st, m) \ __builtin_offsetof(st, m) This builtin is especially useful with C++ es or s that declare a custom unary .


Usage

It is useful when implementing generic data structures in C. For example, the
Linux kernel The Linux kernel is a free and open-source, monolithic, modular, multitasking, Unix-like operating system kernel. It was originally authored in 1991 by Linus Torvalds for his i386-based PC, and it was soon adopted as the kernel for the GNU ope ...
uses to implement , which allows something like a
mixin In object-oriented programming languages, a mixin (or mix-in) is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depend ...
type to find the structure that contains it: #define container_of(ptr, 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 objects: struct my_struct ; extern struct list_node * list_next(struct list_node *); struct list_node *current = /* ... */ while (current != NULL) The linux kernel implementation of container_of uses a GNU C extension called ''statement expressions''. 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))) At first glance, this implementation may seem more complex than necessary, and the unusual use of the conditional operator may seem out of place. A simpler implementation is possible: #define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) This implementation would also serve the same purpose, however, there's a fundamental omission in terms of the original linux kernel implementation. The type of ptr is never checked against the type of the member, this is something that the linux kernel implementation would catch. In the aforementioned type-checked implementation, the check is performed by the unusual use of the conditional operator. The constraints of the conditional operator specify that if the operands to the conditional operator are both pointers to a type, they must both be pointers to compatible types. In this case, despite the fact that the value of the third operand of the conditional expression will never be used, the compiler must perform a check to ensure that (ptr) and &((type *)0)->member are both compatible pointer types.


Limitations

Usage of offsetof is limited to POD types in
C++98 C98 or C-98 may refer to: * C-98 Clipper, the military designation of the Boeing 314 flying boat * CJYC-FM, "Big John FM", formerly known as "C98" * Cray C98, a model of the Cray C90 * Ruy Lopez (ECO code), a chess opening * Lake Village Airpor ...
, standard-layout classes in
C++11 C++11 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++11 replaced the prior version of the C++ standard, called C++03, and was later replaced by C++14. The name follows the tradition of naming language versions by ...
, and more cases are conditionally-supported in
C++17 C++17 is a version of the ISO/IEC 14882 standard for the C++ programming language. C++17 replaced the prior version of the C++ standard, called C++14, and was later replaced by C++20. History Before the C++ Standards Committee fixed a 3-year rel ...
, 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 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. The following program will generate several warnings and print obviously suspicious results when compiled with gcc 4.7.3 on an amd64 architecture: #include #include struct A ; struct B: public virtual A ; int main() Output is:
offsetof(A, a) : 8
offsetof(B, b) : 8


References

{{reflist C standard library