Как правильно настроить, получить доступ и получить multidimensional array в C?
Я видел десятки вопросов о том, «что не так с моим кодом» в отношении многомерных массивов в C. По какой-то причине люди не могут склонить голову вокруг того, что здесь происходит, поэтому я решил ответить на этот вопрос как ссылку на других :
Как правильно настроить, получить доступ и получить multidimensional array в C?
Если у других есть полезные советы, пожалуйста, не стесняйтесь писать!
- Функция для динамического выделения матрицы
- Динамически строить вызов для поиска нескольких столбцов
- Как динамически создавать столбцы в DataGrid WPF?
- Динамическое преобразование параметров в вариативные функции
- Создание DataTemplate в коде
- Изменение цвета ячейки datagridview динамически
- Angular2 - динамически загружать компонент из модуля
- Создание динамического количества элементов ввода с помощью R / Shiny
- R: rvest: соскабливание динамической страницы электронной торговли
- Динамический диапазон диаграмм с помощью INDIRECT: эта функция недействительна (несмотря на выделение диапазона)
- Тестирование типа утки с помощью C # 4 для динамических объектов
- как создать анимированный gif в .net
- Динамические имена переменных в Bash
В 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);
Существует как минимум четыре разных способа создания или моделирования многомерного массива на C89.
Один из них «выделяет каждую строку отдельно», описанную Майком в его ответе. Это не multidimensional array, он просто имитирует один (в частности, он имитирует синтаксис для доступа к элементу). Это может быть полезно в случае, когда каждая строка имеет разный размер, поэтому вы не представляете матрицу, а скорее что-то с «оборванным краем».
Один из них – «выделить multidimensional array». Кажется, это нравится:
int (*rows)[NUM_ROWS][NUM_COLS] = malloc(sizeof *rows); ... free(rows);
Тогда синтаксис для доступа к элементу [i, j] равен (*rows)[i][j]
. В C89, как NUM_COLS
и NUM_ROWS
должны быть известны во время компиляции. Это настоящий 2-мерный массив, а 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-мерным массивом. На практике он имеет тот же макет, что и один.
Статически говоря , это легко понять:
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
Одна из самых важных вещей, которые нужно сделать сейчас, это убедиться, что мы освобождаем память, когда мы закончили. Каждый уровень malloc()
должен иметь одинаковое количество free()
вызовов, а вызовы должны быть в порядке FILO (обратном вызове malloc):
for(i=0; i
Если вы хотите использовать массив typedef’d, это еще проще.
Предположим, что у вас в вашем коде typedef int LabeledAdjMatrix[SIZE][SIZE];
Затем вы можете использовать:
LabeledAdjMatrix *pMatrix = malloc(sizeof(LabeledAdjMatrix));
Тогда вы можете написать:
for (i=0; i
Поскольку pArr
является указателем на вашу матрицу и *
имеет более низкий приоритет, чем []
;
Вот почему обычная идиома режима - это typedef строка:
typedef int LabeledAdjRow[SIZE];
Тогда вы можете написать:
LabeledAdjRow *pMatrix = malloc(sizeof(LabeledAdjRow) * SIZE); for (i=0; i
И вы можете memcpy
все, что непосредственно:
LabeledAdjRow *pOther = malloc(sizeof(LabeledAdjRow) * SIZE); memcpy(pOther, pMatrix, sizeof(LabeledAdjRow) * SIZE);