Variable Length Array (Vla) in C++ Compilers

Variable Length Array (VLA) in C++ compilers

Why does the compiler accept that declaration?

Because its authors chose to make it do so.

GCC in particular allows, by default, a lot of non-standard stuff that was historically accepted by old C compilers. They like "compatibility" in that sense.

What does the standard say about [it]?

Precisely what the warning states it says about it: ISO C++ forbids variable length arrays.

C++ does not have VLAs.

Where you see one being accepted, it is a compiler extension; to find out how that compiler implements such an extension, you would have to ask the compiler's authors (or examine its source, if applicable).

How do I tell if I am using VLA (Variable Length Array)?

int *numberArray; numberArray = new int[size]; is not a variable-length array, it's a dynamically allocated array. That's fine. Note that you have to delete[] it when done, which you do.

A VLA declaration would look like int numberArray[size]; where size is not a constant. It gets automatically deallocated when it goes out of scope, so you don't use delete on it. They are typically allocated on the stack and so can be created and deallocated very fast, but have various pitfalls. The main one is that there is no way to check if enough stack space is available, and your program will simply crash if there isn't; there is no way to safely detect or handle that error. So you would have to be very careful about checking that the value of size is reasonable.

VLAs are not part of standard C++, but some compilers support them anyway.

How to use Visual Studio as an IDE with variable length array(VLA) working?

VLAs can be used in "CMake Project" which are C++ applications. Create a new "CMake Project" instead of "Console Application" and then go to Project in upper-left menus and select the last option (CMake settings for ProjectName). It will open a json file. Under Toolset option, click the drop down menu to select Clang.

Above VS2019 16.1, Clang is already available. If it's not available, click "Modify" VS2019
in Visual Studio Installer and from C/C++ Development tools, select "Clang tools for windows". This will install Clang.

So the main thing is to select "CMake Project" instead of "Console Application" which is often not shown in any instructions.
VLAs will work in the .cpp file now and Visual Studio 2019 can be used as an IDE will VLA support.

https://devblogs.microsoft.com/cppblog/clang-llvm-support-in-visual-studio/

Variable length arrays (VLA) in C and C++

Yes your reasoning is correct, that is how these different forms of array declarations and definitions are viewed by C and C++.

As others already stated, VLA with a veritable variable length (non-const) in global scope is difficult to make sense. What would the evaluation order be, e.g if the the length expression would refer to an object of a different compilation unit? C++ doesn't have VLA, but it has dynamic initialization of objects at file scope. And already this gives you quite a head ache, if you have to rely on evaluation order.

This leaves the small gap for C concerning length expressions that contain a const qualified object, which isn't allowed. This comes from the fact that such objects are not considered "integer constant expressions" by the C standard. This could perhaps change in future versions, but up to now the C committee didn't find it necessary to allow for such a thing: there are enum constants that play that role in C. Their only limitation is that they are limited to int in C, it would be nice to also have them size_t.

Why is this VLA (variable-length array) definition unreliable?

Diagnosis

In the code in the question, the variable n is uninitialized when it is used in the definition of vla. Indeed, with GCC set fussy, the code shown produces a compilation error (it'll give a warning if you are careless enough to omit -Werror from your compilation options — don't do that!):

$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -Wstrict-prototypes -Wmissing-prototypes -Wshadow -pedantic-errors  vla37.c -o vla37  
vla37.c: In function ‘main’:
vla37.c:6:5: error: ‘n’ is used uninitialized [-Werror=uninitialized]
6 | double vla[n];
| ^~~~~~
vla37.c:5:9: note: ‘n’ declared here
5 | int n;
| ^
cc1: all warnings being treated as errors
$

