Понимание порядка в новом [] - PullRequest
3 голосов
/ 08 января 2020

Я понимаю, что использование new[]: new <type>[<size>]. Теперь предположим, что я хотел бы выделить матрицу с количеством столбцов nCols, известных во время компиляции. С точки зрения вышеупомянутого использования, type является int[nCols]. Итак, я бы подумал написать:

const int nCols = 5;
int nRows;
cin >> nRows;
int (*matrix)[nCols] = new (int[nCols]) [nRows];

Как получается, что на самом деле правильный способ написания new int[nRows][nCols]?

Ответы [ 2 ]

3 голосов
/ 08 января 2020

Как получается, что правильный способ написания на самом деле новый int[nRows][nCols]?

Проще говоря, потому что вы можете заключать скобки в выражения (1 + 1 и * 1007) * оба действительны и имеют одинаковую оценку), но вы не можете ставить круглые скобки вокруг произвольных типов (int является допустимым типом, но (int) - нет).

В скобках внутри имени типа всегда есть функция semanti c (например, объявление указателя на функцию), они не просто группируют. cppreference имеет пример , иллюстрирующий это:

new int(*[10])(); // error: parsed as (new int) (*[10]) ()
new (int (*[10])()); // okay: allocates an array of 10 pointers to functions

Кроме того, синтаксис для записи объявлений типов (унаследованных от C) работает в спирали, направленные наружу по часовой стрелке . Обратите внимание, что переменная, для которой вы хотите выделить хранилище, объявлена ​​как

int (*matrix)[nCols]

Переменная является частью innermost . И, наконец, указатель access в C (и C ++) отражает указатель объявление . Следовательно, выражение new[] отражает синтаксис объявления, и, поскольку вы хотите выделить массивы nRow stati c, количество выделяемых элементов сбрасывается в позицию объявления указателя ((*matrix)).


Я советую против написания такого кода на C ++. Прежде всего, используйте constexpr вместо const здесь, хотя в данном конкретном случае голый const остается действительным.

Но, что более важно, вы почти (?) Никогда не захотите использовать new. Вместо того, чтобы вручную выделять массив, используйте std::vector:

std::vector<int[nCols]> matrix(nRows);
// or:
std::vector<std::array<int, nCols>> matrix(nRows);
2 голосов
/ 08 января 2020

Это должно соответствовать обычному объявлению массива и индексации массива.

Примечание T array[M][N]; также объявляет массив M массивов, каждый с размером N, точно так же, как то, что создано new T[M][N].

Теперь подумайте о выражении a[i][j], где a это либо массив M массивов, каждый с размером N, либо указатель на первый элемент массива в таком массиве :

extern int a[M][N];
// OR
extern int b[M][N];
int (*a)[N] = b;
// OR
int (*a)[N] = new int[M][N];

Чтобы оценить a[i][j], мы сначала применяем индекс i, затем индекс j. Это имеет смысл только в порядке их появления, плюс выражение анализируется как (a[i])[j]. Подвыражение a[i] будет i -ым подмассивом типа int[N], поэтому i может варьироваться от 0 до M-1. Как только мы получим этот подмассив, действительные индексы j будут от 0 до N-1. Таким образом, чтобы порядок индексов соответствовал объявлению массива или синтаксису new, язык выбирает их так, чтобы измерение «верхнего уровня» было первым: T[M][N] означает «массив из M массивов N объекты типа T ".

Да, это означает, что при использовании псевдонима типа может быть немного удивительно: если ArrT равно T[X], то ArrT[Y] равно T[Y][X]. Но псевдонимы типов определенно не работают как текстовые замены в любом случае (для другого примера, если Ptr равно int*, const Ptr равно int *const, а не const int*).

...