Подтвердить что ты не робот

Различия между новыми char [n] и новыми (char [n])

Есть ли разница между new char[n] и new (char[n])?

У меня есть второй случай в сгенерированном коде, g++ (4.8.0) дает мне

ISO C++ does not support variable-length array types [-Wvla]

Это заставляет меня думать, являются ли эти два одинаковыми или нет.

  • new char[n] означает "выделить n объекты типа char.
  • означает new (char[n]) означает "выделить 1 объект типа array of n chars"?
  • Удаление первого ясно.
  • Я должен удалить второй с помощью delete или delete[]?
  • Есть ли какие-либо другие различия, о которых я должен знать?
  • Могу ли я безопасно удалить круглые скобки и перевести второй случай в первый, когда другие части программного обеспечения ожидают второй?

Код генерируется сторонним программным обеспечением (и используется другими частями программного обеспечения), поэтому я не могу просто "использовать вектор вместо".

Это минимальный пример:

int main (void)
{
    int n(10);
    int *arr = new (int[n]); // removing parentheses fixes warning
    *arr = 0; // no "unused variable" warning
    return 0;
}
4b9b3361

Ответ 1

Основная проблема здесь в том, что С++ не разрешает использовать массив [n] в типе, если n не является постоянным выражением. g++ и некоторые другие компиляторы иногда позволят это, но невозможно добиться последовательного поведения при запуске смешивания массивов переменной длины и шаблонов.

Явное исключение int* p = new int[n]; работает, потому что здесь [n] является синтаксически частью выражения new, а не частью типа, предоставленного new, а new выполняет "know how" для создания массивов с длиной, определенной во время выполнения.

// can be "constexpr" in C++11:
const int C = 12;

int main() {
    int* p1 = new int[C];
    int* p2 = new (int[C]);
    typedef int arrtype[C];
    int* p3 = new arrtype;

    int n = 10;
    int* p4 = new int[n];
    // int* p5 = new (int[n]);  // Illegal!
    // typedef int arrtype2[n]; // Illegal!
    // int* p6 = new arrtype2;

    delete[] p1;
    delete[] p2;
    delete[] p3;
    delete[] p4;
}

Семантически, однако, после того, как какой-либо окончательный [C] используется для преобразования типа в тип массива, новое выражение только заботится о том, имеет ли он дело с массивом или нет. Все требования о типе выражения, использовать ли new[] и delete[] и т.д., Говорят о таких вещах, как "когда выделенным типом является массив", а не "когда используется новый синтаксис массива". Таким образом, в приведенном выше примере инициализации p1, p2 и p3 эквивалентны, и во всех случаях delete[] является правильной формой освобождения.

Инициализация p4 действительна, но код для p5 и p6 неверен С++. g++ позволял бы им в любом случае, если не использовать -pedantic, и по аналогии ожидаю, что инициализации для p4, p5 и p6 также будут эквивалентными. Разборка @MM поддерживает этот вывод.

Итак, да, это должно быть безопасное улучшение, чтобы удалить "лишние" круглые скобки из этого выражения. Правильное удаление - это тип delete[].

Ответ 2

  • new T[N] создает массив элементов N типа T.

  • new (T[N]) создает один объект типа T[N].

Эффект тот же (и оба выражения дают T *, который указывает на первый элемент массива и его нужно удалить с помощью delete[] (см. 5.3.4/5), но ясно T[N] должен быть допустимым типом в последнем случае, поэтому N должно быть константным выражением, в то время как в первом случае это динамический аргумент нового выражения array.

Ответ 3

  new int [n]
  //Allocates memory for `n` x `sizeof(int)` and returns
  //the pointer which points to the beginning of it. 

 +-----+-----+-----+-----+-----+-----+-----+-----+------+------+
 |     |     |     |     |     |     |     |     |      |      |
 |     |     |     |     |     |     |     |     |      |      |
 |     |     |     |     |     |     |     |     |      |      |
 +-----+-----+-----+-----+-----+-----+-----+-----+------+------+


  new (int [n])
  //Allocate a (int[n]), a square which its item is an array
+----------------------------------------------------------------+
|+-----+-----+-----+-----+-----+-----+-----+-----+------+------+ |
||     |     |     |     |     |     |     |     |      |      | |
||     |     |     |     |     |     |     |     |      |      | |
||     |     |     |     |     |     |     |     |      |      | |
|+-----+-----+-----+-----+-----+-----+-----+-----+------+------+ |
+----------------------------------------------------------------+

Фактически оба они равны.

 

Вот код, сгенерированный ассемблером (просто незначащее различие):

int n = 10;
int *i = new (int[n]);
int *j = new int[n];

i[1] = 123;
j[1] = 123;

----------------------------------

!    int *i = new (int[n]);
main()+22: mov    0x1c(%esp),%eax
main()+26: sub    $0x1,%eax
main()+29: add    $0x1,%eax
main()+32: shl    $0x2,%eax
main()+35: mov    %eax,(%esp)
main()+38: call   0x401620 <_Znaj> // void* operator new[](unsigned int);
main()+43: mov    %eax,0x18(%esp)
!    int *j = new int[n];
main()+47: mov    0x1c(%esp),%eax
main()+51: shl    $0x2,%eax
main()+54: mov    %eax,(%esp)
main()+57: call   0x401620 <_Znaj> // void* operator new[](unsigned int);
main()+62: mov    %eax,0x14(%esp)
!    
!    i[1] = 123;
main()+66: mov    0x18(%esp),%eax
main()+70: add    $0x4,%eax
main()+73: movl   $0x7b,(%eax)
!    j[1] = 123;
main()+79: mov    0x14(%esp),%eax
main()+83: add    $0x4,%eax
main()+86: movl   $0x7b,(%eax)

Вы должны delete оба из них delete [] ...