Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable length array parameter size expression with side effects

This question arose from a remark Eric Postpischil made in another thread.

I have a hard time understanding the use of variable length arrays (VLAs) as function parameters:

  • The array size is not checked.
  • The array size is not recoverable from the array because the standard type adjustment array -> pointer applies for VLAs as well, as the sizeof() calls below demonstrate; even though it would be perfectly possible to pass the whole array on the stack, just as VLAs are created on the stack when they are defined.
  • The size must be passed as an additional parameter, as with pointers.

So why does the language permit to declare function with VLA parameters if they do not offer any advantage and are adjusted like any other array argument to a pointer? Why is the size expression evaluated if it is not used by the language (e.g. to check the size of the actual argument) and is not obtainable inside the function (one still has to pass an explicit variable for that)??

In order to make clearer what I'm baffled about consider the following program (live example here). All function declarations are apparently equivalent. But as Eric pointed out in the other thread, the parameter size expression in the function's declaration is evaluated at run time. The size expression is not ignored.

It is unclear to me what benefit that would have because the size and its evaluation has no effect (beyond possible side effects). In particular, to repeat myself, that information cannot be used by code inside the function. The most obvious change would have been to pass VLAs on the stack like structures. They are, after all, usually also on the stack on the caller side. But like with arrays of constant length the type is adjusted already at declaration time to a pointer — all declarations below are equivalent. Nonetheless the useless and discarded array size expression is evaluated.

#include <stdio.h>

// Nothing to see here.
extern void ptr(int *arr);

// Identical to the above.
extern void ptr(int arr[]);

// Still identical. Is 1 evaluated? Who knows ;-).
extern void ptr(int arr[1]);

// Is printf evaluated when called? Yes.
// But the array is still adjusted to a pointer.
void ptr(int arr[printf("Call-time evaluation of size parameter\n")]){}

// This would not compile, so the declarations above must be equivalent.
// extern void ptr(int **p);

int main()
{
    ptr(0);
    ptr(0);

    return 0;
}
like image 955
Peter - Reinstate Monica Avatar asked Jul 23 '26 08:07

Peter - Reinstate Monica


1 Answers

C 2018 6.9.1 discusses function definitions and tells us in paragraph 10:

On entry to the function, the size expressions of each variably modified parameter are evaluated…

Per 6.7.6 3, a variably modified type is one that has a variable length array type in its declarators, possibly nested. (So int a[n] is variably modified since it is a variable-length array, and the fixed-length int (*a[3])[n] is also variably modified since nested within it is a variable-length array type.)

In the case of void foo(int n, int a[][n]), we see the n must be evaluated because the compiler needs the size to calculate addresses for expressions such as a[i][j]. However, for void foo(int n, int a[n]), this need does not exist, and it is not clear to me whether the text quoted above applies to the type of the parameter before adjustment (int a[n]) or after adjustment (int *a).

As I recall, when this first came to my attention a few years ago, I found both a compiler that did evaluate the expression and a compiler that did not, for a direct array parameter. Calling foo which had been defined with void foo(int a[printf("Hello, world.\n")]) {} would or would not print the string depending on the compiler. Currently, compiling with Apple LLVM 10.0.0 and clang-1000.11.45.5 on macOS 10.14.2 does print the string. (As mentioned above, for a nested array type, the expression must be evaluated, and all compilers I tried exhibited that. Unfortunately, I do not currently recall which compilers these were.)

It is not clear the array size is useful to the compiler. This aspect of the C standard might not have been completely worked out. There is a feature that adds some meaning to the size; if the size is declared with static:

void foo(int a[static SomeExpression]) { … }

then, per 6.7.6.3 7, a must point to at least SomeExpression elements. This means a must not be null, which the compiler can use to optimize some things. However, I do not have any examples of how the number itself can assist with optimization or other aspects of compilation.

like image 124
Eric Postpischil Avatar answered Jul 25 '26 04:07

Eric Postpischil



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!