(That's from GCC 11.2.0 on a machine running RedHat RHEL 7.4.)

The trouble is that the compiler must know the size of the array when it is declared, but the value in n is undefined (indeterminate) because it is uninitialized. It could be huge; it could be zero; it could be negative.

Prescription

The cure for the problem is simple — make sure the size is known and sane before it is used to declare the VLA:

#include <stdio.h>

int main(void)
{
int n;

if (scanf("%d", &n) != 1)
return 1;

double vla[n];
for (int i = 0; i < n; i++)
{
if (scanf("%lf", &vla[i]) != 1)
return 1;
}
for (int i = 0; i < n; i++)
printf("[%d] = %.2f\n", i, vla[i]);
return 0;
}

Now you can run the result:

$ vla41 <<<'9 2.34 3.45 6.12 8.12 99.60 -12.31 1 2 3'
[0] = 2.34
[1] = 3.45
[2] = 6.12
[3] = 8.12
[4] = 99.60
[5] = -12.31
[6] = 1.00
[7] = 2.00
[8] = 3.00
$

(That assumes your shell is Bash or compatible with Bash and supports 'here strings' (the <<<'…' notation.)

The code shown in the question and in this answer is barely adequate in handling I/O errors; it detects input problems but doesn't provide useful feedback to the user.
The code shown does not validate the value of n for plausibility. You should ensure that the size is larger than zero and less than some upper bound. The maximum size depends on the size of the data being stored in the VLA and the platform you're on.

If you're on a Unix-like machine, you probably have 8 MiB of stack; if you're on a Windows machine, you probably have 1 MiB of stack; if you're on an embedded system, you may have much less stack available to you. You need to leave some stack space for other code too, so you should probably check that the array size is not more than, for sake of discussion, 1024 — that would be 8 KiB of stack for an array of double, which is not huge at all but it provides plenty of space for most homework programs. Tweak the number larger to suit your purposes, but when the number grows, you should use malloc() et al to dynamically allocate the array instead of using an on-stack VLA. For example, on a Windows machine, if you use a VLA of type int, setting the size above 262,144 (256 * 1024) almost guarantees that your program will crash, and it may crash at somewhat smaller sizes than that.

Lessons to learn

  • Compile with stringent warning options.
  • Compile with -Werror or its equivalent so warnings are treated as errors.
  • Make sure the variable defining the size of a VLA is initialized before defining the array.
    • Not too small (not zero, not negative).
    • Not too big (not using more than 1 megabyte on Windows, 8 megabytes on Unix).
    • Leave a decent margin for other code to use as well.

Note that all compilers that support VLAs also support variables defined at arbitrary points within a function. Both features were added in C99. VLAs were made optional in C11 — and a compiler should define __STDC_NO_VLA__ if it does not support VLAs at all but claims conformance with C11 or later.

C++ and variable-length arrays

Standard C++ does not support C-style VLAs. However, GCC (g++) does support them by default as an extension. This can cause confusion.

Why is this code considered a VLA even though it uses an int?

First of all, variable length arrays doesn't exists in C++. There are no variable length arrays in C++. This answer only applies to specific GNU compiler extension to support variable length arrays in C++.

why this is even a VLA size is still known to the compiler

The question is not about "what is known to the compiler", the question is about the definitions. The terms and language are defined and it is defined what is a VLA and what isn't. A variable length array is defined as an array that size is not a integer constant expression - int vla[<not integer constant expresion>];. From C99 6.7.5.2p4:

If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.

What is and isn't an integer constant expression has in turn once again a very certain definition.

In C, with both definitions int size = 10; and const int size = 10; the size as an expression is not an integer constant expression. So both array definitions you showed are variable length arrays. The end.

In C++, there is no integer constant expression and most probably (my guess!) GNU compiler working in C++ with VLA extension enabled checks if the size expression inside array declaration is usable in a constant expression. In C++, a const-qualifed integral type is usable in a constant expression, so const int size = 10; int my_array [size]; is a normal array. An integral type that isn't const-qualifed is not usable in a constant expression, so int size = 10; int my_array [size]; is invalid in C++ and in a GNU compiler with extensions enabled results in a variable length array.

Why is this code considered a VLA even though it uses an int?

"Using an int" doesn't necessarily make an expression a constant expression, which is needed for expression inside brackets inside an array definition that isn't a variable length array.

how are these 2 different methods considered as VLA?

As for C, the expression inside brackets inside an array definition has to be a constant expression, because it isn't, these two code snippets result in a VLA declaration.

How do compilers treat variable length arrays

In the C99 version of the C standard, variable length arrays are permitted. However, they are not permitted in any version of C++; you're seeing a G++ extension. Note that Microsoft's C compiler does not fully support C99; since G++ supports C99 it's easy enough to apply the VLA support to C++ as an extension.

As to how the compiler usually implements VLAs, it's the same as alloca() (except that it has to keep the size around for sizeof) - the compiler saves the original stack pointer, then adjusts it down by however many bytes it calculates that it needs. The downside is function entry and exit is a bit more complicated as the compiler needs to store where to reset the stack pointer to rather than just adjusting by fixed constants.



Related Topics



Leave a reply



Submit