2

Can anyone explain why this program prints 4 1 instead of 4 2?

Shouldn't pre increment operator which has higher precedence get executed first and print 4 2?

#include <stdio.h>
int main() {
   int a=1;
   printf ("%ld %d",sizeof(++a),a);
   return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
programmer
  • 95
  • 1
  • 6
  • 2
    The order in which the arguments are evaluated is not specified, and this is UB – William Pursell Feb 04 '21 at 16:10
  • 3
    Even `sizeof(++a)` by itself is quite peculiar. https://wiki.sei.cmu.edu/confluence/display/c/EXP44-C.+Do+not+rely+on+side+effects+in+operands+to+sizeof%2C+_Alignof%2C+or+_Generic – Eugene Sh. Feb 04 '21 at 16:10
  • https://stackoverflow.com/questions/376278/parameter-evaluation-order-before-a-function-calling-in-c – William Pursell Feb 04 '21 at 16:13
  • https://stackoverflow.com/questions/949433/why-are-these-constructs-using-pre-and-post-increment-undefined-behavior/34536741#34536741 – William Pursell Feb 04 '21 at 16:14
  • Both links explain quite well the issue. – dreamcrash Feb 04 '21 at 16:16
  • 3
    @WilliamPursell are you sure about the dupes? With `sizeof(a++)`, `a++` is never evaluated, therefore his code is equivalent to `printf ("%ld %d",sizeof(a),a);` which is perfectly legal, no UB involved here. – Jabberwocky Feb 04 '21 at 16:19
  • Although the presence of `sizeof` does make this slightly different, the question "Shouldn't pre increment operator...get executed first" shows that the OP needs to read those questions. – William Pursell Feb 04 '21 at 16:25
  • The definite answer is Steve Summit's, I'll delete mine. – Jabberwocky Feb 04 '21 at 16:29

3 Answers3

6

Although you've already gotten several answers, I want to provide one more, because your question actually contained three separate misunderstandings, and I want to touch on all of them.

First of all, sizeof is a special operator which, by definition, does not evaluate its argument (that is, whatever subexpression it's taking the size of). So sizeof(++a) does not increment a. And sizeof(x = 5) would not assign 5 to x. And sizeof(printf("Hello!")) would not print "Hello".

Second, if we got rid of the sizeof, and simply wrote

printf("%d %d", ++a, a);

we would not be able to use precedence to figure out the behavior. Precedence is an important concept, but in general it does not help you figure out the behavior of confusing operations involving ++.

Finally, the perhaps surprising answer is that if you write

printf("%d %d", ++a, a);

it is not possible to figure out what it will do at all. It's basically undefined. (Specifically: in any function call like printf("%d %d", x, y) it's unspecified which order the arguments get evaluated in, so you don't know whether x or y gets evaluated first -- although there is an order. But then, when one of them is a and one of them is ++a, you have a situation where a is both being modified and having its value used, so there's no way to know whether the old or the new value gets used, and this makes the expression undefined. See this question for more on this issue.)

P.S. One more issue I forgot to mention, as noted by @Vlad from Moscow: %ld is not a reliable way to print the result of sizeof, which is a value of type size_t. You should use %zu if you can, or %u after casting to (unsigned) if you can't.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • "it is not possible to figure out what it will do." Because it can increment during before or after? – dreamcrash Feb 04 '21 at 16:24
  • This will benefit from a standard citation(s) – Eugene Sh. Feb 04 '21 at 16:31
  • @dreamcrash It is not possible because it's inherently confusing, and because there's no rule which defines the answer. The comma separating function arguments is not a comma operator and does not introduce a sequence point. So if you call `f(a, a++)`, there's no rule to say whether the first argument passed is the old or the incremented value of `a`. Furthermore, this is one of the situations that makes the whole expression (and, therefore, the whole program) undefined. – Steve Summit Feb 04 '21 at 18:36
  • Thanks for the explanation that is why one rather do f(a, a); a++. – dreamcrash Feb 04 '21 at 18:50
3

From the C Standard *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.

So the task of the sizeof operator is to determine the type of the expression used as an operand and then knowing the type of its operand to return the size of an object of the type. If the operand is not a variable length array then the expression used as an operand is not evaluated and the value returned by the sizeof operator is calculated at compile-time.

Thus this call

printf ("%ld %d",sizeof(++a),a);

is equivalent to the call

printf ("%ld %d",sizeof( int ),a);

and in your system sizeof( int ) is equal to 4.

So it does not matter what expression is used (except using a variable length array the size of which is calculated at run time) as an operand. It is the type of the expression that is important. For example you could even write

printf ( "%zu %d\n", sizeof( ( ++a, ++a, ++a, ++a, ++a ) ), a );

and got the same result.

Pay attention to that you should use the conversion specifier zu used for values of the type size_t instead of ld used for signed values. That is you need to write

printf ("%zu %d",sizeof(++a),a);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

According to c99 standard,

the sizeof() operator only takes into account the type of the operand, which may be an expression or the name of a type (i.e int, double, float etc) and not the value obtained on evaluating the expression.

Hence, the operand inside the sizeof() operator is not evaluate.

Papai from BEKOAIL
  • 1,469
  • 1
  • 11
  • 17
  • 3
    C17 makes it explicit: "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." 6.5.3.4 (2). – Nate Eldredge Feb 04 '21 at 16:17
  • @NateEldredge Good find. So C17 is the earliest one with this provision? – Eugene Sh. Feb 04 '21 at 16:18
  • @EugeneSh.: I didn't check the ones in between. – Nate Eldredge Feb 04 '21 at 16:19
  • 2
    The operand of `sizeof` is never evaluated, otherwise constructs like `char *p = NULL; p = malloc(length * sikzeof(*p));` wouldn't work. – Jabberwocky Feb 04 '21 at 16:21
  • @NateEldredge C11 does not have it indeed. http://port70.net/~nsz/c/c11/n1570.html#6.5.3.4p2 UPD: Ah sorry, it is in fact there – Eugene Sh. Feb 04 '21 at 16:21
  • Actually even C89 had it: 3.3.3.4 "The size is determined from the type of the operand, **which is not itself evaluated.**" And C99 has the same language as C17. – Nate Eldredge Feb 04 '21 at 17:22