Динамическое распределение массива массивов в C
Я действительно не понимаю некоторые основные вещи в C, как динамическое распределение массива массивов.
Я знаю, что вы можете сделать:
int **m;
чтобы объявить 2-мерный массив (который впоследствии будет выделен с помощью некоторой функции * alloc). Также можно "легко" получить доступ, выполнив *(*(m + line) + column)
. Но как я должен присвоить значение элементу из этого массива? С помощью gcc следующий оператор m[line][column] = 12;
выходит из строя с ошибкой сегментации.
Любые статьи/документы будут оценены.: -)
Ответы
Ответ 1
Синтаксис m[line][column] = 12
в порядке (при условии, что line
и column
находятся в диапазоне).
Однако вы не указали код, который вы используете для его выделения, поэтому трудно получить, является ли оно неправильным или правильным. Это должно быть что-то вроде
m = (int**)malloc(nlines * sizeof(int*));
for(i = 0; i < nlines; i++)
m[i] = (int*)malloc(ncolumns * sizeof(int));
Некоторые примечания:
- Таким образом, вы можете выделить каждую строку разной длины (например, треугольный массив)
- Вы можете повторно использовать() или бесплатно() отдельную строку позже, используя массив
- Вы должны освободить() каждую строку, когда вы освободите() весь массив
Ответ 2
Ваш синтаксис m [line] [colummn] верен. Но для того, чтобы использовать 2D-массив в C, вы должны выделить для него память. Например, этот код будет выделять память для таблицы данной строки и столбца.
int** AllocateArray(int line, int column) {
int** pArray = (int**)malloc(line*sizeof(int*));
for ( int i = 0; i < line; i++ ) {
pArray[i] = (int*)malloc(column*sizeof(int));
}
return pArray;
}
Обратите внимание, что я оставил проверку ошибок для malloc для краткости. Реальное решение должно включать их.
Ответ 3
Это не 2d-массив - это массив массивов - таким образом ему нужны множественные распределения.
Ответ 4
Здесь представлена модифицированная версия решения quinmars, которая выделяет только один блок памяти и может использоваться с общими значениями с помощью void *
:
#include <stdlib.h>
#include <string.h>
#include <assert.h>
void ** array2d(size_t rows, size_t cols, size_t value_size)
{
size_t index_size = sizeof(void *) * rows;
size_t store_size = value_size * rows * cols;
char * a = malloc(index_size + store_size);
if(!a) return NULL;
memset(a + index_size, 0, store_size);
for(size_t i = 0; i < rows; ++i)
((void **)a)[i] = a + index_size + i * cols * value_size;
return (void **)a;
}
int printf(const char *, ...);
int main()
{
int ** a = (int **)array2d(5, 5, sizeof(int));
assert(a);
a[4][3] = 42;
printf("%i\n", a[4][3]);
free(a);
return 0;
}
Я не уверен, действительно ли безопасно использовать void **
до int **
(я думаю, что стандарт позволяет конверсии иметь место при преобразовании в/из void *
?), но он работает в gcc. Чтобы быть в безопасности, вы должны заменить каждое появление void *
на int *
...
Следующие макросы реализуют безопасную версию предыдущего алгоритма:
#define alloc_array2d(TYPE, ROWS, COLS) \
calloc(sizeof(TYPE *) * ROWS + sizeof(TYPE) * ROWS * COLS, 1)
#define init_array2d(ARRAY, TYPE, ROWS, COLS) \
do { for(int i = 0; i < ROWS; ++i) \
ARRAY[i] = (TYPE *)(((char *)ARRAY) + sizeof(TYPE *) * ROWS + \
i * COLS * sizeof(TYPE)); } while(0)
Используйте их следующим образом:
int ** a = alloc_array2d(int, 5, 5);
init_array2d(a, int, 5, 5);
a[4][3] = 42;
Ответ 5
Хотя я согласен с другими ответами, в большинстве случаев лучше выделять весь массив сразу, потому что malloc довольно медленный.
int **
array_new(size_t rows, size_t cols)
{
int **array2d, **end, **cur;
int *array;
cur = array2d = malloc(rows * sizeof(int *));
if (!array2d)
return NULL;
array = malloc(rows * cols * sizeof(int));
if (!array)
{
free(array2d);
return NULL;
}
end = array2d + rows;
while (cur != end)
{
*cur = array;
array += cols;
cur++;
}
return array2d;
}
Чтобы освободить массив, просто выполните:
free(*array); free(array);
Примечание. Это решение работает только в том случае, если вы не хотите изменять порядок строк, потому что тогда вы можете потерять адрес первого элемента, который вам нужно освободить позже.
Ответ 6
Хамм. Как насчет старого модного дыма и зеркал в качестве опции?
#define ROWS 5
#define COLS 13
#define X(R, C) *(p + ((R) * ROWS) + (C))
int main(void)
{
int *p = (int *) malloc (ROWS * COLS * sizeof(int));
if (p != NULL)
{
size_t r;
size_t c;
for (r = 0; r < ROWS; r++)
{
for (c = 0; c < COLS; c++)
{
X(r,c) = r * c; /* put some silly value in that position */
}
}
/* Then show the contents of the array */
for (r = 0; r < ROWS; r++)
{
printf("%d ", r); /* Show the row number */
for (c = 0; c < COLS; c++)
{
printf("%d", X(r,c));
}
printf("\n");
}
free(p);
}
else
{
/* issue some silly error message */
}
return 0;
}
Ответ 7
Используя malloc(3)
для выделения первого массива и помещая туда указатели, созданные malloc(3)
, должны работать с array[r][c]
, потому что он должен быть эквивалентен *(*(array + r) + c)
, он находится в стандарте C.