Why aren't variable-length arrays part of the C++ standard?
Asked Answered
C

10

458

I haven't used C very much in the last few years. When I read this question today I came across some C syntax which I wasn't familiar with.

Apparently in C99 the following syntax is valid:

void foo(int n) {
    int values[n]; //Declare a variable length array
}

This seems like a pretty useful feature. Was there ever a discussion about adding it to the C++ standard, and if so, why it was omitted?

Some potential reasons:

  • Hairy for compiler vendors to implement
  • Incompatible with some other part of the standard
  • Functionality can be emulated with other C++ constructs

The C++ standard states that array size must be a constant expression (8.3.4.1).

Yes, of course I realize that in the toy example one could use std::vector<int> values(m);, but this allocates memory from the heap and not the stack. And if I want a multidimensional array like:

void foo(int x, int y, int z) {
    int values[x][y][z]; // Declare a variable length array
}

the vector version becomes pretty clumsy:

void foo(int x, int y, int z) {
    vector< vector< vector<int> > > values( /* Really painful expression here. */);
}

The slices, rows and columns will also potentially be spread all over memory.

Looking at the discussion at comp.std.c++ it's clear that this question is pretty controversial with some very heavyweight names on both sides of the argument. It's certainly not obvious that a std::vector is always a better solution.

Convince answered 11/12, 2009 at 10:15 Comment(14)
Just out of curiosity, why does it need to be allocated on the stack? Are you that affraid of heap allocation performance issues?Nauru
@Dimitri Not really, but there's no denying that stack allocation will be faster than heap allocation. And in some cases this may matter.Convince
The main advantage of variable length arrays that all data is close together so when you iterate through this array you read and write bytes next to each other. Your data is fetched into the cache and cpu can work on it without fetching and sending the bytes to/from the memory.Abradant
Variable length arrays are also may be used to replace preprocessor constants with static const variables. Also in C you don't have another options for VLA, and it is sometimes needed to write portable C/C++ code (compatible with both compilers).Amethist
as an aside, it appears clang++ allows VLAs.Sabec
Not a fair comparison, as for "really painful expression here" you don't have any equivalent in the C version. Try writing an initializer for a VLA (hint: impossible)Shaw
As long as the type is trivially constructable and cheap to construct (like a primitive) then you could define a sensible maximum and then allocate an array of fixed size. Then, just work in the [0, n) range.Poco
They were proposed for C++14 as runtime-sized arrays (which must be allocated on the stack, and had some differences from C VLAs), along with class template, dynarray (the std::array to their raw array), but both were voted out (with the latter being relegated to a TS). Apparently, dynarray was intended to be coupled with special compiler magic, so that when used on the stack, it could be optimised into being as efficient as a runtime-sized array (at least, on platforms with a traditional stack-and-heap setup). I'm not all that familiar with the specifics.Dru
Clang supports the class as std::experimental::dynarray, in header <experimental/dynarray>, as part of libc++. Both Clang and GCC support C VLAs, with GCC (but not Clang) also allowing them to be initialised; note that these come with the C limitation that sizeof is evaluated at runtime for VLAs.Dru
As for GCC and clang: my recommendation is -Werror=vla (GCC: include in a spec file).Diagnostician
There is the alternative of using alloca() or _alloca() to allocate memory from the stack, which gets automatically freed when a function returns.Equidistance
It seems only -pedantic will produce a warning on their usage in GCC/Clang.Harod
Rcgldr, a variable length array would be superior to alloca in every way.Luciennelucier
Another argument against VLAs: lkml.org/lkml/2018/3/7/621Homing
I
251

There recently was a discussion about this kicked off in usenet: Why no VLAs in C++0x.

I agree with those people that seem to agree that having to create a potential large array on the stack, which usually has only little space available, isn't good. The argument is, if you know the size beforehand, you can use a static array. And if you don't know the size beforehand, you will write unsafe code.

