Объявление массива int

Есть ли разница между этими двумя объявлениями?

int x[10];

против.

int* x = new int[10];

Я предполагаю, что прежнее объявление (как и последнее) является объявлением указателя, и обе переменные можно рассматривать одинаково. Означает ли это, что они по сути одинаковы?

Ответ 1

#include<iostream>    

int y[10];


void doSomething()
{
    int x[10];
    int *z  = new int[10];
    //Do something interesting

    delete []z;
}

int main()
{
    doSomething();

}

int x[10]; 

- создает массив целых чисел размера 10 в стеке.
 - Вам не нужно явно удалять эту память, потому что она уходит, когда стек распаковывается.
 - Его область действия ограничена функцией doSomething()

int y[10];

- Создает массив целых чисел размера 10 в сегменте BSS/Data.
 - Вам не нужно явно удалять эту память.
 - Поскольку он объявлен global, он доступен по всему миру.

int *z = new int[10];

- выделяет динамический массив из 10 целых чисел в куче и возвращает адрес этой памяти в z.
 - Вы должны явно удалить эту динамическую память после ее использования. с помощью:

delete[] z;

Ответ 2

Единственное, что похоже на

int x[10];

и

int* x = new int[10];

заключается в том, что либо можно использовать в некоторых контекстах, где ожидается a int*:

int* b = x;   // Either form of x will work

void foo(int* p) {}

foo(x);      // Either form will work

Однако они не могут использоваться во всех контекстах, где ожидается int*. В частности,

delete [] x;  // UB for the first case, necessary for the second case.

Некоторые из основных отличий были объяснены в других ответах. Другие основные отличия:

Разница 1

sizeof(x) == sizeof(int)*10   // First case

sizeof(x) == sizeof(int*)     // Second case.

Разница 2

Тип &x равен int (*)[10] в первом случае

Тип &x равен int** во втором случае

Разница 3

Данная функция

void foo(int (&arr)[10]) { }

вы можете вызвать его, используя первый x, а не второй x.

foo(x);     // OK for first case, not OK for second case.

Ответ 3

Первый представляет собой массив int размера 10. Говорить, что его созданный на стек ошибочен. Потому что стандарт не гарантирует этого. Его реализация определена. Его продолжительность хранения может быть статической или автоматической в ​​зависимости от того, является ли x глобальной переменной или локальной переменной.

Во втором, вы создаете указатель типа int*. Не обязательно созданный на куче, стандарт не говорит об этом. Выделенная память охватывает более 10 * sizeof(int) байтов. Для этого вы должны освободить память самостоятельно, написав:

delete [] x; 

В этом случае память указателя x динамически распределяется и динамически освобождается, поэтому такие объекты имеют динамическую память.

Ответ 4

В соответствии со стандартом мы должны фактически различать три разных типа объявлений массива:

int x[10];

void method() {
     int y[10];
     int *z = new int[10];
     delete z;
}

В первом объявлении int x[10] используется продолжительность хранения статическая, определенная cppreference как: "Хранилище объекта выделяется, когда программа запускается и освобождается при завершении программы. Существует только один экземпляр объекта. Все объекты, объявленные в области пространства имен (включая глобальное пространство имен), имеют такую ​​продолжительность хранения, а также те, которые объявлены с помощью static или extern."

Вторая, int y[10], использует автоматическую продолжительность хранения, определенную cppreference как: "Объект выделяется в начале закрывающего кодового блока и освобождается в конце. Все локальные объекты имеют такую ​​продолжительность хранения, за исключением объявленных статических, extern или thread_local."

Третий, int *z = new int[10], обычно называется динамическим распределением памяти, и на самом деле представляет собой двухэтапную последовательность:

  • Сначала вызывается оператор new, который динамически выделяет память, используя либо стандартные методы распределения стандартной библиотеки, либо пользовательскую реализацию (поскольку новый может быть переопределен во время выполнения). Выделенная память будет достаточной, чтобы соответствовать выделенным N элементам, плюс любая дополнительная память, необходимая для хранения метаданных для данного распределения (чтобы впоследствии можно было успешно освободить).
  • Во-вторых, если первый шаг будет успешным, мы перейдем к инициализации или построению каждого объекта в массиве.

Как уже упоминалось в других комментариях, эти типы декларации имеют свои тонкие различия, но наиболее распространенными являются:

  • В большинстве современных операционных систем:

    • Обычно в стеке обычно выделяется
    • автоматическое хранилище, которое (вообще говоря) представляет собой выделенное в потоке пространство памяти с использованием механизма LIFO В хранилище
    • static используется предварительно выделенное пространство памяти, зарезервированное внутри исполняемого файла (более конкретно. BSS и .DATA сегменты, в зависимости от того, является ли переменная нулевой инициализирована или не)
    • динамическая память распределяется с использованием памяти кучи и подчиняется милости системы управления ОЗУ системы и других механизмов, таких как пейджинг.
  • Динамически выделенная память должна быть явно delete -ий, запрограммированной программистом, тогда как статические и автоматические переменные хранилища занимаются "средой"

  • Статические и автоматические переменные хранилища ограничены определенной областью, тогда как динамически распределенная память не имеет границ, то есть переменная, объявленная в одном модуле, может быть передана любому другому модулю, который работает в том же адресном пространстве

  • При распределении массива с использованием new[] размер может быть 0

  • (Как уже указывалось @R Sahu) Типы &x и &z различны:

    • &x int (*)[10]
    • &z int **

