Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a22129a16d61aedd

Jump to content

Talk:Offsetof

Page contents not supported in other languages.
Add topic
From Wikipedia, the free encyclopedia

union?

[edit]
  • It evaluates to the offset (in bytes) of a given member within a struct or union type [...]
    • But a union's member all start at the same address, thereby this macro would only return 0, rendering it trivial for union types.

--Abdull (talk) 13:26, 6 August 2008 (UTC)Reply

The member-designator argument can be a sequence of struct/union members and possibly array indices. So given
union foo { int x ; struct { double a, b; } y[]; };
one may usefully (and validly) write offsetof(union foo, y[10].b). The article could be clearer about this. –Henning Makholm (talk) 17:25, 5 November 2010 (UTC)Reply

"Modern compilers"

[edit]

The article asserts that the crufty old &((struct foo*)NULL).bar idiom doesn't do the expected on modern compilers. My experience doesn't bear this out... 128.2.100.148 (talk) 10:30, 17 October 2010 (UTC)Reply

I find this claim dubious too. I suspect it may have originated as an attempt to explain why there are builtins for it in many compilers. I have removed it as unsourced speculation. –Henning Makholm (talk) 17:13, 5 November 2010 (UTC)Reply
You are correct. For example Microsoft's headers from 2025 (EWDK) contain the following for kernel mode CRT:
#ifdef  _WIN64
:#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
:#else
:#define offsetof(s,m)   (size_t)&(((s *)0)->m)
:#endif
The UCRT stddef.h is a bit more complex and contains this:
#if defined _MSC_VER && !defined _CRT_USE_BUILTIN_OFFSETOF
:    #ifdef __cplusplus
:        #define offsetof(s,m) ((::size_t)&reinterpret_cast<char const volatile&>((((s*)0)->m)))
:    #else
:        #define offsetof(s,m) ((size_t)&(((s*)0)->m))
:    #endif
:#else
:    #define offsetof(s,m) __builtin_offsetof(s,m)
:#endif
Clearly there is no concern that the classic macro wouldn't work, at least in the C language. At the same time, there is a builtin which handles both C and C++.
P.J. Plauger discusses offsetof implementation in The Standard C Library (1992). On page 216 he states:

Standard C defines no portable way to write this macro. Each implementation, how ever, must have some nonstandard way to implement it. An implementation may, for example, reliably evaluate some expression whose behavior is undefined in the C Standard.

On page 222 he goes into further detail and describes why the classic offsetof macro (which his library uses) may or may not work on a given implementation:

For the macro offsetof I chose to use a common trick. Many implementations let you type cast an integer zero to a data-object pointer type, then perform pointer arithmetic on the result. That is certainly undefined behavior, so you may well find an implementation that balks at this approach.

The translator must indulge you a bit further for this definition of the macro to work properly. It must let you type cast the zero-based address back to an integer type, in this case size_t in disguise. Moreover, it must tolerate such antics in an integer constant expression. That's what you need to initialize static data objects.

Luckily, quite a few translators grant such a triple indulgence. If you encounter one that doesn't, you will have to research how its implementors expect you to define offsetof. To comply with the C Standard, each implementation must provide some method.

In C89, a paragraph in section 6.4 (Constant expressions) reads:

An address constant is a pointer to an lvalue, designating an object of static storage duration, or to a function designator; [...]

In C99, the corresponding paragraph (6.6.9) states:

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; [...]

That is, null pointer was explicitly added as a possible address constant.
Additionally, footnote 87 in C99 starts with the following:

Thus, &*E is equivalent to E (even if E is a null pointer) [...]

Which is logical because the expression &*E does not involve a pointer dereference, so using a null pointer is not a problem.
There is one very good reason why there is no portable (i.e. strictly conforming) way to implement offsetof, which is that conversions between pointers and integers are implementation-defined. The null pointer need not have the value of zero, and conversions might (theoretically) behave in all kinds of interesting ways. However, on platforms in common use today, such as x86/x64/ARM, pointers are effectively integers, hence conversions behave in a very straightforward manner, even though that is not a guarantee the language itself makes. Codegen86 (talk) 15:11, 17 March 2026 (UTC)Reply

I think the claim is false. Here is Section 6.6 Paragraph 9 from the late draft of the C99 standard cited in the Wikipedia C99 article.

An address constant is a null pointer, a pointer to an lvalue 
designating an object of static storage duration, or a pointer 
to a function designator; it shall be created explicitly using 
the unary & operator or an integer constant cast to pointer type, 
or implicitly by the use of an expression of array or function type. 
The array-subscript [] and member-access . and -> operators, the 
address & and indirection * unary operators, and pointer casts may 
be used in the creation of an address constant, but the value of an 
object shall not be accessed by use of these operators.

So &(struct foo *)0->somefield is a valid address constant (assuming somefield is not a bit field). The numeric value of the address is the offset in bytes from the beginning of the structure, and hence must be smaller than the sizeof(struct foo), and furthermore it is non-negative. Hence it is safe to cast it to size_t.

If I don't hear any objections in the next few days, I'll rewrite the section to not say that the behavior of the historical macro is undefined. DMJ001 (talk) 21:32, 26 February 2011 (UTC)Reply

I don't think it convincing to say that &(struct foo *)0->somefield is valid. Section 6.6 also states
The semantic rules for the evaluation of a constant expression are the same as for
nonconstant expressions.

