Как динамически выделять непрерывный блок памяти для 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 является постоянным выражением (но тогда вы должны обновить свой компилятор).