Ответ 5

Объявления полностью разные.

Первый случай,

int x[10];

объявляет x как массив 10 integer, тогда как второй случай

int* x = new int[10];

объявляет x как указатель на int - переменную со значением, равным адресу int, и инициализирует этот указатель на результат нового выражения (new int [10]), который динамически выделяет массив из десяти целых чисел.

Не соглашаясь с различиями, они могут использоваться аналогичным образом;

  • Синтаксис массива
  • (например, x[i], где i является интегральным значением между 0 и 9 включительно), может использоваться для установки или получения значений соответствующих массивов в приведенном выше синтаксисе;
  • арифметика указателя может использоваться для получения адреса элемента массива (например, x + i эквивалентно &x[i] для i между 0 и 10 включительно. [да, возможно получить адрес "один за конец";
  • Выделение указателя и доступ к массиву эквивалентны. т.е. *(x+i) и x[i] эквивалентны для i между 0 и 9 [разыменование указателя "один за конец" дает поведение undefined].

Однако есть также некоторые ключевые отличия:

Результаты оператора sizeof. sizeof(x) дает разные значения в двух случаях.

  • В первом случае sizeof(x) == sizeof(int)*10. sizeof(int) дает байт, определенный реализацией, но sizeof(x)/sizeof(*x) всегда будет указывать количество элементов в массиве (т.е. a std::size_t со значением 10).
  • Во втором, sizeof(x) == sizeof(int *) - это значение, определяемое реализацией. Значение sizeof(x)/sizeof(*x) практически маловероятно, чтобы получить значение 10. Это означает, что этот метод не может быть использован для получения количества элементов.

Lifetime.

  • В первом случае время жизни x зависит от области, в которой происходит объявление. Если декларация происходит в области файла (то есть в блоке компиляции, вне любого функционального блока), то x имеет статическую продолжительность хранения (так что существует до тех пор, пока программа запущена). Если объявление происходит в блоке, x - и все его элементы перестают существовать, когда заканчивается блок. Например

    {
        int x[10];
    
    }    //  x and all its elements cease to exist here
    
  • Во втором случае только указатель x имеет время жизни, зависящее от объема. Динамически распределенная память (результат new x[10]) никогда не освобождается. Это означает, что время жизни x и время жизни (динамически выделенного) массива, которое он ссылается, развязаны, что приводит нас к третьему различию.....

Результат присваивания Массив нельзя переназначить, указатель может (если только соответствующий const не квалифицирован).

Рассмотрим контекст

 // x as previously defined in one or the other form

 int y[10];
 int z;

 x = y;
 x = &z;

В первом случае оба назначения приводят к диагностике компилятора - присвоения недействительны. Во втором случае присвоения действительны и вызывают x для указания на адрес (первый элемент) y и на адрес z соответственно. Если значение x не сохраняется в другом указателе перед переназначением, то память, выделенная новым выражением (new int [10]), просочилась - она ​​больше не доступна программе, но не освобождается.

Ответ 6

Первый случай: x создается в сегменте стек/данных на основе того, является ли он нестатической локальной переменной или статической/глобальной переменной. И адрес x не поддается изменению.

Второй случай: 'x' - это указатель, указывающий на массив, созданный вообще в куче (свободный магазин). Вы можете изменить x на другое. Кроме того, вам нужно позаботиться об освобождении его, используя delete[] x;

Ответ 7

Они одинаковы, поскольку оба x указывают на первый адрес памяти в массиве из 10 целых чисел, однако очень отличаются тем, что

int x[10] 

объявляет память в статической памяти произвольного доступа, а ключевое слово "новый" создает их динамически с кучей и примерно такое же, как использование malloc в c для динамического создания массива.

Не только это, но (я считаю, не испытал теорию) есть вероятность, что:

int* x = new int[10];

может выйти из строя и в зависимости от компилятора может вернуть ошибку или нулевой указатель. Если компилятор С++ придерживается стандартов ANSI/ISO, то он поддерживает форму "no-throw" new, которая возвращает null, если распределение не выполняется, а не бросает исключение.

Другая разница в том, что "новый" оператор может быть перегружен.

Однако я не уверен в том, что либо один (в С++) создает нуль-завершенный массив. Я знаю, что в c, в компиляторе, который я использую, по крайней мере, вы должны всегда добавлять \0 к любым строкам или массивам, если вы ожидаете, что сможете перебирать их без чрезмерного расширения границ.

Просто моя $0,02.:)