Подтвердить что ты не робот

Как распределить 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()?

4b9b3361

Ответ 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));