В чем разница между int * x [n] [m] и int (* x) [n] [m]?

Как я вижу, int *x[n][m] объявляет x как 2-мерный массив указателей на целые числа, поэтому выделение памяти должно быть таким же простым, как x[i][j] = new int, и, как и ожидалось, оно отлично работает. Теперь, если я изменил объявление на:

int (*x)[n][m]

x[i][j] = new int больше не работает и приводит к ошибке компиляции.

x = (int(*)[n][m]) malloc (sizeof(int[n][m])) однако компилируется. Из нескольких тестов, которые я выполнил, после выделения памяти различные комбинации объявления/распределения, похоже, не влияют на значения, хранящиеся в переменной. Я что-то упускаю? Итак, мой вопрос: Есть ли разница между int * x [n] [m] и int (* x) [m] [n]. Как int (* x) [n] [m] хранится в памяти?

Ответ 1

int *a[n][m] - это двумерный массив указателей на int.

int (*p)[n][m] является указателем на двумерный массив int (это тип, который вы получаете, принимая адрес int[n][m]).

В обоих случаях n и m должны быть компилированы константы времени, иначе объявления не являются законными в С++ (но находятся на C). Однако ваш компилятор может иметь расширение, чтобы его разрешить.

Сначала можно использовать для моделирования трехмерного массива. Я говорю симуляцию, потому что это не будет подходящий массив со смежным хранилищем, и типы будут отличаться в первую очередь. В каждом из элементов a вы можете сохранить адрес в первый элемент массива целых чисел. Каждый из них может иметь разный размер и распределяться динамически. Вы также можете сохранить указатель на одно (возможно, выделенное стеком) целое число.

int i = 0;
int a1[2] = {};

int* a2[2][2];
a2[0][0] = a1;  // array to pointer decay here
a2[0][1] = new int[42];
a2[1][0] = new int[84];
a2[1][1] = &i;

p может указывать на один массив 2d или его массив:

int arr[2][3];
int (*p1)[2][3] = &arr;  // arr decays to int(*)[3], so we need to take the address
int (*p2)[2][3] = new int[42][2][3]; // allocate 42 arrays dynamically

Ответ 2

Как вы можете легко обнаружить:

  • int * x [n] [m] - двумерный массив указателей на int.
  • int (* x) [n] [m] - указатель на двумерный массив из целых чисел.

Ответ на ваш вопрос заключается в том, что первым является массив, поэтому память является "встроенной" - она ​​может быть статической, автоматической (в стеке) или в куче, в зависимости от того, где вы ее определяете.

Второй - это указатель на массив, и указатель должен быть инициализирован до того, как он будет использоваться. Скорее всего, память будет выделена в куче, но также возможно, что это может быть статический или автоматический массив, определенный в другом месте. Если вы получаете доступ к элементам массива перед инициализацией указателя, вы получаете Undefined Behavior.

Ответ 3

Хороший сайт для декодирования имен объявления C. Принимая ваши примеры (и подставляя n=1 и m=2), мы видим:

int *x[1][2]

Переведено на:

объявить x как массив 1 из массива 2 указателя на int

В то время как

int (*x)[1][2]

Переведено на:

объявить x как указатель на массив 1 массива 2 из int