C99 VLAs could provide a small benefit of being able to create small arrays without wasting space or calling constructors for unused elements, but they will introduce rather large changes to the type system (you need to be able to specify types depending on runtime values - this does not yet exist in current C++, except for new operator type-specifiers, but they are treated specially, so that the runtime-ness doesn't escape the scope of the new operator).

You can use std::vector, but it is not quite the same, as it uses dynamic memory, and making it use one's own stack-allocator isn't exactly easy (alignment is an issue, too). It also doesn't solve the same problem, because a vector is a resizable container, whereas VLAs are fixed-size. The C++ Dynamic Array proposal is intended to introduce a library based solution, as alternative to a language based VLA. However, it's not going to be part of C++0x, as far as I know.

Immensurable answered 11/12, 2009 at 10:28 Comment(20)
+1 and accepted. One comment though, I think the safety argument is a little bit weak since there are so many other ways to cause stack overflows. The safety argument could be used to support the position that you should never use recursion and that you should allocate all objects from the heap.Convince
So you're saying that because there are other ways to cause stack overflows, we might as well encourage more of them?Cyanogen
@Andreas, agreed about the weakness. But for recursion, it takes a huge number of calls until stack is eaten up, and if that can be the case, people would use iteration. As some people on the usenet thread say, though, this is not an argument against VLAs in all cases, since sometimes you definitely may know an upper bound. But in those cases, from what i see a static array can equally be sufficient, since it would not waste much space anyway (if it would, then you would actually have to ask whether the stack area is large enough again).Immensurable
Also look at Matt Austern's answer in that thread: The language specification of VLAs would probably considerably more complex for C++, because of the stricter type matches in C++ (example: C allows assigning a T(*)[] to a T(*)[N] - in C++ this is not allowed, since C++ does not know about "type compatibility" - it requires exact matches), type parameters, exceptions, con- and destructors and stuffs. I'm not sure whether the benefits of VLAs would really pay off all that work. But then, i have never used VLAs in real life, so i probably don't know good use cases for them.Immensurable
@Johannes: there are probably some cases where use of a VLA overcomes your objections. For instance a recursive function, at each step i requires N_i amount of space. The only upper limit I have, S, is on the sum of all N_i, not any individual N_i. So I might know that (a) S is small enough for my stack, so VLA's won't overflow, but (b) S * the max depth of recursion is too big for my stack. Of course this doesn't justify a tricky language feature: I should just allocate S on the stack at the top of the recursion, and pass down a pointer to what's left unused.Mcauley
Ah, I now see that Kaz Kylheku already gave that example on the com.std.c++ thread. The Windows string example given by Alf afterwards actually seems wrong to me, in that for my taste it puts too much responsibility on the caller of a narrow string function, to ensure that there is enough stack left for the library to use a VLA. VLA's don't do what you actually want in that kind of string-conversion situation, which is to allocate on the stack if there's space and from the heap if there isn't. But I guess on Windows, there's "room" on the stack iff there is on the heap, pretty much.Mcauley
@Johannes: "benefit of being able to create small arrays without wasting space or calling constructors for not used elements". A boost::array-like container which takes a runtime parameter for how many element to initialize of the allocation would be excactly as useful (except for maybe in recursive algorithms). Any runtime sized array ought to have a max number of elements, and allocating this amount of stack space immediately will give you your un-avoidable crash sooner, which is good.Torp
@Viktor, indeed. But a maximally sized array will also call all constructors, which might not be what you want. I think the array-like container is a good compromise between changing the language to introduce VLAs (which i believe is a large change), and having no support at all.Immensurable
@Johannes: No, what I mean is; if the array is a wrapper around a primitive type, (ie unsigned char[N * sizeof(T)]) the array can call the placement new/constructor for the number of elements in the runtime size variable. (if N denotes the static maximum size, T the type and n the runtime size, your constructor does something like for(i...n) {new (this + sizof(T)*i) T();}Torp
@Viktor, i agree. Such a thing would be useful, and you could provide N as a template parameter as the maximal count of items. And writing it will be easier in C++0x with the various alignment tools available (alignof, etc).Immensurable
The main reason I want VLAs is that they serve very well for evaluating polynomials of arbitrary degree--I need the speed of stack allocation, and the value for n will not be very high--and it's usually 2-4, so setting an arbitrary maximum of 20 feels wasteful. I currently do this with a messy template solution, but VLAs would fit much more cleanly.Hormonal
@AHelps: Perhaps what would be best for that would be a type that behaves somewhat like vector but requires a fixed LIFO usage pattern and maintains one or more per-thread statically-allocated buffers which are generally sized according to the largest total allocation the thread has ever used, but which could be explicitly trimmed. A normal "allocation" would in the common case require nothing more than a pointer copy, pointer-from-pointer subtraction, integer comparison, and pointer addition; de-allocation would simply require a pointer copy. Not much slower than a VLA.Scudo
BTW, in C++ I recommend folly's or boost's small_vector (github.com/facebook/folly/blob/master/folly/docs/…). Folly's version allows you to also forbid it to use the heap. That solves the issues with unneeded constructor calls with a "large enough stack array" approach.Immensurable
@AndreasBrinck Well said. Isn't c++' way that let programmers decide what to use and use it safely.Diatomic
@JohannesSchaub-litb https://mcmap.net/q/17196/-why-aren-39-t-variable-length-arrays-part-of-the-c-standard/11862989 can you please answer to this Question. it's related to this only but in context of dynamic array.Snooker
Strange argument, to claim that not knowing the size beforehand means unsafe code.Scrod
@Scrod what it was meant to say is that if you don't know an upper bound.Immensurable
I think a utility that statically allocated a fixed amount of memory on the stack, and only called the constructor for some of the elements, would not be unsafe of course.Immensurable
"And if you don't know the size beforehand, you will write unsafe code" I don't buy that argument. Because... how is std::something foo [constant_expression]; any safer to declare at local scope? I don't know the size of standard containers beforehand either. At least in case of application logic VLA you have control of the size yourself. With C++ standard container classes, you rely on the library implementation making a reasonably compact class and hoping that some other library vendor made it somewhat similar sized, so that C++ code can be ported between compilers without stack overflows.Zimmer
VLA is not more unsafer than having a big stack object inside a if condition on a recursive call; Should we remove such possibilities just because they are unsafe? This is the hypocrisy of the C++ standards commitee; They did everything they have to make sure designated initializers not to be in standard in such a long time. So I do not trust them when they say either: difficult to implement or compile time (same with modules) or there are loopholes or it is unsafe... All they did resulted cripping the progress and it should not be tolerated anymore,Wow
T
369

(Background: I have some experience implementing C and C++ compilers.)

Variable-length arrays in C99 were basically a misstep. In order to support VLAs, C99 had to make the following concessions to common sense:

  • sizeof x is no longer always a compile-time constant; the compiler must sometimes generate code to evaluate a sizeof-expression at runtime.

  • Allowing two-dimensional VLAs (int A[x][y]) required a new syntax for declaring functions that take 2D VLAs as parameters: void foo(int n, int A[][*]).

  • Less importantly in the C++ world, but extremely important for C's target audience of embedded-systems programmers, declaring a VLA means chomping an arbitrarily large chunk of your stack. This is a guaranteed stack-overflow and crash. (Anytime you declare int A[n], you're implicitly asserting that you have 2GB of stack to spare. After all, if you know "n is definitely less than 1000 here", then you would just declare int A[1000]. Substituting the 32-bit integer n for 1000 is an admission that you have no idea what the behavior of your program ought to be.)

Okay, so let's move to talking about C++ now. In C++, we have the same strong distinction between "type system" and "value system" that C89 does… but we've really started to rely on it in ways that C has not. For example:

template<typename T> struct S { ... };
int A[n];
S<decltype(A)> s;  // equivalently, S<int[n]> s;

If n weren't a compile-time constant (i.e., if A were of variably modified type), then what on earth would be the type of S? Would S's type also be determined only at runtime?

What about this:

template<typename T> bool myfunc(T& t1, T& t2) { ... };
int A1[n1], A2[n2];
myfunc(A1, A2);

The compiler must generate code for some instantiation of myfunc. What should that code look like? How can we statically generate that code, if we don't know the type of A1 at compile time?

Worse, what if it turns out at runtime that n1 != n2, so that !std::is_same<decltype(A1), decltype(A2)>()? In that case, the call to myfunc shouldn't even compile, because template type deduction should fail! How could we possibly emulate that behavior at runtime?

Basically, C++ is moving in the direction of pushing more and more decisions into compile-time: template code generation, constexpr function evaluation, and so on. Meanwhile, C99 was busy pushing traditionally compile-time decisions (e.g. sizeof) into the runtime. With this in mind, does it really even make sense to expend any effort trying to integrate C99-style VLAs into C++?

As every other answerer has already pointed out, C++ provides lots of heap-allocation mechanisms (std::unique_ptr<int[]> A = new int[n]; or std::vector<int> A(n); being the obvious ones) when you really want to convey the idea "I have no idea how much RAM I might need." And C++ provides a nifty exception-handling model for dealing with the inevitable situation that the amount of RAM you need is greater than the amount of RAM you have. But hopefully this answer gives you a good idea of why C99-style VLAs were not a good fit for C++ — and not really even a good fit for C99. ;)


