VLAs and side-effect in sizeof's operand
Asked Answered
P

1

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.

Polyphemus answered 24/6, 2015 at 12:57 Comment(0)
P
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.

Polyphemus answered 24/6, 2015 at 13:6 Comment(4)
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.Intercontinental
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.Incision
@Incision 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.]Polyphemus
@Incision 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.Polyphemus

© 2022 - 2024 — McMap. All rights reserved.