Reputation: 140796
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.
The question Is sizeof(*ptr) undefined behavior when pointing to invalid memory? addresses a closely related issue (sizeof(*ptr)
where ptr
is a variable that happens to be a null pointer) but the answers do not cover ->
(I'm not aware of any difference between a -> b
and (*a) . b
, but that doesn't mean there isn't one) nor do they consider the possibility of the rules being different if the pointer involved is a compile-time constant null (or otherwise invalid) pointer. Also, the answers discuss only 6.5.3.4 (and the equivalent pieces of C++) and take it for granted that nothing else in the standard contradicts their conclusions from that section.
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.
Upvotes: 3
Views: 121
Reputation: 224842
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.
Upvotes: 10