For more on the topic, see N3810 "Alternatives for Array Extensions", Bjarne Stroustrup's October 2013 paper on VLAs. Bjarne's POV is very different from mine; N3810 focuses more on finding a good C++ish syntax for the things, and on discouraging the use of raw arrays in C++, whereas I focused more on the implications for metaprogramming and the typesystem. I don't know if he considers the metaprogramming/typesystem implications solved, solvable, or merely uninteresting.


A good blog post that hits many of these same points is "Legitimate Use of Variable Length Arrays" (Chris Wellons, 2019-10-27).

Therrien answered 3/2, 2014 at 3:1 Comment(23)
I agree VLAs were just wrong. The much more widely implemented, and far more useful, alloca() should have been standardized in C99 instead. VLAs are what happens when a standards committee jumps out ahead of implementations, instead of the other way around.Impecunious
I didn't realise that C allowed VLAs as function parameters. I agree (for what that is worth, as I don't write compilers) that that is rather frightening. VLAs as local variables on the other hand do seem sensible to me. Granted you can overflow your stack, but you have to think about that any time you use local variables, so there is nothing really new there. And the "sizeof" thing - isn't that just the price for a feature?Cacao
The variably-modified type system is a great addition IMO, and none of your bullet points violate common sense. (1) the C standard does not distinguish between "compile-time" and "run-time" so this is a non-issue; (2) The * is optional, you can (and should) write int A[][n]; (3) You can use the type system without actually declaring any VLAs. For example a function can accept array of variably modified type, and it can be called with non-VLA 2-D arrays of differing dimensions. However you make valid points in the latter part of your post.Shaw
the type argument becomes especially obvious when transitioning to gsl::span<>. a VLA (since g++ supports those as extensions of c++) does not make sense when being passed through a template trying to deduce its size.Bushy
You know, all of that useful stuff you described would continue to exist even if VLAs were supported - it would just not compile for VLAs, just for fixed-length arrays. Not that I disagree with your VLA reservations, it's just that they're more about use than about having the feature. Doing reinterpret_casts of void * to a function pointer also means unexpected behavior and a possible stack overflow, but that doesn't mean it shouldn't be possible.Woolpack
"declaring a VLA means chomping an arbitrarily large chunk of your stack. This is a guaranteed stack-overflow and crash. (Anytime you declare int A[n], you're implicitly asserting that you have 2GB of stack to spare" is empirically false. I just ran a VLA program with a stack far less than 2GB without any stack overflow.Fronniah
@Jeff: What was the maximum value of n in your test case, and what was the size of your stack? I suggest you try inputting a value for n at least as large as the size of your stack. (And if there's no way for the user to control the value of n in your program, then I suggest you just propagate the maximum value of n straight into the declaration: declare int A[1000] or whatever it is you need. VLAs are only necessary, and only dangerous, when the maximum value of n isn't bounded by any small compile-time constant.)Therrien
In my experience the main reason I like VLA is that I can index 2 dimensional arrays like arrays a[2][3] and memcpy(&a[1][2], ptr). I never really cared if that memory is on the stack or on the heap. If there was a way to generate a view into some buffer as arbitrary shape matrix and access it like array that would be all that I would want from VLA. That means a class that gets a pointer, and provides operator [] and & just for the syntactic sugar.Cormac
@MadScientist: The standard should have included a LIFO-allocation function whose freeing intrinsic requires passing a pointer and size of the earlier allocation, a pair of mark/release intrinsics which may use something larger than a pointer [as with va_list], where the release would clean up any LIFO-allocated objects since the corresponding "mark", and a specification that returning from a function in which things have been LIFO-allocated may free the objects or not, at the implementation's convenience.Scudo
@MadScientist: Such semantics could be supported by any platform [unlike those of alloca], and separating out the mark/release semantics allow implementations that do such allocation on the heap to recover storage cleanly after a longjmp.Scudo
Since alloca() can be implemented using such intrinsics it's by definition true that alloca() could be implemented on any platform, as a compiler standard function. There's no reason that the compiler couldn't detect the first instance of alloca() and arrange for the types of marks and releases to be embedded in the code, and there's no reason a compiler can't implement alloca() using the heap if it can't be done with the stack. What is hard/non-portable is have alloca() implemented on top of a C compiler, so that it works across a wide array of compilers and operating systems.Impecunious
@MadScientist: The semantics I describe could be supported on any hosted compiler that would allow the addition of a library and header file. While a hosted compiler for any platform could handle alloca() if it were acceptable to have longjmp abandon any stranded made by in functions that are exited thereby, I think it would be better to define semantics that can be supported by existing compilers, and that can coexist with longjmp.Scudo
Incidentally, if one wants to use the convention of passing array-size arguments after pointers to teh arrays themselves, that can be done using K&R1 function syntax, but not using "modern" syntax.Scudo
Well, somehow C++ compilers have solved those problems by allowing VLAs as an extension. You also miss the point regarding difference between VLAs of small size and fixed array of size equal to the maximum VLA size - you'd have to default construct all elements for static array, and you might not be able to!Yeanling
@SergeyA: Interesting comment about "you might not be able to [default-construct A]." True: in the one special case where the length of A vla[n] is dynamically determined to be 0, we won't need to call A::A() at all. So, should the compiler require A::A() to exist and be callable? Clang's VLA extension says yes; GCC's extension says "sometimes." godbolt.org/z/DDW4Lx (And re "solved those problems," see godbolt.org/z/Guvxoo .)Therrien
'After all, if you know "n is definitely less than 1000 here", then you would just declare int A[1000].' is just nonsense. If, say, the VLA length is 10 on 99.99% of function invocations and only hits its upper bound of 1000 on 0.01% of invocations, you've just basically wasted 1000 bytes that will never be freed as long as the frame remains on the stack -- which could be almost all the time if the function is high up your control flow hierarchy. You may think 1000 bytes is not much, but then factor in all your cache misses every time your CPU has to move in and out of that function!Nb
As explained in the link you provide by this answer, making VLA an optional feature was misguided. Allocating actual VLA objects is indeed a mildly useful feature, but using pointers to VLA is a major improvement of the C language. There's a whole lot of uses for such pointers: clearer multi-dimensional malloc, type safe function calls with size passed etc etc. They improve type safety and readability.Zimmer
As for embedded, I work almost exclusively with embedded systems and I use pointers to VLA all the time. Allocating VLA objects is however banned by my coding standard. But I don't recall ever seeing a stack overflow caused by VLA in any embedded system. The "anti-VLA movement" rather seems to be coming from the PC people with Microsoft in front. Because if VLA are allowed, MS would have to update their so called "2019" compiler from 1989 to fall in line with the 1999 version of the language.Zimmer
@Lundin: Having the Standard recognize categories of "full-featured" and "limited function" compilers, with the proviso that conforming limited function compilers would be required to reject properly-written programs that need unsupported features, would be much more useful than requiring that people wishing to write "conforming" implementations waste effort on features that will be useless to their customers. A microcontroller with 16 bytes of RAM might be unable to usefully process most C programs, but that doesn't mean the Standard shouldn't specify the behavior of those it can handle.Scudo
@Scudo Turns out that pointers to VLA might become mandatory again since the committee recognized it was indeed misguided to remove them. It's been proposed anyway.Zimmer
@Lundin: Indeed so, but if were looking to buy a compiler and had a choice between having the vendor expend the effort necessary to support VLA pointers, versus having the vendor expend that same amount of effort doing something else, officially allowing the vendor to expend efforts elsewhere provided they reject programs that require VLA-pointer support would seem better than requiring that they waste time implementing features their customers would never use.Scudo
The trouble with a fixed size is that when you can't really say in advance what the size will be (but it won't be anything like 1e5 or more), you tend to have to allocate one or a few orders of magnitude more than what you actually need most of the time. Are you saying there's no downside to allocating a fixed length of say 20000 when in practice, most of the time you need something like 500 to 1500, and occasionally say 11000 or 13000 ?Jepum
"After all, if you know 'n is definitely less than 1000 here', then you would just declare int A[1000]" what on earth were you thinking when you wrote this? And in the context of an embedded system with limited space too, you would always allocate an array of max potential size? Preposterous. What you've pointed out here is exactly an argument FOR VLAs; to avoid code being like you described...Walking
I
251

