Как распределить 2-мерный массив с помощью оператора One malloc
В интервью мне задали вопрос, как я могу выделить двухмерный массив, и ниже было мое решение.
#include <stdlib.h>
int **array;
array = malloc(nrows * sizeof(int *));
for(i = 0; i < nrows; i++)
{
array[i] = malloc(ncolumns * sizeof(int));
if(array[i] == NULL)
{
fprintf(stderr, "out of memory\n");
exit or return
}
}
Я думал, что проделал хорошую работу, но затем он попросил меня сделать это, используя один оператор malloc()
не два. Я не знаю, как это достичь.
Может кто-нибудь предложить мне какую-нибудь идею сделать это в одиночном malloc()
?
Ответы
Ответ 1
Просто вычислите общий объем памяти, необходимый как для указателей строк nrows
, так и фактические данные, добавьте все это и выполните один вызов:
int **array = malloc(nrows * sizeof *array + (nrows * (ncolumns * sizeof **array));
Если вы считаете, что это выглядит слишком сложно, вы можете разбить его и сделать его немного самодокументированным, назвав разные выражения выражения размера:
int **array; /* Declare this first so we can use it with sizeof. */
const size_t row_pointers_bytes = nrows * sizeof *array;
const size_t row_elements_bytes = ncolumns * sizeof **array;
array = malloc(row_pointers_bytes + nrows * row_elements_bytes);
Затем вам нужно пройти и инициализировать указатели строк, чтобы каждый указатель строки указывал на первый элемент для этой конкретной строки:
size_t i;
int * const data = array + nrows;
for(i = 0; i < nrows; i++)
array[i] = data + i * ncolumns;
Обратите внимание, что результирующая структура тонко отличается от того, что вы получаете, если вы это сделаете, например. int array[nrows][ncolumns]
, потому что у нас есть явные указатели строк, что означает, что для массива, выделенного таким образом, нет реального требования, чтобы все строки имели одинаковое количество столбцов.
Это также означает, что доступ вроде array[2][3]
делает что-то отличное от аналогичного доступа к фактическому 2d-массиву. В этом случае сначала происходит самый внутренний доступ, а array[2]
считывает указатель из третьего элемента в array
. Этот указатель затем обрабатывается как основа массива (столбца), в который мы индексируем, чтобы получить четвертый элемент.
Напротив, для чего-то вроде
int array2[4][3];
который является "упакованным" собственным 2d-массивом, занимающим всего 12 целых чисел, доступ, такой как array[3][2]
, просто разбивается на добавление смещения к базовому адресу, чтобы получить элемент.
Ответ 2
int **array = malloc (nrows * sizeof(int *) + (nrows * (ncolumns * sizeof(int)));
Это работает, потому что в C массивы - это всего лишь все элементы один за другим как куча байтов. Метаданных нет. malloc() не знает, будет ли он выделяться для использования в качестве символов, int или строк в массиве.
Затем вы должны инициализировать:
int *offs = &array[nrows]; /* same as int *offs = array + nrows; */
for (i = 0; i < nrows; i++, offs += ncolumns) {
array[i] = offs;
}
Ответ 3
Здесь другой подход.
Если вы знаете количество столбцов во время компиляции, вы можете сделать что-то вроде этого:
#define COLS ... // integer value > 0
...
size_t rows;
int (*arr)[COLS];
... // get number of rows
arr = malloc(sizeof *arr * rows);
if (arr)
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < COLS; j++)
arr[i][j] = ...;
}
Если вы работаете на C99, вы можете использовать указатель на VLA:
size_t rows, cols;
... // get rows and cols
int (*arr)[cols] = malloc(sizeof *arr * rows);
if (arr)
{
size_t i, j;
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
arr[i][j] = ...;
}
Ответ 4
Вы должны иметь возможность сделать это с помощью (немного уродливым со всеми кастами):
int** array;
size_t pitch, ptrs, i;
char* base;
pitch = rows * sizeof(int);
ptrs = sizeof(int*) * rows;
array = (int**)malloc((columns * pitch) + ptrs);
base = (char*)array + ptrs;
for(i = 0; i < rows; i++)
{
array[i] = (int*)(base + (pitch * i));
}
Ответ 5
Я не поклонник этого "массива указателей на массив" для решения парадигмы многомерного массива. Всегда предпочитал один размерный массив, доступ к элементу с массивом [row * cols + col]? Нет проблем с инкапсулированием всего в классе и внедрением метода "at".
Если вы настаиваете на доступе к элементам массива с помощью этих обозначений: Matrix [i] [j], вы можете сделать небольшую магию С++. Решение @John пытается сделать это таким образом, но он требует, чтобы число столбцов было известно во время компиляции. С некоторыми С++ и переопределением оператора [] вы можете получить это полностью:
class Row
{
private:
int* _p;
public:
Row( int* p ) { _p = p; }
int& operator[](int col) { return _p[col]; }
};
class Matrix
{
private:
int* _p;
int _cols;
public:
Matrix( int rows, int cols ) { _cols=cols; _p = (int*)malloc(rows*cols ); }
Row operator[](int row) { return _p + row*_cols; }
};
Итак, теперь вы можете использовать объект Matrix, например, для создания таблицы умножения:
Matrix mtrx(rows, cols);
for( i=0; i<rows; ++i ) {
for( j=0; j<rows; ++j ) {
mtrx[i][j] = i*j;
}
}
Теперь вы должны убедиться, что оптимизатор работает правильно, и нет функции вызова или каких-либо других накладных расходов. Конструктор не вызывается. Пока вы не перемещаете матрицу между функциями, даже переменная _cols не создается. Утверждение mtrx [i] [j] в основном делает mtrx [i * cols + j].
Ответ 6
Как распределить 2-мерный массив, используя команду One malloc (?)
Пока нет ответов, чтобы выделить память для истинного 2D-массива.
int **array
- указатель на указатель на int. array
не является указателем на 2D-массив.
int a[2][3]
является примером истинного двумерного массива или массива 2 массива 3 из int
Чтобы выделить память для истинного 2D-массива с помощью C99, используйте malloc()
и сохраните указатель на массив переменной длины (VLA)
// Simply allocate and initialize in one line of code
int (*c)[nrows][ncolumns] = malloc(sizeof *c);
if (c == NULL) {
fprintf(stderr, "out of memory\n");
return;
}
// Use c
(*c)[1][2] = rand();
...
free(c);
Без поддержки VLA, если размеры являются константами, код может использовать
#define NROW 4
#define NCOL 5
int (*d)[NROW][NCOL] = malloc(sizeof *d);
Ответ 7
Вы можете выделить (row*column) * sizeof(int)
байт памяти с помощью malloc.
Вот фрагмент кода для демонстрации.
int row = 3, col = 4;
int *arr = (int *)malloc(row * col * sizeof(int));
int i, j, count = 0;
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
*(arr + i*col + j) = ++count; //row major memory layout
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
printf("%d ", *(arr + i*col + j));