3

The compilers for C++ may behave differently. Since an array in C++ can be declared using the following methods:

Method A:

int size;
cout << "Enter size of array: ";
cin >> size;

int x[size];  
for (int i = 0; i < size; i++)
{
    //cout << "x[" << i << "] = ";
    x[i] = i + 1;
}  

Method B

int size;
cout << "Enter size of array: ";
cin >> size;

int *x = new int[size];  
for (int i = 0; i < size; i++)
{
    //cout << "x[" << i << "] = ";
    x[i] = i + 1;
}

Both are working fine by taking input from user at run-time. I know, using method B, we have to delete x like delete [] x.

  • Why to use int *x = new int[size];, if both are serving the same purpose?
  • What is the benefit of using new?

Following is the code snippet I'm testing:

#include<iostream>
using namespace std;

int main()
{
    int size;
    cout << "Enter size of array: ";
    cin >> size;

    //int *x = new int[size];
    int x[size];

    for (int i = 0; i < size; i++)
    {
        //cout << "x[" << i << "] = ";
        x[i] = i + 1;
    }

    cout << endl << "Display" << endl;
    for (int i = 0; i < size; i++)
    {
        cout << "x[" << i << "] = " << x[i] << endl;
    }  
    return 0;  
}
Malikx
  • 65
  • 1
  • 9
  • The first one won't work. You need to have the array size defined *at compile time*. Not at run-time. That's basically the main use difference. – The Quantum Physicist Feb 19 '18 at 12:36
  • 9
    "Method A" is invalid. C++ doesn't have [variable-length arrays](https://en.wikipedia.org/wiki/Variable-length_array). Whenever you think "dynamic array" your next thought should *always* be [`std::vector`](http://en.cppreference.com/w/cpp/container/vector). – Some programmer dude Feb 19 '18 at 12:36
  • @Someprogrammerdude but I can run both methods without any warnings and errors using DEVC++ – Malikx Feb 19 '18 at 12:38
  • 1
    @Malikx Then you should definitely enable more warnings. – Biffen Feb 19 '18 at 12:39
  • @TheQuantumPhysicist I also with the same conception, but I just tried Method A, it is working fine, I got astonished that why is it so? – Malikx Feb 19 '18 at 12:39
  • 1
    Method A is a compiler extensions supported by *some* compilers - it is not standard C++ – UnholySheep Feb 19 '18 at 12:40
  • 1
    @Malikx - A construct being valid C++ and a compiler accepting it aren't one and the same. Compilers may implement language extensions or have bugs in them. – StoryTeller - Unslander Monica Feb 19 '18 at 12:40
  • Oh god, I knew that the first one doesn't work fine, but now I'm using GCC, which allows it, and I was a bit shocked. – Nikita Smirnov Feb 19 '18 at 12:41
  • @Malikx Your compiler is probably too friendly, but based on the standard, Method A shouldn't work and shouldn't even compile. This could easily lead to a "stack overflow" error. – The Quantum Physicist Feb 19 '18 at 12:42
  • @GoverNator same here, now how to interpret it? I'm not getting it why is it happening? – Malikx Feb 19 '18 at 12:45
  • 4
    Because aren't compiling with the `-pedantic` flag... – StoryTeller - Unslander Monica Feb 19 '18 at 12:47
  • 1
    Possible duplicate of [Why aren't variable-length arrays part of the C++ standard?](https://stackoverflow.com/questions/1887097/why-arent-variable-length-arrays-part-of-the-c-standard) –  Feb 19 '18 at 12:53

2 Answers2

4

C++ standard does not define method A, but it is allowed in ISO C99 and GCC supports it in C++ mode as well. From https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html:

Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C90 mode and in C++. These arrays are declared like any other automatic arrays, but with a length that is not a constant expression. The storage is allocated at the point of declaration and deallocated when the block scope containing the declaration exits.

I've just found some related discussion here: Variable Length Arrays in C++14?

Thinkeye
  • 888
  • 12
  • 22
3

The simple answer is: Stack space is limited. You can expect to be able to allocate 1GiB on the heap, but you cannot expect to be able to allocate the same amount on the stack.

The first variant int x[size]; uses the stack. As such, you may find that your program crashes with a segfault if size is large. The allocation happens by simply decrementing the stack pointer register of your CPU. If you decrement it too much, the kernel will just view your next access to stack memory as an out-of-bounds access, and kill your process. It is not possible to detect this condition before actually being shot, or to recover from it in an orderly fashion. The kernel just shoots you without so much as a warning.

The second variant int* x = new int[size]; uses the heap. As such, you can expect the allocation to succeed as long as there is enough free RAM available to back the allocation. The operator new() will explicitly ask the kernel for the memory in an orderly fashion, and the kernel will either comply or signal unavailability of so much memory back in an orderly fashion. In the case of an error, the operator new() will proceed to throw an exception which you may catch and handle however you like.


Aside:
If your kernel is overcommitting its memory (like any modern Linux), you are not guaranteed to get an exception in case of an error. The kernel will just reply "ok" when operator new() asks it for the memory without actually providing any, and may later find that it cannot fulfill its promises. When that happens, it will invoke the OOM-killer (Out-Of-Memory killer), which shoots some processes based on some heuristic. So, never expect not to be shot for excessive memory consumption.

This is actually an optimization to be able to actually use all the available memory: For one thing, many programs do not actually use all the memory they allocate (like allocating a large buffer, but only using parts of it). For another, it is impossible to know which of the COW (Copy-On-Write) mapped pages will actually need copying. The kernel has no idea how much memory will be needed in the future, it only knows whether it currently has enough memory.


TL;DR:
Use int x[size]; only when you can prove that size will never exceed a small max value. If size is on the order of 10 to 100 (and the type itself is not insanely large), go for it. Use int* x = new[size]; in all other cases.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106