cscopelanguage-lawyerlifetimevariable-length-array

does calling a function means leaving the scope of an object declared prior to that call in C?


In the C17's final draft N2176 document, the 7th paragraph of 6.2.4 section says

For such an object that does have a variable length array type, its lifetime extends from the declaration of the object until execution of the program leaves the scope of the declaration.

I was looking for all the possible ways where the execution of the program leaves the scope of declaration of such object, and I intuitively thought of a function call. Does a function call after declaration of an object of variable length array type causes the termination of the lifetime of that array (because calling a function causes the execution of program leave the caller block and enter the block of the callee function's block, which is not the scope of the variable length array that we declared in caller function. Isn't this a case of "execution leaving the scope of declaration?")?

(I commonly declare a variable length array type object and call a function after that declaration, sometimes I even pass that array as an argument to those calls. But now I'm wondering whether such a call would terminate the lifetime of that array object, thus causing any future access to that array (after the function call) to be something like an undefined behaviour.)

example code:

int main()
{
   int n; // n > 0 
   scanf("%d", &n);

   int arr[n];
   init_arr(arr, n);  //does this call terminates lifetime of the arr object, as this call causes the execution of program leave this block and move to the init_arr function's block?

   printf("%d\n", arr[0]); // is this access of arr[0] an undefined behaviour or is its value indeterminate?

   return 0;
}

Solution

  • The text you quote in 6.2.4 is an error in the C standard.

    The text is different for ordinary object types versus variable-length array types. The latter was added to the standard later, and a mistake was made when adding text for it.

    Consider the text for an object without variable length array type:

    … its lifetime extends from entry into the block with which it is associated until execution of that block ends in any way. (Entering an enclosed block or calling a function suspends, but does not end, execution of the current block.)

    Here it is clear that the object still exists (has memory reserved for it) while the called function is executing (the function may access the object, for example, if it is passed a pointer to the object) and when the function returns.

    Now consider the text for an object with variable length array type:

    … its lifetime extends from the declaration of the object until execution of the program leaves the scope of the declaration.26

    and its footnote (I have quoted from the current standard, C 2024. The text is the same except for the number of the footnote.):

    Leaving the innermost block containing the declaration, or jumping to a point in that block or an embedded block prior to the declaration, leaves the scope of the declaration.

    A reason the standard needs to say something different about variable length arrays is because the program needs to track the size of a variable length array at run-time. C 2024 6.8 says:

    A block allows a set of declarations and statements to be grouped into one syntactic unit. … the variable length array declarators of ordinary identifiers with block scope, are evaluated … each time the declaration is reached in the order of execution, as if it were a statement,…

    This text enables compilers to arrange their handling of variable length arrays to do “something” as execution passes through the declaration of the a variable length array. That could include allocating stack space and it could include storing the array size. Execution leaving the block entirely would be the same as for a non-variable-length array, but consider what happens when execution jumps to a point prior to the declaration, still within the block, and executes the declaration again. Now the declaration may specify a different size for the array. The array cannot exist with two different sizes, so either the lifetime of the first instance must have ended or the standard would have to introduce a new concept of an object changing size during its lifetime.

    Regardless of which of those you choose, the standard needed different text here for variable length arrays than for other objects, and so it specified that jumping to a point in the block prior to the declaration ended the lifetime of the object. When this text was added, a mistake was made. We know it was a mistake to say the lifetime ends when execution leaves the scope of the declaration, since that renders undefined code that we clearly want supported, such as:

    {
        char a[n];
        strcpy(a, b); // Undefined behavior; `a` does not exist once `strcpy` call starts.
        …
    }
    

    So the text for variable length array types is an error. It should say something like:

    … its lifetime extends from declaration of the object until execution of the block with which it is associated ends in any way or jumps to a point in the block (including another block embedded within it) prior to the declaration.