There recently was a discussion about this kicked off in usenet: Why no VLAs in C++0x.

I agree with those people that seem to agree that having to create a potential large array on the stack, which usually has only little space available, isn't good. The argument is, if you know the size beforehand, you can use a static array. And if you don't know the size beforehand, you will write unsafe code.

C99 VLAs could provide a small benefit of being able to create small arrays without wasting space or calling constructors for unused elements, but they will introduce rather large changes to the type system (you need to be able to specify types depending on runtime values - this does not yet exist in current C++, except for new operator type-specifiers, but they are treated specially, so that the runtime-ness doesn't escape the scope of the new operator).

You can use std::vector, but it is not quite the same, as it uses dynamic memory, and making it use one's own stack-allocator isn't exactly easy (alignment is an issue, too). It also doesn't solve the same problem, because a vector is a resizable container, whereas VLAs are fixed-size. The C++ Dynamic Array proposal is intended to introduce a library based solution, as alternative to a language based VLA. However, it's not going to be part of C++0x, as far as I know.

Immensurable answered 11/12, 2009 at 10:28 Comment(20)
+1 and accepted. One comment though, I think the safety argument is a little bit weak since there are so many other ways to cause stack overflows. The safety argument could be used to support the position that you should never use recursion and that you should allocate all objects from the heap.Convince
So you're saying that because there are other ways to cause stack overflows, we might as well encourage more of them?Cyanogen
@Andreas, agreed about the weakness. But for recursion, it takes a huge number of calls until stack is eaten up, and if that can be the case, people would use iteration. As some people on the usenet thread say, though, this is not an argument against VLAs in all cases, since sometimes you definitely may know an upper bound. But in those cases, from what i see a static array can equally be sufficient, since it would not waste much space anyway (if it would, then you would actually have to ask whether the stack area is large enough again).Immensurable
Also look at Matt Austern's answer in that thread: The language specification of VLAs would probably considerably more complex for C++, because of the stricter type matches in C++ (example: C allows assigning a T(*)[] to a T(*)[N] - in C++ this is not allowed, since C++ does not know about "type compatibility" - it requires exact matches), type parameters, exceptions, con- and destructors and stuffs. I'm not sure whether the benefits of VLAs would really pay off all that work. But then, i have never used VLAs in real life, so i probably don't know good use cases for them.Immensurable
@Johannes: there are probably some cases where use of a VLA overcomes your objections. For instance a recursive function, at each step i requires N_i amount of space. The only upper limit I have, S, is on the sum of all N_i, not any individual N_i. So I might know that (a) S is small enough for my stack, so VLA's won't overflow, but (b) S * the max depth of recursion is too big for my stack. Of course this doesn't justify a tricky language feature: I should just allocate S on the stack at the top of the recursion, and pass down a pointer to what's left unused.Mcauley
Ah, I now see that Kaz Kylheku already gave that example on the com.std.c++ thread. The Windows string example given by Alf afterwards actually seems wrong to me, in that for my taste it puts too much responsibility on the caller of a narrow string function, to ensure that there is enough stack left for the library to use a VLA. VLA's don't do what you actually want in that kind of string-conversion situation, which is to allocate on the stack if there's space and from the heap if there isn't. But I guess on Windows, there's "room" on the stack iff there is on the heap, pretty much.Mcauley
@Johannes: "benefit of being able to create small arrays without wasting space or calling constructors for not used elements". A boost::array-like container which takes a runtime parameter for how many element to initialize of the allocation would be excactly as useful (except for maybe in recursive algorithms). Any runtime sized array ought to have a max number of elements, and allocating this amount of stack space immediately will give you your un-avoidable crash sooner, which is good.Torp
@Viktor, indeed. But a maximally sized array will also call all constructors, which might not be what you want. I think the array-like container is a good compromise between changing the language to introduce VLAs (which i believe is a large change), and having no support at all.Immensurable
@Johannes: No, what I mean is; if the array is a wrapper around a primitive type, (ie unsigned char[N * sizeof(T)]) the array can call the placement new/constructor for the number of elements in the runtime size variable. (if N denotes the static maximum size, T the type and n the runtime size, your constructor does something like for(i...n) {new (this + sizof(T)*i) T();}Torp
@Viktor, i agree. Such a thing would be useful, and you could provide N as a template parameter as the maximal count of items. And writing it will be easier in C++0x with the various alignment tools available (alignof, etc).Immensurable
The main reason I want VLAs is that they serve very well for evaluating polynomials of arbitrary degree--I need the speed of stack allocation, and the value for n will not be very high--and it's usually 2-4, so setting an arbitrary maximum of 20 feels wasteful. I currently do this with a messy template solution, but VLAs would fit much more cleanly.Hormonal
@AHelps: Perhaps what would be best for that would be a type that behaves somewhat like vector but requires a fixed LIFO usage pattern and maintains one or more per-thread statically-allocated buffers which are generally sized according to the largest total allocation the thread has ever used, but which could be explicitly trimmed. A normal "allocation" would in the common case require nothing more than a pointer copy, pointer-from-pointer subtraction, integer comparison, and pointer addition; de-allocation would simply require a pointer copy. Not much slower than a VLA.Scudo
BTW, in C++ I recommend folly's or boost's small_vector (github.com/facebook/folly/blob/master/folly/docs/…). Folly's version allows you to also forbid it to use the heap. That solves the issues with unneeded constructor calls with a "large enough stack array" approach.Immensurable
@AndreasBrinck Well said. Isn't c++' way that let programmers decide what to use and use it safely.Diatomic
@JohannesSchaub-litb https://mcmap.net/q/17196/-why-aren-39-t-variable-length-arrays-part-of-the-c-standard/11862989 can you please answer to this Question. it's related to this only but in context of dynamic array.Snooker
Strange argument, to claim that not knowing the size beforehand means unsafe code.Scrod
@Scrod what it was meant to say is that if you don't know an upper bound.Immensurable
I think a utility that statically allocated a fixed amount of memory on the stack, and only called the constructor for some of the elements, would not be unsafe of course.Immensurable
"And if you don't know the size beforehand, you will write unsafe code" I don't buy that argument. Because... how is std::something foo [constant_expression]; any safer to declare at local scope? I don't know the size of standard containers beforehand either. At least in case of application logic VLA you have control of the size yourself. With C++ standard container classes, you rely on the library implementation making a reasonably compact class and hoping that some other library vendor made it somewhat similar sized, so that C++ code can be ported between compilers without stack overflows.Zimmer
VLA is not more unsafer than having a big stack object inside a if condition on a recursive call; Should we remove such possibilities just because they are unsafe? This is the hypocrisy of the C++ standards commitee; They did everything they have to make sure designated initializers not to be in standard in such a long time. So I do not trust them when they say either: difficult to implement or compile time (same with modules) or there are loopholes or it is unsafe... All they did resulted cripping the progress and it should not be tolerated anymore,Wow
K
37

You could always use alloca() to allocate memory on the stack at runtime, if you wished:

void foo (int n)
{
    int *values = (int *)alloca(sizeof(int) * n);
}

Being allocated on the stack implies that it will automatically be freed when the stack unwinds.

Quick note: As mentioned in the Mac OS X man page for alloca(3), "The alloca() function is machine and compiler dependent; its use is dis-couraged." Just so you know.

Kathrinkathrine answered 11/12, 2009 at 10:31 Comment(4)
Also, the scope for alloca() is the entire function, not just the block of code containing the variable. So using it inside of a loop it will continuously increase the stack. A VLA does not have this problem.Emaciated
However, VLAs having the scope of the enclosing block means they are significantly less useful than alloca() with the scope of the entire function. Consider: if (!p) { p = alloca(strlen(foo)+1); strcpy(p, foo); } This cannot be done with VLAs, precisely because of their block scope.Impecunious
That does not answer OP's why question. Moreover, this is a C-like solution, and not really C++-ish.Pol
not equivalent. alloca has dirty syntax.Profligate
G
27

In my own work, I've realized that every time I've wanted something like variable-length automatic arrays or alloca(), I didn't really care that the memory was physically located on the cpu stack, just that it came from some stack allocator that didn't incur slow trips to the general heap. So I have a per-thread object that owns some memory from which it can push/pop variable sized buffers. On some platforms I allow this to grow via mmu. Other platforms have a fixed size (usually accompanied by a fixed size cpu stack as well because no mmu). One platform I work with (a handheld game console) has precious little cpu stack anyway because it resides in scarce, fast memory.

I'm not saying that pushing variable-sized buffers onto the cpu stack is never needed. Honestly I was surprised back when I discovered this wasn't standard, as it certainly seems like the concept fits into the language well enough. For me though, the requirements "variable size" and "must be physically located on the cpu stack" have never come up together. It's been about speed, so I made my own sort of "parallel stack for data buffers".

Guava answered 21/3, 2013 at 17:5 Comment(2)
This has the downside of having to manage that stack manually, but it's generally a very good approach.Frunze
I've thought about this too... are you saying that you effectively have a thread_local std::pmr::unsynchronized_pool_resource; that you can get at from anywhere when you want thread-local scratch space?Being
T
17

Seems it will be available in C++14:

https://en.wikipedia.org/wiki/C%2B%2B14#Runtime-sized_one_dimensional_arrays

Update: It did not make it into C++14.

Torp answered 13/8, 2013 at 10:40 Comment(6)
interesting. Herb Sutter discusses it here under Dynamic Arrays: isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meeting (this is the reference for the wikipedia information)Cayla
"Run-time sized arrays and dynarray have been moved to the Array Extensions technical specification" wrote 78.86.152.103 on Wikipedia on 18 January 2014‎: en.wikipedia.org/w/…Biased
Wikipedia isn't a normative reference :) This proposal did not make it into C++14.Shaw
@ViktorSehr: What's the status of this w.r.t. C++17?Woolpack
@Woolpack No idea, use boost::container::static_vectorTorp
@this: No: the Arrays TS was killed in 2016.Recency
M
16

There are situations where allocating heap memory is very expensive compared to the operations performed. An example is matrix math. If you work with smallish matrices say 5 to 10 elements and do a lot of arithmetics the malloc overhead will be really significant. At the same time making the size a compile time constant does seem very wasteful and inflexible.

I think that C++ is so unsafe in itself that the argument to "try to not add more unsafe features" is not very strong. On the other hand, as C++ is arguably the most runtime efficient programming language features which makes it more so are always useful: People who write performance critical programs will to a large extent use C++, and they need as much performance as possible. Moving stuff from heap to stack is one such possibility. Reducing the number of heap blocks is another. Allowing VLAs as object members would one way to achieve this. I'm working on such a suggestion. It is a bit complicated to implement, admittedly, but it seems quite doable.

Marvamarve answered 22/1, 2011 at 19:33 Comment(0)
S
12

This was considered for inclusion in C++/1x, but was dropped (this is a correction to what I said earlier).

It would be less useful in C++ anyway since we already have std::vector to fill this role.

Schoolhouse answered 11/12, 2009 at 10:26 Comment(8)
No, we don't, std::vector doesn't allocate data on the stack. :)Neoptolemus
@M.M: Fair enough, but in practice we still can't use std::vector instead of, say, alloca().Woolpack
@Woolpack in terms of getting correct output for your program , you can. Performance is a quality-of-implementation issueShaw
@Shaw quality-of-implementation is not portable. and if you don't need performance, you don't use c++ in the first placeFivespot
How do you handle multidimensions with vectors without tedious multiplications. C++ only gives excuses to exclude useful tools while they lie about "we need to allow people to write things we don't imagine". If this was useless why all these languages support it: en.wikipedia.org/wiki/Variable-length_array even C# added it and yes it is called stackalloc....Wow
@Neoptolemus std::pmr::vector can allocate on the stack.Being
@Being With a pre allocated compile-time sized memory resource on the stack (still same problem), virtual function call overhead and reallocation/relocation on resizing. No thanks...Togoland
Fair enough. Here's how it can work: godbolt.org/z/YWrj449or (inspired by codingtidbit.com/2020/05/25/…)Being
T
3

VLAs are a part of a larger family of Variably Modified types. This family of types is very special because they have runtime components.

The code:

int A[n];

Is seen by compiler as:

typedef int T[n];
T A;

Note that the runtime size of array is not bound to the variable A but to the type of the variable.

Nothing prevents one from making new variables of this type:

T B,C,D;

or the pointers or arrays

T *p, Z[10];

Moreover, pointers allow one to create VLAs with dynamic storage.

T *p = malloc(sizeof(T));
...
free(p);

What dispels a popular myth that VLAs can only be allocated on stack.

Back to the question.

This runtime component does not work well with type deduction which is one of the bases with C++ typing system. It would not possible to use templates, deduction and overloading.

C++ typing system is static, all types must be fully defined or deduced during compilation. VM types are completed only during program execution. Additional complexity introducing VM types to already hellishly complex C++ was simply considered unjustified. Mainly because their main practical application are automatic VLAs (int A[n];) which have an alternative in form of std::vector.

It a bit sad because VM types provides very elegant and efficient solutions to programs handling multidimensional arrays.

In C one can simply write:

void foo(int n, int A[n][n][n]) {
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
      for (int k = 0; k < n; ++k)
        A[i][j][k] = i * j * k;
}

...

int A[5][5][5], B[10][10][10];
foo(5, A);
foo(10, B);

Now try to provide as efficient and elegant solution in C++.

Torhert answered 16/9, 2021 at 22:12 Comment(0)
N
1

Use std::vector for this. For example:

std::vector<int> values;
values.resize(n);

The memory will be allocated on the heap, but this holds only a small performance drawback. Furthermore, it is wise not to allocate large datablocks on the stack, as it is rather limited in size.

Nauru answered 11/12, 2009 at 10:22 Comment(5)
A major application for variable length arrays is evaluation of arbitrary degree polynomials. In that case, your "small performance drawback" means "the code runs five times slower in typical cases." That's not small.Hormonal
Why don't you simply use std::vector<int> values(n);? By using resize after construction you are prohibiting non-moveable types.Edwyna
not equivalent. dirty syntax.Profligate
@Hormonal In my experience 99% of people who want VLA are not doing it because they want anything to do with arbitrary degree polynomials, but rather because they want a storage, but do not know what value to provide until user enters something. In this case this solution is the best there is.Adrien
@Adrien Yes, but it means that people with my use case (which includes things like physics engines, font rendering, CAD, computer graphics, and math libraries) are unable to write maximally efficient code. Perhaps those kinds of applications really are only 1% of people. Should we find a better language than C++, perhaps?Hormonal
J
1

Arrays like this are part of C99, but not part of standard C++. as others have said, a vector is always a much better solution, which is probably why variable sized arrays are not in the C++ standatrd (or in the proposed C++0x standard).

BTW, for questions on "why" the C++ standard is the way it is, the moderated Usenet newsgroup comp.std.c++ is the place to go to.

Jamshid answered 11/12, 2009 at 10:25 Comment(2)
-1 Vector is not always better. Often, yes. Always, no. If you only need a small array, are on a platform where heap space is slow, and your library's implementation of vector uses heap space, then this feature might very well be better if it existed.Pierce
@PatrickM: FWIW, std::vector with the default allocator must always allocate with dynamic storage duration, because with automatic storage duration it is impossible to meet the postconditions of move and swap. (Note that when a custom allocator is activated, move and swap are allowed to fail when the allocators aren't the same object, so then this particular problem goes away)Tull

© 2022 - 2024 — McMap. All rights reserved.