Как правильно настроить, получить доступ и получить multidimensional array в C?

Я видел десятки вопросов о том, «что не так с моим кодом» в отношении многомерных массивов в C. По какой-то причине люди не могут склонить голову вокруг того, что здесь происходит, поэтому я решил ответить на этот вопрос как ссылку на других :

Как правильно настроить, получить доступ и получить multidimensional array в C?

Если у других есть полезные советы, пожалуйста, не стесняйтесь писать!

В 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); 
  • Динамическая форма с повторяющейся формой
  • Заменить динамический контент в XML-файле
  • Добавить массив кнопок в GridView в приложении для Android
  • Запрос сводной таблицы MySQL с динамическими столбцами
  • Динамически добавлять текстовые элементы в линейный режим
  • Как десериализовать дочерний объект с динамическими (числовыми) именами клавиш?
  • Программно создавать и добавлять композитный компонент в бэк-файл
  • Android - динамически добавлять виды в представление
  • Автозаполнение jQuery для динамически создаваемых входов
  • Давайте будем гением компьютера.