Как правильно настроить, получить доступ и получить многомерный массив в C?
Я видел десятки вопросов о том, "что не так с моим кодом" в отношении многомерных массивов в C. По какой-то причине люди не могут обернуться вокруг того, что здесь происходит, поэтому я решил ответить на этот вопрос как ссылку на другие:
Как правильно настроить, получить доступ и получить многомерный массив в C?
Если у других есть полезные советы, пожалуйста, не стесняйтесь публиковать сообщения!
Ответы
Ответ 1
В C с C99 даже динамические многомерные массивы можно легко выделить за один раз с помощью malloc
и освободить с помощью free
:
double (*A)[n] = malloc(sizeof(double[n][n]));
for (size_t i = 0; i < n; ++i)
for (size_t j = 0; j < n; ++j)
A[i][j] = someinvolvedfunction(i, j);
free(A);
Ответ 2
Существует как минимум четыре разных способа создания или моделирования многомерного массива на C89.
Один из них "выделяет каждую строку отдельно", описанную Майком в его ответе. Это не многомерный массив, он просто имитирует один (в частности, он имитирует синтаксис для доступа к элементу). Это может быть полезно в случае, когда каждая строка имеет разный размер, поэтому вы не представляете матрицу, а скорее нечто "оборванное".
Один означает "выделить многомерный массив". Кажется, это нравится:
int (*rows)[NUM_ROWS][NUM_COLS] = malloc(sizeof *rows);
...
free(rows);
Тогда синтаксис доступа к элементу [i, j] равен (*rows)[i][j]
. В C89, как NUM_COLS
, так и NUM_ROWS
должны быть известны во время компиляции. Это истинный 2-D массив, а rows
- указатель на него.
Один - это "выделить массив строк". Это выглядит так:
int (*rows)[NUM_COLS] = malloc(sizeof(*rows) * NUM_ROWS);
...
free(rows);
Тогда синтаксис для доступа к элементу [i, j] равен rows[i][j]
. В C89, NUM_COLS
должен быть известен во время компиляции. Это настоящий 2-мерный массив.
Один из них: "выделите 1-мерный массив и притворитесь". Это выглядит так:
int *matrix = malloc(sizeof(int) * NUM_COLS * NUM_ROWS);
...
free(matrix);
Тогда синтаксис для доступа к элементу [i, j] равен matrix[NUM_COLS * i + j]
. Это (конечно) не является истинным 2-мерным массивом. На практике он имеет тот же макет, что и один.
Ответ 3
Статически говоря, это легко понять:
int mtx[3][2] = {{1, 2},
{2, 3},
{3, 4}};
Ничего сложного здесь. 3 строки, 2 столбца; данные в колонке 1: 1, 2, 3
; данные в колонке 2: 2, 3, 4
.
Мы можем получить доступ к элементам через одну и ту же конструкцию:
for(i = 0; i<3; i++){
for(j = 0; j<2; j++)
printf("%d ", mtx[i][j]);
printf("\n");
}
//output
//1 2
//2 3
//3 4
Теперь рассмотрим это с помощью Указателей:
Скобки - очень хорошая конструкция, помогающая упростить вещи, но она не помогает, когда нам нужно работать в динамической среде, поэтому нам нужно думать об этом с точки зрения указателей. Если мы хотим сохранить "строку" целых чисел, нам нужен массив:
int row[2] = {1,2};
И знаешь что? Мы можем получить доступ к нему точно так же, как указатель.
printf("%d, %d\n",*row,*(row+1)); //prints 1, 2
printf("%d, %d\n",row[0],row[1]); //prints 1, 2
Теперь, если мы не знаем количество значений в строке, мы можем сделать этот массив динамической длиной, если у нас есть указатель на int, и мы даем ему некоторую память:
int *row = malloc(X * sizeof(int)); //allow for X number of ints
*row = 1; //row[0] = 1
*(row+1) = 2; //row[1] = 2
…
*(row+(X-1)) = Y; // row[x-1] = Some value y
Итак, теперь у нас есть динамический 1-мерный массив; один ряд. Но нам нужно много строк, а не только одно, и мы не знаем, сколько. Это означает, что нам нужен еще один динамический 1-мерный массив, каждый элемент которого будет указателем, указывающим на строку.
//we want enough memory to point to X number of rows
//each value stored there is a pointer to an integer
int ** matrix = malloc(X * sizeof(int *));
//conceptually:
(ptr to ptr to int) (pointer to int)
**matrix ------------> *row1 --------> [1][2]
*row2 --------> [2][3]
*row3 --------> [3][4]
Теперь все, что осталось сделать, это написать код, который будет выполнять эти динамические распределения:
int i, j, value = 0;
//allocate memory for the pointers to rows
int ** matrix = malloc(Rows * sizeof(int*));
//each row needs a dynamic number of elements
for(i=0; i<Rows; i++){
// so we need memory for the number of items in each row…
// we could call this number of columns as well
*(matrix + i) = malloc(X * sizeof(int));
//While we’re in here, if we have the items we can populate the matrix
for(j=0; j<X; j++)
*(*(matrix+i)+j) = value; // if you deference (matrix + i) you get the row
// if you add the column and deference again, you
// get the actual item to store (not a pointer!)
}
Одна из самых важных вещей, которые нужно сделать сейчас - убедиться, что мы освобождаем память, когда это было сделано. Каждый уровень malloc()
должен иметь одинаковое количество вызовов free()
, а вызовы должны быть в порядке FILO (обратном вызове malloc):
for(i=0; i<Rows; i++)
free(*(matrix + i));
free(matrix);
//set to NULL to clean up, matrix points to allocated memory now so let’s not use it!
matrix = NULL;
Ответ 4
Если вы хотите использовать массив typedef'd, это еще проще.
Скажите, что у вас есть код typedef int LabeledAdjMatrix[SIZE][SIZE];
Затем вы можете использовать:
LabeledAdjMatrix *pMatrix = malloc(sizeof(LabeledAdjMatrix));
Затем вы можете написать:
for (i=0; i<SIZE; i++) {
for (j=0; j<SIZE; j++) (*parr)[i][j] = k++; /* or parr[0][i][j]... */
}
Поскольку pArr
является указателем на вашу матрицу, а *
имеет более низкий приоритет, чем []
;
Вот почему обычная идиома режима - это typedef строку:
typedef int LabeledAdjRow[SIZE];
Затем вы можете написать:
LabeledAdjRow *pMatrix = malloc(sizeof(LabeledAdjRow) * SIZE);
for (i=0; i<SIZE; i++) {
for (j=0; j<SIZE; j++) parr[i][j] = k++;
}
И вы можете memcpy
все, что непосредственно:
LabeledAdjRow *pOther = malloc(sizeof(LabeledAdjRow) * SIZE);
memcpy(pOther, pMatrix, sizeof(LabeledAdjRow) * SIZE);
Ответ 5
Начиная с ответа Джен, если вы хотите выделить пространство для трехмерного массива, таким же образом, вы можете сделать
int(*A)[n][n] = malloc(sizeof(int[num_of_2D_arrays][n][n]));
for (size_t p = 0; p < num_of_2D_arrays; p++)
for (size_t i = 0; i < n; i++)
for (size_t j = 0; j < n; j++)
A[p][i][j] = p;
for (size_t p = 0; p < num_of_2D_arrays; p++)
printf("Outter set %lu\n", p);
for (size_t i = 0; i < n; i++)
for (size_t j = 0; j < n; j++)
printf(" %d", A[p][i][j]);
printf("\n");
free(A);