How is the sizeof operator evaluated - c

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.
Footnote
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…”

The semantics of sizeof() per the (draft) C11 standard:
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.
Note "If the type of the operand is a variable length array type, the operand is evaluated". The means that the size of a VLA is computed at run time.
"otherwise, the operand is not evaluated and the result is an integer constant" means the result is evaluated at compile time.
The return type is size_t. Full stop:
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).
Note that the type is size_t. Don't use unsigned long nor unsigned long long nor anything else. Always use size_t.

You're overthinking things a bit.
Yes, when the operand of sizeof is a variable-length array expression, then that has to be evaluated at run time - otherwise, it's a compile-time operation and the operand is not evaluated.
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).
Doesn't matter - the expression *p is not evaluated as part of the sizeof operation. All that matters is the type of *p, which is known at translation. This is a perfectly valid idiom for dynamic memory allocation:
size_t size = some_value();
int *p = malloc( sizeof *p * size );
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?
Again, the expression c+c won't be evaluated - all that matters is the type.
Does sizeof return a value of type int, is it a size_t (ULL on my platform), or is it implementation-defined.
size_t. That's stated explicitly in the language definition:
6.5.3.4 The sizeof and _Alignof operators
...
5 The value of the result of both operators is implementation-defined, and its type (an
unsigned integer type) is size_t, defined in <stddef.h> (and other headers).
C 2011 Online Draft
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.
What that article is saying is that an operand that's a cast-expression won't be parsed correctly. The syntax for sizeof is
unary-expression:
...
sizeof unary-expression
sizeof ( type-name )
and the syntax for a cast-expression is
cast-expression:
unary-expression
( type-name ) cast-expression
If you write an expression like
sizeof (int) *p;
it won't be parsed as
sizeof ((int) *p);
Instead, it will be parsed as
(sizeof (int)) *p;
and interpreted as a multiplicative-expression:
multiplicative-expression * cast-expression
IOW, the compiler will think you're trying to multiply the result of sizeof (int) to the value of p (which should result in a diagnostic). If you wrap the cast-expression in parentheses, then it's parsed correctly.
Type-casting has the same precedence as the sizeof operator
That is not correct. Unary expressions (including sizeof expressions) have higher precedence than cast expressions. That's why sizeof (int) *p is parsed as (sizeof (int)) *p.

