Как динамически выделять непрерывный блок памяти для 2D-массива
Если я выделяю 2D-массив, такой как int a [N] [N]; он будет выделять непрерывный блок памяти.
Но если я попытаюсь сделать это динамически следующим образом:
int **a = malloc(rows * sizeof(int*));
for(int i = 0; i < rows; i++)
a[i] = malloc(cols * sizeof(int));
Это поддерживает единичный шаг между элементами в строках, но это может быть не так между строк.
Одним из решений является преобразование из 2D в 1D, кроме того, есть ли другой способ сделать это?
Ответы
Ответ 1
Если размеры вашего массива известны во время компиляции:
#define ROWS ...
#define COLS ...
int (*arr)[COLS] = malloc(sizeof *arr * ROWS);
if (arr)
{
// do stuff with arr[i][j]
free(arr);
}
Если ваши размеры массива неизвестны во время компиляции, и вы используете компилятор C99 или компилятор C2011, который поддерживает массивы переменной длины:
size_t rows, cols;
// assign rows and cols
int (*arr)[cols] = malloc(sizeof *arr * rows);
if (arr)
{
// do stuff with arr[i][j]
free(arr);
}
Если ваши размеры массива неизвестны во время компиляции, и вы не используете компилятор C99 или компилятор C2011, который поддерживает массивы переменной длины:
size_t rows, cols;
// assign rows and cols
int *arr = malloc(sizeof *arr * rows * cols);
{
// do stuff with arr[i * rows + j]
free(arr);
}
Ответ 2
На самом деле, n-мерные массивы (выделенные в стеке) на самом деле являются только одномерными векторами. Множественное индексирование - это просто синтаксический сахар. Но вы можете написать функцию доступа, чтобы эмулировать что-то вроде того, что вы хотите:
int index_array(int *arr, size_t width, int x, int y)
{
return arr[x * width + y];
}
const size_t width = 3;
const size_t height = 2;
int *arr = malloc(width * height * sizeof(*arr));
// ... fill it with values, then access it:
int arr_1_1 = index_array(arr, width, 1, 1);
Однако, если у вас есть поддержка C99, тогда объявление указателя на массив возможно, и вы даже можете использовать синтаксический сахар:
int (*arr)[width] = malloc(sizeof((*arr) * height);
arr[x][y] = 42;
Ответ 3
Предположим, вы хотите динамически выделить 2-мерный целочисленный массив строк ROWS и столбцов COLS. Затем вы можете сначала выделить непрерывный кусок целых чисел ROWS * COLS, а затем вручную разбить его на строки ROWS. Без синтаксического сахара это читает
int *mem = malloc(ROWS * COLS * sizeof(int));
int **A = malloc(ROWS * sizeof(int*));
for(int i = 0; i < ROWS; i++)
A[i] = mem + COLS*i;
// use A[i][j]
и может быть сделано более эффективно, избегая умножения,
int *mem = malloc(ROWS * COLS * sizeof(int));
int **A = malloc(ROWS * sizeof(int*));
A[0] = mem;
for(int i = 1; i < ROWS; i++)
A[i] = A[i-1] + COLS;
// use A[i][j]
Наконец, можно вообще отказаться от дополнительного указателя,
int **A = malloc(ROWS * sizeof(int*));
A[0] = malloc(ROWS * COLS * sizeof(int));
for(int i = 1; i < ROWS; i++)
A[i] = A[i-1] + COLS;
// use A[i][j]
но там важная GOTCHA! Вы должны быть осторожны, чтобы сначала освободить A [0], а затем A,
free(A[0]);
free(A); // if this were done first, then A[0] would be invalidated
Та же идея может быть расширена до трехмерных или более массивных массивов, хотя код будет беспорядочным.
Ответ 4
Вы можете обрабатывать динамически выделенную память как массив любого измерения, обращаясь к ней по шагам:
int * a = malloc(sizeof(int) * N1 * N2 * N3); // think "int[N1][N2][N3]"
a[i * N2 * N3 + j * N3 + k] = 10; // like "a[i, j, k]"
Ответ 5
Извините, что у меня нет форматирования или каких-либо ошибок, но это сотовый телефон.
Я также столкнулся с шагами, где я пытался использовать fwrite() для вывода с использованием переменной int ** в качестве адреса src.
Одним из решений было использование двух выписок malloc():
#define HEIGHT 16
#define WIDTH 16
.
.
.
//allocate
int** data = malloc(HEIGHT*sizeof(int**));
int* realdata = malloc(HEIGHT*WIDTH*sizeof(int));
//manually index
for(int i = 0;i<HEIGHT;i++)
data[i]=&realdata[i*WIDTH];
//populate
int idx = 0;
for(int i=0; i < HEIGHT; i++)
for(int j=0; j < WIDTH; j++)
data[i][j]=idx++;
//select
int idx=0;
for(int i=0; i < HEIGHT; i++){
for(int j= 0; j < WIDTH; j++)
printf("%i, ", data[i][j]);
printf("/n");
}
//deallocate
.
.
.
Ответ 6
Вы можете напечатать ваш массив (для меньшего потока), а затем сделать что-то вроде этого:
#include <stdlib.h>
#define N 10
typedef int A[N][N];
int main () {
A a; // on the stack
a[0][0]=1;
A *b=(A*)malloc (sizeof(A)); // on the heap
(*b)[0][0]=1;
}
Ответ 7
Лучший способ - выделить указатель на массив,
int (*a)[cols] = malloc(rows * sizeof *a);
if (a == NULL) {
// alloc failure, handle or exit
}
for(int i = 0; i < rows; ++i) {
for(int j = 0; j < cols; ++j) {
a[i][j] = i+j;
}
}
Если компилятор не поддерживает массивы переменной длины, это работает только в том случае, если cols
является постоянным выражением (но тогда вы должны обновить свой компилятор).