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'
Related
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.)
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.
Why does this code compile?
_Static uint32_t my_arr[2];
_Static_assert(sizeof(my_arr) == 8, "");
_Static_assert(sizeof(my_arr[0]) == 4, "");
_Static_assert(sizeof(my_arr)[0] == 4, "");
The first 2 asserts are obviously correct, but I would have expected the last line to fail, as my understanding is that sizeof() should evaluate to an integer literal, which can't be treated as an array. In other words, it would fail in the same way that the following line fails:
_Static_assert(4[0] == 4, "");
Interestingly, the following does indeed fail to compile (which should be doing the same thing, no?):
_Static_assert(*sizeof(my_arr) == 4, "");
error: invalid type argument of unary '*' (have 'long unsigned int')
_Static_assert(*sizeof(my_arr) == 4, "");
If it matters, I'm using gcc 5.3.0
sizeof is not a function. It's a unary operator like ! or ~.
sizeof(my_arr)[0] parses as sizeof (my_arr)[0], which is just sizeof my_arr[0] with redundant parentheses.
This is just like !(my_arr)[0] parses as !(my_arr[0]).
In general, postfix operators have higher precedence than prefix operators in C. sizeof *a[i]++ parses as sizeof (*((a[i])++)) (the postfix operators [] and ++ are applied to a first, then the prefix operators * and sizeof).
(This is the expression version of sizeof. There's also a type version, which takes a parenthesized type name: sizeof (TYPE). In that case the parens would be required and part of the sizeof syntax.)
sizeof has two "versions": sizeof(type name) and sizeof expression. The former requires a pair of () around its argument. But the latter - the one with an expression as an argument - does not have () around its argument. Whatever () you use in the argument is seen as part of the argument expression, not part of sizeof syntax itself.
Since my_arr is known to the compiler as an object name, not a type name, your sizeof(my_arr)[0] is actually seen by the compiler as sizeof applied to an expression: sizeof (my_arr)[0], where (my_arr)[0] is the argument expression. The () surrounding the array name is purely superfluous. The whole expression is interpreted as sizeof my_arr[0]. This is equivalent to your previous sizeof(my_arr[0]).
(This means, BTW, that your previous sizeof(my_arr[0]) also contains a pair of superfluous ().)
It is a rather widespread misconception that sizeof's syntax somehow requires a pair of () around its argument. This misconception is what misleads people's intuition when interpreting such expressions as sizeof(my_arr)[0].
[] have a higher precendence than sizeof. So sizeof(my_arr)[0] is the same as sizeof((my_arr)[0]).
Here is a link to a precedence table.
You're using the version of the sizeof operator that takes an expression as parameter. Unlike the one that takes a type, it doesn't require parentheses. Hence, the operand is simply (my_arr)[0], with the parentheses being redundant.
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.
The below fails to compile:
typedef int arr[10];
int main(void) {
return sizeof arr;
}
sizeof.c:3: error: expected expression before ‘arr’
but if I change it to
sizeof(arr);
everything is fine. Why?
According to 6.5.3, there are two forms for sizeof as the following:
sizeof unary-expression
sizeof ( type-name )
Since arr in your code is a type-name, it has to be parenthesized.
That's the way the language is specified, type names must be parenthesized here.
Suppose the grammar looked like this:
sizeof unary-expression
sizeof type-name
Now, e.g. the following expression would be ambiguous:
sizeof int * + 0
It could be either sizeof(int *) + 0 or sizeof(int) * +0. This ambiguity doesn't arise for unary expressions, as an asterisk appended to an expression isn't an expression (but for some type names, appending one, is again a type name).
Something had to be specified here and requiring type-names to be parenthesized is a way to solve the ambiguity.
I think it's because you have typedef. If you remove it, it should compile.
Example from wikipedia:
/* the following code fragment illustrates the use of sizeof
* with variables and expressions (no parentheses needed),
* and with type names (parentheses needed)
*/
char c;
printf("%zu,%zu\n", sizeof c, sizeof (int));