Here's an attempt to provide a complete guide to the sizeof operator and its many quirks. Warning: this post may contain heavy "language-lawyering".
Formal syntax and valid forms
sizeof is a keyword in C and the syntax is defined in C17 6.5.3 as:
sizeof unary-expression
sizeof ( type-name )
Meaning that there are two possible ways to use it: sizeof op or sizeof(op). In the former case, the operand has to be an expression (for example sizeof my_variable) and in the latter case it has to be a type (for example sizeof(int)).
When we use sizeof, we almost always use a parenthesis. Always using parenthesis is considered good practice (and Linus Torvalds famously once had one of his usual childish tantrums about it). But which form of sizeof we use depends on if we pass an expression or a type. So even when we use paranthesis around an expression, we actually don't use the second version then, but the former. Example:
int x;
printf("%zu\n", sizeof(x));
In this case we are passing an expression to sizeof. The expression is (x) and the parenthesis is a regular ("primary expression") parenthesis that we may use around any expression in C - it does not belong to the sizeof operator in this case.
"The operand to sizeof cannot be a type-cast" - precedence and associativity or...?
Following the above explanation, whenever we write sizeof (int) * p, this gets interpreted as the second form with a type name. Why?
Why isn't very obvious at all, this is in fact dang subtle. It is easy to get tricked by "operator precedence tables" like the one you link. It states that the cast operator like sizeof is a unary operator with right-to-left associativity. But this isn't actually true when digging through the dirty details of C grammar.
There is actually no such thing as a precedence table in the C standard, nor does it define associativity explicitly. Instead operator precedence is decided (as complicated as humanly possible) by a long chain of syntax definitions in chapter 6.5. In each sub chapter, the operator group refers to the previous and sometimes next operator group in the formal syntax, thereby stating that the current group has lower precedence than the previous. For 6.5.3 unary operators, it goes like:
unary-expression:
postfix-expression
++ unary-expression
-- unary-expression
unary-operator cast-expression
sizeof unary-expression
sizeof ( type-name )
_Alignof ( type-name )
unary-operator: one of
& * + - ˜ !
Translated from standardese to English, this grammar goo is to be read roughly as:
"Here is the group of unary expressions. They are the prefix ++ and -- operators, or one of the unary operators (listed separately), or sizeof in the two different forms, or _Alignof. They may follow a postfix expression, meaning that any postfix expression (or operator groups even higher up the syntax chain) has higher precedence then the unary operators. They may be followed by a cast expression, which thereby has lower precedence than the unary operators."
So depending on how you put it, there's actually a subtle error in the link or maybe they could have explained this better (I'm not sure if I even just managed myself, so I don't blame them really). Outside the formal C standard, the concept of "right-to-left associativity" doesn't work unless the cast operator is listed as part of the unary operators in that table even though it actually has lower precedence in the grammar.
So anyway, the sizeof (type-name) operator is a unary expression and takes precedence in the grammar above the cast operator. And that's why the compiler will not treat this as the two operators sizeof and (cast), but as the operator sizeof(type) followed by the binary multiplication operator.
And so sizeof (int) * p turns into equivalent of (sizeof(int)) * p, sizeof with binary multiplication, which is probably nonsense and perhaps the actual intent here was to dereference a pointer p, cast and then take the size.
We could however write something like sizeof ((int)*p)) and then the parsing order is: parenthesis, then (because of unary operator right-to-left associativity) de-reference, then cast, then sizeof.
What is the type returned by sizeof?
It returns a special large, unsigned integer type size_t (C17 6.5.3.4/5) generally regarded as "large enough" to hold the largest object allowed in the system. The type is commonly used whenever we wish to take the size of something, like when iterating through an array.
For example you might see some code on SO in the form for(size_t i=0; i<n; i++) when iterating through an array, since this is the most correct type "large enough" to contain the size of an array. (int might be too small and besides it is signed too and we can't have negative sizes.)
size_t is found in stddef.h, which in turn is included by a lot of other standard headers like stdio.h. It can hold values up to SIZE_MAX defined in stdint.h.
size_t is printed with printf by using the %zu conversion specifier, hence my previous example printf("%zu\n", sizeof(x));.
Compile-time or run-time?
sizeof is normally a compile-time operator meaning that the operand does not get evaluated. With one exception and that is variable-length arrays (VLA), where the size is simply not known at compile-time.
C17 6.5.3.4/2:
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.
Most of the time this doesn't matter. However, we can cook up some artificial example like this:
#include <stdio.h>
int main (void)
{
int size;
scanf("%d",&size); // enter 2
int arr[5][size];
printf("%zu ", sizeof(size++)); // size++ not executed
printf("%d ", size); // print 2
printf("%zu ", sizeof(arr[size++])); // size++ is executed
printf("%d ", size);
}
When I try this out and enter 2, it prints 4 2 8 3:
4 because that's the size of an int on this system.
2 because the operand size++ was not executed/evaluated.
8 because the 2 * sizeof(int) is 8.
3 because the operand arr[size++] was executed/evaluated, since arr[n] results in a VLA operand.
This behavior of which operand that gets evaluated or not is well-defined and guaranteed.
Hence a popular trick int* ptr = malloc(n * sizeof *ptr);. In case *ptr would get evaluated, it's an uninitialized pointer that we definitely can't dereference and it would have been undefined behavior. But since it is guaranteed not to get evaluated, the trick is safe.
An exception to "array decay"
sizeof is one of the few operands that is an exception to the rule of "array decay":
C17 6.3.2.1/3
Except when it is the operand of the sizeof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type "array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is not an lvalue.
sizeof is used in C's definition of a byte
The size of a byte in C is defined as per C17 3.6
3.6
byte
addressable unit of data storage large enough to hold any member of the basic character set of the execution environment
and then 6.5.3.4/4:
When sizeof is applied to an operand that has type char, unsigned char, or signed char, (or a qualified version thereof) the result is 1.
For this reason it doesn't make much sense to write things like malloc(n * sizeof(char) because sizeof(char) is by definition guaranteed to always be 1.
(The number of bits in a char is however not guaranteed to be 8.)

Related

String literals in sizeof

From my knowledge, string literals are stored in system-protected areas when used in executable code and not initialization. Does this also hold when using string literals in the sizeof function for example:
sizeof("example")
Is this string (char array) even created in memory and if not, where is it created and how does it get the correct result of 8 including the nul character?
Being the operand of sizeof has no effect on where string literals are stored:
6.5.3.4 The sizeof and _Alignof operators
...
2 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.
C 2011 Online Draft
Emphasis added.
IOW, sizeof doesn't care where something is stored, because it's not looking at storage; it's looking at the type of its operand. You can apply sizeof to things that have no storage like arithmetic expressions and numeric literals:
sizeof 100 == sizeof (int)
sizeof (2 * 3) == sizeof (int)
sizeof (x + y) == sizeof (whatever the common type of x and y is)
A string literal like "foo" has the type "4-element array of char" (3 characters plus the terminator), so sizeof "foo" evaluates to 4.
The C standard says nothing about this. It's all up to the compiler.
Most likely the string will not be part of the program but we can't know.
Nitpick: Since the result of sizeof("example") isn't used even that won't go anywhere.
Except for the special case where sizeof is applied to a variable-length array, it is always evaluated at compile-time. The C standard doesn't force compilers to not allocate the string literal in your example, but in practice every compiler I know of will not do so. Instead they will replace the whole expression with the number 8 in the machine code.
Furthermore, note that the operand of sizeof isn't evaluated for side effects either. So code like sizeof("example" + i++); will not increment i. That's where tricks like type* ptr = malloc(sizeof *ptr); come from: the sizeof expression is evaluated at compile-time, without ever de-referencing the pointer.

Is sizeof a function or an operator?

Why do we say sizeof(variable) is an operator, not a function?
It looks like a function call and when I am thinking about the meaning of operator, it appears to me something like + or - or * and so on
It's an operator, and you don't need to use brackets, except "when the operand is a type name, it must be enclosed in parentheses". This is a syntax restriction, but should not be confused with a function call.
See the last example below from the GNU documentation:
size_t a = sizeof(int);
size_t b = sizeof(float);
size_t c = sizeof(5);
size_t d = sizeof(5.143);
size_t e = sizeof a;
Without parentheses for a type name, you may see an error like this, with the gcc compiler:
test.c:7:20: error: expected expression before ‘int’
7 | size_t s = sizeof int;
| ^~~
But doing sizeof 12 or sizeof a is fine.
It's an operator because it doesn't take arguments like a function does. It operates at the syntax level.
f(int) is not a valid function call, but sizeof(int) is a valid use of sizeof.
It can also operate on variables or types, it's quite flexible by design, which is something an operator can do as it's baked deep into the C syntax.
More details can be found here.
As the grammar indicates:
unary-expression:
postfix-expression
++ unary-expression
-- unary-expression
unary-operator cast-expression
sizeof unary-expression
sizeof (type-name)
_Alignof (type-name)
unary-operator: one of
& * + - ~ !
it's an operator that not only does not require parentheses when taking an unary-expression argument, but it behaves differently with parentheses than a function call would.
Consider that given _Static_assert(sizeof(0)==4,"");, the following holds:
_Static_assert(sizeof(0)==4,"");
int takeIntGive4(int X){ (void)X; return 4; }
#include <assert.h>
int main()
{
assert(sizeof(0)["foobar"] == 1 && 1 == sizeof(char)); //if sizeof were more function-like you'd get 'a'
assert(takeIntGive4(0)["foobar"] == 'a');
}
In other words, even though sizeof(0) == takeIntGive4(0) is true on this platform, you can't always just replace takeIntGive4(0) with sizeof(0) because sizeof has a lower precedence than a function call and sizeof(0)["foobar"] will be interpreted as sizeof( (0)["foobar"] ), not (sizeof(0))["foobar"] like it would be if sizeof() were a function.
You can make sizeof function-like, by wrapping it in a macro that parenthesizes it:
#define SIZEOF(X) (sizeof(X))
but keep in mind that sizeof also additionally returns integer constant expressions (except when used with variable-length arrays (VLAs)), which you can use in case labels, bitfield sizes, and array sizes and which function call expressions are incapable of returning.
It's an operator that's evaluated at compile-time.
In fact, it only requires the function-like syntax sizeof(T) when T is a type and not an instance of a type.
So, for example, if you have a variable int x, sizeof x is permissible; but sizeof(int) is required for the type int.
It is an operator because it is built into the language. Just like + or < it is included in the language grammar. Because of this the sizeof operator can be evaluated when a program is compiled, unlike a user-defined function. For example we can define a function macro which returns the length of a (non-variable length) array:
#define LENGTH(array) (sizeof (array) / sizeof (array)[0])
The length expression is calculated when the program is compiled. At run-time the length of an array is not available (unless you store it in a separate variable).
sizeof is an operator. It is listed among the list of unary operators in section 6.5.3p1 of the C standard:
6.5.3 Unary operators
Syntax
unary-expression:
postfix-expression
++ unary-expression
-- unary-expression
unary-operator cast-expression
sizeof unary-expression
sizeof (type-name)
_Alignof (type-name)
unary-operator: one of
& * + - ~ !
As shown above it has two forms. The first form is sizeof followed by an expression. Note that in this form parenthesis are not required, unlike in a function call where they are. The second form is sizeof followed by a type name in parenthesis. Only the second form requires parenthesis, and a function cannot be passed a type name.
It is further referred to as an operator in section 6.5.3.4:
6.5.3.4 The sizeof and _Alignof operators
...
2 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
Is sizeof a function or an operator?
Why we said sizeof(variable) is operator not function?
In addition to what other have answered, code can take the address of a function, but not the address of sizeof. Much like can cannot take the address of = or *.
size_t (*f1)() = strlen;
size_t (*f2)() = sizeof;
// ^ error: expected expression before ';' token
With objects, the () are not needed with sizeof, unlike a function call.
char array[42];
size_t n1 = sizeof array;
size_t n2 = strlen(array) + 1;
sizeof can be used with types, not so with a function call.
size_t sz1 = sizeof(double);
size_t sz2 = printf(double);
// ^ error: expected expression before 'double'

Postfix operator along with sizeof operator [duplicate]

Here is the code compiled in dev c++ windows:
#include <stdio.h>
int main() {
int x = 5;
printf("%d and ", sizeof(x++)); // note 1
printf("%d\n", x); // note 2
return 0;
}
I expect x to be 6 after executing note 1. However, the output is:
4 and 5
Can anyone explain why x does not increment after note 1?
From the C99 Standard (the emphasis is mine)
6.5.3.4/2
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.
sizeof is a compile-time operator, so at the time of compilation sizeof and its operand get replaced by the result value. The operand is not evaluated (except when it is a variable length array) at all; only the type of the result matters.
short func(short x) { // this function never gets called !!
printf("%d", x); // this print never happens
return x;
}
int main() {
printf("%d", sizeof(func(3))); // all that matters to sizeof is the
// return type of the function.
return 0;
}
Output:
2
as short occupies 2 bytes on my machine.
Changing the return type of the function to double:
double func(short x) {
// rest all same
will give 8 as output.
sizeof(foo) tries really hard to discover the size of an expression at compile time:
6.5.3.4:
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.
In short: variable length arrays, run at runtime. (Note: Variable Length Arrays are a specific feature -- not arrays allocated with malloc(3).) Otherwise, only the type of the expression is computed, and that at compile time.
sizeof is a compile-time builtin operator and is not a function. This becomes very clear in the cases you can use it without the parenthesis:
(sizeof x) //this also works
Note
This answer was merged from a duplicate, which explains the late date.
Original
Except for variable length arrays sizeof does not evaluate its arguments. We can see this from the draft C99 standard section 6.5.3.4 The sizeof operator paragraph 2 which says:
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.
A comment(now removed) asked whether something like this would evaluate at run-time:
sizeof( char[x++] ) ;
and indeed it would, something like this would also work (See them both live):
sizeof( char[func()] ) ;
since they are both variable length arrays. Although, I don't see much practical use in either one.
Note, variable length arrays are covered in the draft C99 standard section 6.7.5.2 Array declarators paragraph 4:
[...] If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.
Update
In C11 the answer changes for the VLA case, in certain cases it is unspecified whether the size expression is evaluated or not. From section 6.7.6.2 Array declarators which says:
[...]Where a size expression is part of the operand of a sizeof
operator and changing the value of the size expression would not
affect the result of the operator, it is unspecified whether or not
the size expression is evaluated.
For example in a case like this (see it live):
sizeof( int (*)[x++] )
As the operand of sizeof operator is not evaluated, you can do this:
int f(); //no definition, which means we cannot call it
int main(void) {
printf("%d", sizeof(f()) ); //no linker error
return 0;
}
Online demo : http://ideone.com/S8e2Y
That is, you don't need define the function f if it is used in sizeof only. This technique is mostly used in C++ template metaprogramming, as even in C++, the operand of sizeof is not evaluated.
Why does this work? It works because the sizeof operator doesn't operate on value, instead it operates on type of the expression. So when you write sizeof(f()), it operates on the type of the expression f(), and which is nothing but the return type of the function f. The return type is always same, no matter what value the function would return if it actually executes.
In C++, you can even this:
struct A
{
A(); //no definition, which means we cannot create instance!
int f(); //no definition, which means we cannot call it
};
int main() {
std::cout << sizeof(A().f())<< std::endl;
return 0;
}
Yet it looks like, in sizeof, I'm first creating an instance of A, by writing A(), and then calling the function f on the instance, by writing A().f(), but no such thing happens.
Demo : http://ideone.com/egPMi
Here is another topic which explains some other interesting properties of sizeof:
sizeof taking two arguments
The execution cannot happen during compilation. So ++i/i++ will not happen. Also sizeof(foo()) will not execute the function but return correct type.
sizeof runs at compile-time, but x++ can only be evaluated at run-time. To solve this, the C++ standard dictates that the operand of sizeof is not evaluated. The C Standard says:
If the type of the operand [of sizeof] is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
This line here:
printf("%d and ", sizeof(x++)); // note 1
causes UB. %d Expects the type int not size_t. After you get UB the behavior is undefined including the bytes written to stdout.
If you would fix that by replacing %d with %zu or casting the value to int, but not both, you would still not increase x but that is a different problem and should be asked in a different question.
sizeof() operator gives size of the data-type only, it does not evaluate inner elements.

C assignment expression in argument to sizeof [duplicate]

Here is the code compiled in dev c++ windows:
#include <stdio.h>
int main() {
int x = 5;
printf("%d and ", sizeof(x++)); // note 1
printf("%d\n", x); // note 2
return 0;
}
I expect x to be 6 after executing note 1. However, the output is:
4 and 5
Can anyone explain why x does not increment after note 1?
From the C99 Standard (the emphasis is mine)
6.5.3.4/2
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.
sizeof is a compile-time operator, so at the time of compilation sizeof and its operand get replaced by the result value. The operand is not evaluated (except when it is a variable length array) at all; only the type of the result matters.
short func(short x) { // this function never gets called !!
printf("%d", x); // this print never happens
return x;
}
int main() {
printf("%d", sizeof(func(3))); // all that matters to sizeof is the
// return type of the function.
return 0;
}
Output:
2
as short occupies 2 bytes on my machine.
Changing the return type of the function to double:
double func(short x) {
// rest all same
will give 8 as output.
sizeof(foo) tries really hard to discover the size of an expression at compile time:
6.5.3.4:
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.
In short: variable length arrays, run at runtime. (Note: Variable Length Arrays are a specific feature -- not arrays allocated with malloc(3).) Otherwise, only the type of the expression is computed, and that at compile time.
sizeof is a compile-time builtin operator and is not a function. This becomes very clear in the cases you can use it without the parenthesis:
(sizeof x) //this also works
Note
This answer was merged from a duplicate, which explains the late date.
Original
Except for variable length arrays sizeof does not evaluate its arguments. We can see this from the draft C99 standard section 6.5.3.4 The sizeof operator paragraph 2 which says:
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.
A comment(now removed) asked whether something like this would evaluate at run-time:
sizeof( char[x++] ) ;
and indeed it would, something like this would also work (See them both live):
sizeof( char[func()] ) ;
since they are both variable length arrays. Although, I don't see much practical use in either one.
Note, variable length arrays are covered in the draft C99 standard section 6.7.5.2 Array declarators paragraph 4:
[...] If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.
Update
In C11 the answer changes for the VLA case, in certain cases it is unspecified whether the size expression is evaluated or not. From section 6.7.6.2 Array declarators which says:
[...]Where a size expression is part of the operand of a sizeof
operator and changing the value of the size expression would not
affect the result of the operator, it is unspecified whether or not
the size expression is evaluated.
For example in a case like this (see it live):
sizeof( int (*)[x++] )
As the operand of sizeof operator is not evaluated, you can do this:
int f(); //no definition, which means we cannot call it
int main(void) {
printf("%d", sizeof(f()) ); //no linker error
return 0;
}
Online demo : http://ideone.com/S8e2Y
That is, you don't need define the function f if it is used in sizeof only. This technique is mostly used in C++ template metaprogramming, as even in C++, the operand of sizeof is not evaluated.
Why does this work? It works because the sizeof operator doesn't operate on value, instead it operates on type of the expression. So when you write sizeof(f()), it operates on the type of the expression f(), and which is nothing but the return type of the function f. The return type is always same, no matter what value the function would return if it actually executes.
In C++, you can even this:
struct A
{
A(); //no definition, which means we cannot create instance!
int f(); //no definition, which means we cannot call it
};
int main() {
std::cout << sizeof(A().f())<< std::endl;
return 0;
}
Yet it looks like, in sizeof, I'm first creating an instance of A, by writing A(), and then calling the function f on the instance, by writing A().f(), but no such thing happens.
Demo : http://ideone.com/egPMi
Here is another topic which explains some other interesting properties of sizeof:
sizeof taking two arguments
The execution cannot happen during compilation. So ++i/i++ will not happen. Also sizeof(foo()) will not execute the function but return correct type.
sizeof runs at compile-time, but x++ can only be evaluated at run-time. To solve this, the C++ standard dictates that the operand of sizeof is not evaluated. The C Standard says:
If the type of the operand [of sizeof] is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.
This line here:
printf("%d and ", sizeof(x++)); // note 1
causes UB. %d Expects the type int not size_t. After you get UB the behavior is undefined including the bytes written to stdout.
If you would fix that by replacing %d with %zu or casting the value to int, but not both, you would still not increase x but that is a different problem and should be asked in a different question.
sizeof() operator gives size of the data-type only, it does not evaluate inner elements.

Is the operand of `sizeof` evaluated with a VLA?

An argument in the comments section of this answer prompted me to ask this question.
In the following code, bar points to a variable length array, so the sizeof is determined at runtime instead of compile time.
int foo = 100;
double (*bar)[foo];
The argument was about whether or not using sizeof evaluates its operand when the operand is a variable length array, making sizeof(*bar) undefined behavior when bar is not initialized.
Is it undefined behavior to use sizeof(*bar) because I'm dereferencing an uninitialized pointer? Is the operand of sizeof actually evaluated when the type is a variable length array, or does it just determine its type (how sizeof usually works)?
Edit: Everyone seems to be quoting this passage from the C11 draft. Does anyone know if this is the wording in the official standard?
Yes, this causes undefined behaviour.
In N1570 6.5.3.4/2 we have:
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.
Now we have the question: is the type of *bar a variable length array type?
Since bar is declared as pointer to VLA, dereferencing it should yield a VLA. (But I do not see concrete text specifying whether or not it does).
Note: Further discussion could be had here, perhaps it could be argued that *bar has type double[100] which is not a VLA.
Supposing we agree that the type of *bar is actually a VLA type, then in sizeof *bar, the expression *bar is evaluated.
bar is indeterminate at this point. Now looking at 6.3.2.1/1:
if an lvalue does not designate an object when it is evaluated, the
behavior is undefined
Since bar does not point to an object (by virtue of being indeterminate), evaluating *bar causes undefined behaviour.
Two other answers have already quoted N1570 6.5.3.4p2:
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.
According to that paragraph from the standard, yes, the operand of sizeof is evaluated.
I'm going to argue that this is a defect in the standard; something is evaluated at run time, but the operand is not.
Let's consider a simpler example:
int len = 100;
double vla[len];
printf("sizeof vla = %zu\n", sizeof vla);
According to the standard, sizeof vla evaluates the expression vla. But what does that mean?
In most contexts, evaluating an array expression yields the address of the initial element -- but the sizeof operator is an explicit exception to that. We might assume that evaluating vla means accessing the values of its elements, which has undefined behavior since those elements have not been initialized. But there is no other context in which evaluation of an array expression accesses the values of its elements, and absolutely no need to do so in this case. (Correction: If a string literal is used to initialize an array object, the values of the elements are evaluated.)
When the declaration of vla is executed, the compiler will create some anonymous metadata to hold the length of the array (it has to, since assigning a new value to len after vla is defined and allocated doesn't change the length of vla). All that has to be done to determine sizeof vla is to multiply that stored value by sizeof (double) (or just to retrieve the stored value if it stores the size in bytes).
sizeof can also be applied to a parenthesized type name:
int len = 100;
printf("sizeof (double[len]) = %zu\n", sizeof (double[len]));
According to the standard, the sizeof expression evaluates the type. What does that mean? Clearly it has to evaluate the current value of len. Another example:
size_t func(void);
printf("sizeof (double[func()]) = %zu\n", sizeof (double[func()]));
Here the type name includes a function call. Evaluating the sizeof expression must call the function.
But in all of these cases, there's no actual need to evaluate the elements of the array object (if there is one), and no point in doing so.
sizeof applied to anything other than a VLA can be evaluated at compile time. The difference when sizeof is applied to a VLA (either an object or a type) is that something has to be evaluated at run time. But the thing that has to be evaluated is not the operand of sizeof; it's just whatever is needed to determine the size of the operand, which is never the operand itself.
The standard says that the operand of sizeof is evaluated if that operand is of variable length array type. That's a defect in the standard.
Getting back to the example in the question:
int foo = 100;
double (*bar)[foo] = NULL;
printf("sizeof *bar = %zu\n", sizeof *bar);
I've added an initialization to NULL to make it even clearer that dereferencing bar has undefined behavior.
*bar is of type double[foo], which is a VLA type. In principle, *bar is evaluated, which would have undefined behavior since bar is uninitialized. But again, there is no need to dereference bar. The compiler will generate some code when it processes the type double[foo], including saving the value of foo (or foo * sizeof (double)) in an anonymous variable. All it has to do to evaluate sizeof *bar is to retrieve the value of that anonymous variable. And if the standard were updated to define the semantics of sizeof consistently, it would be clear that evaluating sizeof *bar is well defined and yields 100 * sizeof (double) without having to dereference bar.
Indeed the Standard seems to imply that behaviour be undefined:
re-quoting N1570 6.5.3.4/2:
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.
I think the wording from the Standard is confusing: the operand is evaluated does not mean that *bar will be evaluated. Evaluating *bar does not in any way help compute its size. sizeof(*bar) does need to be computed at run time, but the code generated for this has no need to dereference bar, it will more likely retrieve the size information from a hidden variable holding the result of the size computation at the time of bar's instantiation.

Resources