Section 6.5.3.2 Paragraph 4 (which is a semantic rule) states

If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.83)

and footnote 83 says

Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an
address inappropriately aligned for the type of object pointed to, and the address of an object after the
end of its lifetime.

(emphasis mine)

As far as I can see, this means &(struct foo *)0->somefield (which, as equivalent to &(*(struct foo *)0).somefield, contains the action of dereferencing a null pointer) does produce an undefined behavior, regardless the fact that &(struct foo *)0->somefield is a constant expression. --D41D8CD98F (talk) 14:09, 14 December 2014 (UTC)Reply

But section 6.6 Constant Expressions, Paragraph 9, the states that value of the object is not accessed by the operations of using &, *, or ->, so there is no null pointer dereferencing occurring as some suggest.  Preceding unsigned comment added by 68.40.190.43 (talk) 05:49, 5 February 2019 (UTC)Reply

[edit]

Are you sure that it is not a copyright violation to include GPL2-licensed code of the Linux kernel code (as currently in this article) on Wikipedia? — Preceding unsigned comment added by 193.40.6.84 (talk) 09:27, 21 May 2012 (UTC)Reply

Why would it be? 190.60.93.218 (talk) 19:08, 8 August 2012 (UTC)Reply

The container_of macro originates in BSD (at least in 4BSD, possibly earlier). That's probably a better source than the Linux kernel anyway...  Preceding unsigned comment added by 109.223.126.221 (talk) 14:33, 23 December 2015 (UTC)Reply

Undefined behavior for offsetof

[edit]

It is unclear whether or not the undefined behavior is referring to the particular macro implementation or offsetof. Overdamped (talk) 14:26, 12 June 2013 (UTC)Reply

It is referring to that particular macro implementation of offsetof. Compilers can implement special builtin versions of offsetof that don't have to invoke undefined behaviour. 217.34.231.28 (talk) 15:12, 16 June 2016 (UTC)Reply
It should also be noted that implementations can use any particular macro implementation for offsetof(), while knowingly or purposefully designing the semantics of the compiler so that the macro does not invoke undefined behavior for a particular target architecture. — Loadmaster (talk) 22:20, 16 June 2016 (UTC)Reply

Example uses a GNU C extension which is non-standard

[edit]

The macro container_of makes use of the "statement expressions" extension that GCC defines. See this page for more details: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html

This extension is not standard C or C++, so its use in this example should be explicitly noted (or the example should be replaced with one that does not use non-standard language extensions)  Preceding unsigned comment added by 64.94.36.4 (talk) 20:03, 15 November 2016 (UTC)Reply

There's a standard C version, which I contributed, described below the one from the Linux kernel.
I don't know if it should be swapped around. The version I contributed isn't used in practice by a major piece of software. It's somewhat useful to have a real world example to introduce a use-case.
Also, it's uncertain if container_of doesn't rely on UB. The C standard is quite unclear on the topic. Some readings hint that this may be okay, but it's so vague as to put into question the likelihood that implementers would reliably read it that way. Of course, a lot of code "relies on UB", in the sense that it is designed for specific implementations of C which explicitly or implicitly define some UB (sometimes even through configuration e.g. -fwrapv or -fno-strict-aliasing). But code which is written like this cannot be said to be standard C.
All that said, I can imagine the following as a rewrite:
One common use-case of offsetof is in the Linux kernel where ... (cited) Below is an example of an implementation of such a macro ... (example) (... explanation of the type safety aspect, and the simpler version for illustration ...). The Linux kernel version of this macro also looks different as it utilises statement expressions ... It's uncertain why ...
Let me know what you think. It would also avoid any questions about the legality of including the verbatim code.
Arch-TK (talk) 13:57, 29 June 2026 (UTC)Reply

"suspicious" results

[edit]

The article states: "... 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..."

However, I think the author(s) has/have neglected the contribution from the virtual methods. Using Microsoft Visual Studio the not so well known compile time flag /d1reportAllClassLayout reveals better the class structure, including the virtual tables. Comparing the output from /d1reportAllClassLayout and offsetof() reveals the stated "...obviously suspicious results..." is incorrect. Using the gcc compiler we can alternatively use the compile time flag -fdump-class-hierarchy.  Preceding unsigned comment added by 195.11.244.2 (talk) 10:52, 26 April 2017 (UTC)Reply

Styling

[edit]

Warning: Pedantry.

@2605:8D80:13E3:86E8:E:2413:4C7F:F0A8 made some changes to the styling of the code on the page.

Specifically they uppercased all the macro names, and switched all the styling over to the C++ "preferred" style.

This is appropriate for the C++ in Limitations, but the rest of the page is C focused.

And, of course, C has no "style guide" so it's kind of moot to be making this argument.

But I'd still like to argue for the following:

  • The macro names should be lowercased again. While it's certainly common to uppercase macro names in C (and in C++), container_of is not commonly uppercase. The referenced examples are even referencing real world cases (or in one case, code I wrote) which did not use a uppercase macro name.
  • The C code should use snake_case names and * against against the (potentially absent) identifier (i.e. type *name). Because this is the style of the Linux kernel and many C codebases where container_of is most commonly used/useful. And it's how it was styled originally.

This probably doesn't justify a talk page entry, but I wanted to create one just in case someone had strong opinions on this topic. I don't want to start an edit war for no good reason.

Arch-TK (talk) 13:35, 29 June 2026 (UTC)Reply