21

I know that sizeof never evaluates its operand, except in the specific case where said operand is a VLA. Or, I thought I knew.

void g(int n) {
    printf("g(%d)\n", n);
}

int main(void) {
    int i = 12;

    char arr[i]; // VLA

    (void)sizeof *(g(1), &arr); // Prints "g(1)"
    (void)sizeof (g(2), arr);   // Prints nothing

    return 0;
}

What is going on?

Just in case, this is compiled with GCC 5.1 on Coliru.

Jens
  • 69,818
  • 15
  • 125
  • 179
Quentin
  • 62,093
  • 7
  • 131
  • 191

1 Answers1

19

It seems that I should think twice before posting, because it struck me right after I did.

My understanding of how sizeof interacts with VLAs is actually correct, as the following quote confirms (thanks @this !) :

6.5.3.4 The sizeof and _Alignof operators
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

That's not what is causing this surprising (to me) behaviour.

(void)sizeof (g(2), arr);

In the (g(2), arr) subexpression, the comma operator triggers arr's array-to-pointer decay. Thus, sizeof's operand is no longer a VLA, but a plain char*, and it falls back to not evaluating its operand.

Apparently this behaviour has been altered in C++, where the comma operator won't decay arrays anymore.

Community
  • 1
  • 1
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 3
    You might want to add this somewhere: *6.5.3.4 The sizeof and _Alignof operators 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 fever readers will be confused. – this Jun 24 '15 at 13:37
  • Perhaps I'm missing something, however; sizeof() is a compile time operator, not a run time function. so (void)sizeof() is nonsence as sizeof() returns a size_t (effectively a long int) value, not a pointer and sizeof() only expects one parameter. – user3629249 Jun 25 '15 at 22:25
  • @user3629249 `sizeof` is a strictly compile-time operator and doesn't evaluate its operant, *except* when its operand is a VLA, then it does evaluate it, at runtime. The `(void)` cast has nothing to do with pointers : it explicitly discards the value `sizeof` just calculated. It doesn't actually affect the behaviour of the program, but is better style than just having an expression that is computed, and then dropped on the floor (for which GCC produces a warning). [cont.] – Quentin Jun 26 '15 at 07:37
  • @user3629249 Finally, don't let the syntax trick you : `sizeof` is an operator, not a function, and it doesn't take parentheses when its operand is an expression (it does for a type). The parentheses here are to give precedence to the comma operator (which is not the comma of an argumet list). `sizeof` still takes a single operand, that is the result of what's in the parentheses. – Quentin Jun 26 '15 at 07:40