My project demands a complete understanding of how the sizeof
operator works. The C standard specification in this regard is vague and it will be dangerous to rely on my interpretations of it. I am particularly interested in when and how the sizeof
ought to be processed.
My previous knowledge suggested that it is a compile-time operator, which I never questioned, because I never abused sizeof
too much.
However:
int size = 0;
scanf("%i", &size);
printf("%i\n", sizeof(int[size]));
This for instance cannot be evaluated at compile time by any meaning.
char c = '\0';
char*p = &c;
printf("%i\n", sizeof(*p));
I do not remember the exact code that produces U/B, but here, *p
is an actual expression (RTL unary dereference). By presumption, does it mean that sizeof(c+c)
is a way to force compile-time evaluation by means of the expression or will it be optimized by the compiler?
Does sizeof
return a value of type int
, is it a size_t
(ULL on my platform), or is it implementation-defined?
This article states that "The operand to sizeof
cannot be a type-cast", which is incorrect. Type-casting has the same precedence as the sizeof
operator, meaning in a situation where both are used, they are simply evaluated right to left. sizeof(int) * p
probably does not work, because if the operand is a type in braces, this is handled first, but sizeof((int)*p)
works just fine.
I am asking for a little technical elaboration on how sizeof
is implemented. That can be of use to anyone who doesn't want to spread misinformation, inaccuracies or as in my case - work on a project that is directly dependent on it.
1. My previous knowledge suggested that it is a compile-time operator, which I never questioned, because I never abused sizeof
too much…
C 2018 6.5.3.4 2 specifies the behavior of sizeof
and says:
… 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.
In your example with sizeof(int[size])
, the type of int[size]
is a variable length array type, so the operand is evaluated1, effectively computing the size during program execution.
In your example with sizeof(*p)
, the type of *p
is not a variable length array type, so the operand is not evaluated. The fact that p
may point to an object of automatic storage duration that is created during program execution is irrelevant; the type of *p
is known during compilation, so *p
is not evaluated, and the result of sizeof
is an integer constant.
2. Does sizeof
return a value of type int
, is it a size_t
(ULL on my platform), or is it implementation-defined.
C 2018 6.5.3.4 5 says “The value of the result of both operators [sizeof
and _Alignof
] is implementation-defined, and its type (an unsigned integer type) is size_t
, defined in <stddef.h> (and other headers).”
3. This article states that "The operand to sizeof
cannot be a type-cast", which is incorrect. Type-casting has the same precedence as the sizeof
operator, meaning in a situation where both are used, they are simply evaluated right to left.
sizeof(int) * p
probably does not work, because if the operand is a type in braces, this is handled first, but sizeof((int)*p)
works just fine.
The article means the operand cannot directly be a cast-expression (C 2018 6.5.4) in the form ( type-name ) cast-expression
, due to how the formal grammar of C is structured. Formally, an expression operand to sizeof
is a unary-expression (6.5.3) in the grammar, and a unary-expression can, through a chain of grammar productions, be a cast-expression inside parentheses.
1 We often think of a type-name (a specification of a type, such as int [size]
) as more of a passive declaration than an executable statement or expression, but C 2018 6.8 4 tells us “There is also an implicit full expression in which the non-constant size expressions for a variably modified type are evaluated…”