Specifically in ISO C 1999, not in C++ nor in any newer revision of C1, is it strictly conforming to use sizeof ((struct T *) 0)->field
to determine the size of a struct field? For example
#include <stddef.h>
struct thing {
int a;
short b[3];
};
const size_t size_of_thing_b = sizeof ((struct thing *)0)->b; /* this line */
My initial opinion was that this construct has undefined behavior (and is therefore not strictly conforming). Evidence in favor of that opinion is that the very similar expression &((struct thing *)0)->b
definitely does have undefined behavior. In the abstract machine, it dereferences a null pointer. That argument seems like it ought to carry over if &
is replaced with sizeof
… except that the operand of sizeof EXPR
is specifically not evaluated (6.5.3.4p2) (unless there’s a VLA involved, which there isn’t in this case), so the abstract machine doesn’t do anything with it, and I don’t see any other reason why it should have undefined behavior.
Questions about how to get the sizeof
a struct field have been asked before (e.g. sizeof single struct member in C) but the answers typically take for granted that sizeof ((struct T *)0)->field
has a well-defined result.
1 I am also interested in whether the conformance of this code might have changed since the 1999 specification, either due to wording changes in the normative textpost-1999 or due to DR responses, but only secondarily.
The language of 6.5.3.4p2 is pretty clear on this:
The
sizeof
operator yields the size (in bytes) of its operand, which
may be an expression or the parenthesized name of a type. The size is
determined from the type of the operand. The result is an integer. If
the type of the operand is a variable length array type, the operand
is evaluated; otherwise, the operand is not evaluated and the result
is an integer constant.
So while an expression such as (struct thing *)0)->b
, if evaluated, would invoke undefined behavior due to a NULL pointer dereference, applying sizeof
to this expression does not evaluate it, and the expression is looked at just to determine its type.
So sizeof (struct thing *)0)->b
is well defined and evaluates to an integer constant.
Also, the text of 6.5.3.4p2 is unchanged in both C11 and C23, so the same applies to those versions of the standard.