10

I tried to use this version:

int n;
cin >> n;
int a[n]; // compiler error

But it doesn’t work. What am I doing wrong?

user70960
  • 326
  • 1
  • 15
westron
  • 119
  • 1
  • 7
  • 7
    Variable length arrays (VLAs for short) are not supported by standard C++. Consider using `std::vector` instead. – Algirdas Preidžius Apr 01 '18 at 09:33
  • 2
    Note that despite 'use `new`' is the first suggestion in all of the answers below, `std::vector` should be preferred as it's a lot more safe and convenient. – HolyBlackCat Apr 01 '18 at 12:10

4 Answers4

10

There are three standard conformant ways to declare an array with a size unknown at compile time. Presented from most recommended to least.

std::vector

The community's favorite container and for good reason. Not only can it be declared with a run-time size, but the size can be changed at any time. This facilitates use when size cannot be predetermined, eg when repeatedly polling for user input. Examples:

// Known size
size_t n;
std::cin >> n;
std::vector<int> vec(n);

// Unknown size
std::vector<int> vec;
int input;
while (std::cin >> input) { // Note: not always the best way to read input
    vec.push_back(in);
}

There's not much downside to using std::vector. The known size case requires exactly one dynamic allocation. The unknown size requires more in the general case, but you wouldn't be able to do any better anyway. So performance is more or less optimal.

Semantically, it might not be ideal for sizes that are constant throughout the execution. It might not be apparent to the reader that this container is not intended to change. It is not known to the compiler either so it will allow you to do something wrong like push_back into a vector that is logically of constant size.

std::unique_ptr (or std::shared_ptr)

The safest solution if enforcing static size is important to you.

size_t n;
std::cin >> n;
auto arr = std::make_unique<int[]>(n);

arr's size cannot change, though it can be made to release the current array and point to another one of different size. Therefore, if logically the size of your container is constant, this conveys intent in a clearer way. Unfortunately, it is also much weaker than std::vector even in the constant-size case. It is not size-aware, so you have to explicitly store the size. For the same reason it does not offer iterators and can't be used in range for loops. It is up to you (and the project in question) if you want to sacrifice these features to enforce static size.

new[] - delete[]

Technically a solution, but unless you are forced to use an old C++ standard or you are writing a low-level library that manages memory internally they are strictly worse than the std::unique_ptr or std::shared_ptr solution. They offer no more features, but are significantly less safe because you have to explicitly free the memory when you're done with it. Otherwise, you will leak it and this might cause significant problems. To make matters worse, using delete[] properly can be non-trivial for programs with complicated flows of execution and exception handling. Please don't use this when the above solutions are available to you!

size_t n;
std::cin >> n;
int* arr = new int[n];
...
// Control flow must reach exactly one corresponding delete[] !!!
delete[] arr;

Bonus: Compiler extension

Some compilers might actually be ok with the following code

size_t n;
std::cin >> n;
int arr[n];

Relying on this has severe drawbacks. Your code cannot be compiled on all C++ conformant compilers. It probably doesn't even compile on all versions of the given compiler. Also, I doubt that the produced executable checks the value of n and allocates on the heap when needed meaning you can blow up your stack. This solution only makes sense when you know the upper bound of n is small and when performance is so important to you that you're willing to rely on compiler-specific behavior to get it. These are truly exceptional cases.

patatahooligan
  • 2,642
  • 16
  • 28
  • 1
    Please post this answer to the older question this duplicates, since it's most extensive and has better explanation than the existing ones. – Ben Voigt Apr 01 '18 at 17:49
  • @BenVoigt Perhaps make that older Q a dupe of this one? – Matt Apr 01 '18 at 18:04
  • @BenVoigt I actually have left an answer there, but it is less complete than this one because for whatever reason the OP asked for a non-vector solution. Maybe they shouldn't be considered duplicates. I consider this one a more constructive question. – patatahooligan Apr 01 '18 at 18:16
  • I think explaining why the vector is the first choice, then offering another smart pointer, then also showing the bare pointer approach, is perfect for that question as well. – Ben Voigt Apr 01 '18 at 20:11
  • @BenVoigt I updated it for completeness' sake but I still believe that this is the question that should be open. It is more general (encompasses the other), more upvoted, and the answers are newer and more complete. – patatahooligan Apr 01 '18 at 21:44
5

You can allocate an array on the heap:

int n;
cin >> n;
int *a = new int[n];

// use 'a'

delete[] a;

You can also use an std::vector:

int n;
cin >> n;
vector<int> a(n);

You could use an array of arrays (a "matrix"):

int n, m;
cin >> n >> m;
int **a = new int*[n];
for(int i = 0; i < n; i++)
    a[i] = new int[m];

// use 'a'

for (int i = 0; i < n; i++)
    delete[] a[i];

delete[] a;

You could also have a vector of vectors.

2

Google: C++ dynamic arrays

int n;
cin >> n;
int *a = new int[n];

// use 'a'

delete[] a;
isanae
  • 3,007
  • 1
  • 19
  • 41
user70960
  • 326
  • 1
  • 15
  • 5
    If you use dynamic arrays, you also have to clean up at the end: delete[] a; otherwise the memory is still reserved – Thomas Apr 01 '18 at 09:47
  • Because you are creating the array onto the heap and not onto the stack... – Thomas Apr 01 '18 at 09:50
2

C++ has no variable-length arrays. Alternative is to using dynamic arrays as mentioned by @user70960:

int n(0);
cin >> n;
int *a(new int[n]);

// use 'a'

delete[] a;

Or you can perform the same using vectors as follows:

int n(0);
cin >> n;
vector<int> a(n);
isanae
  • 3,007
  • 1
  • 19
  • 41
EV_A
  • 98
  • 7