Статический и динамический массив C++

Компиляторы для C++ могут вести себя по-разному. Поскольку массив в C++ можно объявить следующими методами:

Метод А:

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;
}  

Метод Б

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;
}

Оба работают нормально, получая данные от пользователя во время во время выполнения. Я знаю, что используя метод B, мы должны удалить x, например delete [] x.

  • Зачем использовать int *x = new int[size];, если оба служат одной цели?
  • В чем преимущество использования new?

Ниже приведен фрагмент кода, который я тестирую:

#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;  
}

person Malikx    schedule 19.02.2018    source источник
comment
Первый не сработает. Вам необходимо определить размер массива во время компиляции. Не во время выполнения. Это в основном основное различие использования.   -  person The Quantum Physicist    schedule 19.02.2018
comment
Метод А недействителен. В C++ нет массивов переменной длины. Всякий раз, когда вы думаете о динамическом массиве, вашей следующей мыслью всегда должна быть std::vector< /а>.   -  person Some programmer dude    schedule 19.02.2018
comment
@Someprogrammerdude, но я могу запускать оба метода без каких-либо предупреждений и ошибок, используя DEVC++   -  person Malikx    schedule 19.02.2018
comment
@Malikx Тогда вам обязательно нужно включить больше предупреждений.   -  person Biffen    schedule 19.02.2018
comment
@TheQuantumPhysicist У меня тоже такая же концепция, но я только что попробовал метод А, он работает нормально, я был удивлен, почему это так?   -  person Malikx    schedule 19.02.2018
comment
Метод A — это расширение компилятора, поддерживаемое некоторыми компиляторами — это не стандарт C++.   -  person UnholySheep    schedule 19.02.2018
comment
@Malikx - допустимая конструкция C++ и компилятор, принимающий ее, не являются одним и тем же. Компиляторы могут реализовывать языковые расширения или содержать в них ошибки.   -  person StoryTeller - Unslander Monica    schedule 19.02.2018
comment
О боже, я знал, что первый не работает нормально, но теперь я использую GCC, который позволяет это, и я был немного шокирован.   -  person Nikita Smirnov    schedule 19.02.2018
comment
@Malikx Ваш компилятор, вероятно, слишком дружелюбен, но в соответствии со стандартом метод A не должен работать и даже не должен компилироваться. Это может легко привести к ошибке переполнения стека.   -  person The Quantum Physicist    schedule 19.02.2018
comment
@GoverNator то же самое, теперь как это интерпретировать? Я не понимаю, почему это происходит?   -  person Malikx    schedule 19.02.2018
comment
Потому что не компилируются с флагом -pedantic...   -  person StoryTeller - Unslander Monica    schedule 19.02.2018


Ответы (2)


Простой ответ: Пространство в стеке ограничено. Вы можете ожидать, что сможете выделить 1 ГБ в куче, но не можете ожидать, что сможете выделить такой же объем в стеке.

Первый вариант int x[size]; использует стек. Таким образом, вы можете обнаружить, что ваша программа вылетает из-за segfault, если size велико. Распределение происходит путем простого уменьшения регистра указателя стека вашего процессора. Если вы уменьшите его слишком сильно, ядро ​​​​просто рассмотрит ваш следующий доступ к памяти стека как доступ за пределы и уничтожит ваш процесс. Невозможно обнаружить это состояние до того, как вас действительно застрелят, или вылечиться от него упорядоченным образом. Ядро просто стреляет в вас без предупреждения.

Второй вариант int* x = new int[size]; использует кучу. Таким образом, вы можете ожидать, что выделение будет выполнено успешно, пока имеется достаточно свободной оперативной памяти для резервного копирования. operator new() будет явно запрашивать память у ядра упорядоченным образом, и ядро ​​либо подчинится, либо сообщит о недоступности такого большого объема памяти упорядоченным образом. В случае ошибки operator new() продолжит генерировать исключение, которое вы можете поймать и обработать по своему усмотрению.


Кроме того:
если ваше ядро ​​чрезмерно выделяет свою память (как любой современный Linux), вы не гарантированно получите исключение в случае ошибки. Ядро просто ответит "хорошо", когда operator new() запрашивает у него память, фактически не предоставляя ее, и позже может обнаружить, что не может выполнить свои обещания. Когда это произойдет, он вызовет OOM-убийцу (убийцу нехватки памяти), который стреляет в некоторые процессы на основе некоторой эвристики. Так что никогда не ожидайте, что вас не расстреляют за чрезмерное потребление памяти.

На самом деле это оптимизация, позволяющая фактически использовать всю доступную память: во-первых, многие программы на самом деле не используют всю выделяемую ими память (например, выделяют большой буфер, а используют только его части). Во-вторых, невозможно узнать, какая из сопоставленных страниц COW (Copy-On-Write) действительно нуждается в копировании. Ядро не знает, сколько памяти потребуется в будущем, оно знает только, достаточно ли памяти в данный момент.


TL;DR:
Используйте int x[size]; только в том случае, если вы можете доказать, что size никогда не превысит небольшое максимальное значение. Если size находится в диапазоне от 10 до 100 (и сам тип не безумно большой), дерзайте. Используйте int* x = new[size]; во всех остальных случаях.

person cmaster - reinstate monica    schedule 19.02.2018

Стандарт C++ не определяет метод A, но он разрешен в ISO C99, а GCC поддерживает его и в режиме C++. Из https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html:

Автоматические массивы переменной длины разрешены в ISO C99, и в качестве расширения GCC принимает их в режиме C90 и в C++. Эти массивы объявляются так же, как любые другие автоматические массивы, но их длина не является константным выражением. Хранилище выделяется в момент объявления и освобождается, когда область блока, содержащая объявление, выходит.

Я только что нашел здесь связанное обсуждение: Массивы переменной длины в C++14?

person Thinkeye    schedule 19.02.2018
comment
Его можно успешно скомпилировать здесь: onlinegdb.com/online_c++_compiler - person Malikx; 19.02.2018