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

Как работать с динамическими многомерными массивами в C?

Кто-нибудь знает, как я могу использовать динамически распределенные многомерные массивы с помощью C? Возможно ли это?

4b9b3361

Ответ 1

С динамическим распределением, используя malloc:

int** x;

x = malloc(dimension1_max * sizeof(int*));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = malloc(dimension2_max * sizeof(int));
}

[...]

for (int i = 0; i < dimension1_max; i++) {
  free(x[i]);
}
free(x);

Здесь выделяется 2D-массив размером dimension1_max * dimension2_max. Например, если вы хотите массив размером 640 * 480 (fe пикселей изображения), используйте dimension1_max= 640, dimension2_max= 480. Затем вы можете получить доступ к массиву с помощью x[d1][d2], где d1= 0..639, d2= 0..479.

Но поиск в SO или Google также показывает другие возможности, например в этом вопросе SO

Обратите внимание, что ваш массив не будет выделять смежную область памяти (640 * 480 байтов) в этом случае, что может вызвать проблемы с функциями, которые предполагают это. Таким образом, чтобы массив удовлетворял условию, замените блок malloc выше на это:

int** x;
int* temp;

x = malloc(dimension1_max * sizeof(int*));
temp = malloc(dimension1_max * dimension2_max * sizeof(int));
for (int i = 0; i < dimension1_max; i++) {
  x[i] = temp + (i * dimension2_max);
}

[...]

free(temp);
free(x);

Ответ 2

Начиная с C99, C имеет двумерные массивы с динамическими границами. Если вы хотите избежать размещения таких зверей в стеке (что и следует делать), вы можете легко выделить их за один раз, как показано ниже.

double (*A)[n] = malloc(sizeof(double[n][n]));

и что это. Затем вы можете легко использовать его, как вы используете для 2D-массивов с чем-то вроде A[i][j]. И не забывайте, что один в конце

free(A);

Рэнди Мейерс написал ряд статей, объясняющих массивы переменной длины (VLA).

Ответ 3

Основы

Массивы в c объявляются и получают доступ с помощью оператора []. Так что

  int ary1 [5];
Код>

объявляет массив из 5 целых чисел. Элементы нумеруются с нуля, поэтому ary1 [0] - это первый элемент, а ary1 [4] - последний элемент. Примечание1: инициализация по умолчанию отсутствует, поэтому память, занимаемая массивом, может сначала содержать что-либо. Примечание2: ary1 [5] обращается к памяти в состоянии undefined (что может быть даже не доступно для вас), поэтому не делайте этого!

Многомерные массивы реализуются как массив массивов (массивов (из...)). Так

  float ary2 [3] [5];
Код>

объявляет массив из трех одномерных массивов из 5 чисел с плавающей запятой. Теперь ary2 [0] [0] - это первый элемент первого массива, ary2 [0] [4] - последний элемент первого массива, а ary2 [2] [4] - последний элемент последнего массива. Стандарт '89 требует, чтобы эти данные были смежными (раздел A8.6.2 на стр. 216 моего K & R 2nd ed. Ed.), Но, похоже, агностик для заполнения.

Попытка пойти динамически в более чем одном измерении

Если вы не знаете размер массива во время компиляции, вам нужно динамически выделить массив. Заманчиво попробовать

  double * buf3;
buf3 = malloc (3 * 5 * sizeof (double));
/* проверка ошибок идет здесь */
Код>

который должен работать, если компилятор не вставил выделение (добавьте лишнее пространство между одномерными массивами). Это может быть безопаснее:

  double * buf4;
buf4 = malloc (sizeof (double [3] [5]));
/* проверка ошибок */
Код>

но в любом случае трюк приходит к разыменованию. Вы не можете написать buf [i] [j], потому что buf имеет неправильный тип. Вы также не можете использовать

  double ** hdl4 = (double **) buf;
hdl4 [2] [3] = 0;/* Неправильно! */
Код>

потому что компилятор ожидает, что hdl4 будет адресом адреса double. Вы также не можете использовать double incomplete_ary4 [] [];, потому что это ошибка;

Итак, что вы можете сделать?

  • Сделайте арифметику строк и столбцов самостоятельно
  • Выделить и выполнить работу в функции
  • Используйте массив указателей (об этом говорит механизм qrdl)

Сделайте сами математику

Просто вычислите смещение памяти для каждого элемента следующим образом:

  for (i = 0; я < 3; ++ i) {    для (j = 0; j < 3; ++ j) {       buf3 [i * 5 + j] = someValue (i, j);/* Не нужно беспокоиться о                                            заполнение в этом случае */    } }
Код>

Выделить и выполнить работу в функции

Определите функцию, которая принимает необходимый размер в качестве аргумента и действует как обычный

  void dary (int x, int y) { double ary4 [x] [y]; ary4 [2] [3] = 5;
}
Код>

Конечно, в этом случае ary4 является локальной переменной, и вы не можете ее вернуть: вся работа с массивом должна выполняться в функции, которую вы вызываете в функциях, которые it.

Массив указателей

Рассмотрим это:

  double ** hdl5 = malloc (3 * sizeof (double *));
/* Проверка ошибок */
для (i = 0; я < 3; ++ i) {  hdl5 [i] = malloc (5 * sizeof (double))  /* Проверка ошибок */
}
Код>

Теперь hdl5 указывает на массив указателей, каждый из которых указывает на массив двойников. Классный бит состоит в том, что вы можете использовать двумерную нотацию массива для доступа к этой структуре --- hdl5 [0] [2] получает средний элемент первой строки --- но это не меньше-другой объект, чем двумерный массив, объявленный double ary [3] [5];.

Эта структура более гибкая, чем двухмерный массив (потому что строки не обязательно должны иметь одинаковую длину), но доступ к ней обычно будет медленнее и требует больше памяти (вам нужно место для хранения промежуточных указателей).

Обратите внимание, что, поскольку я не настроил никаких охранников, вам придется самостоятельно следить за размером всех массивов.

Арифметика

c не поддерживает векторную, матричную или тензорную математику, вам придется реализовать ее самостоятельно или принести библиотеку.

Умножение с помощью масштабирования и сложение и вычитание массивов одного ранга являются легкими: просто перебирайте элементы и выполняйте операцию по ходу. Внутренние продукты также прямолинейны.

Внешние продукты означают больше циклов.

Ответ 4

Если вы знаете количество столбцов во время компиляции, это довольно просто:

#define COLS ...
...
size_t rows;
// get number of rows
T (*ap)[COLS] = malloc(sizeof *ap * rows); // ap is a *pointer to an array* of T

Вы можете рассматривать ap как любой 2D-массив:

ap[i][j] = x;

Когда вы закончите, вы освободите его как

free(ap);

Если вы не знаете количество столбцов во время компиляции, но вы работаете с компилятором C99 или компилятором C2011, который поддерживает массивы переменной длины, все еще довольно просто:

size_t rows;
size_t cols;
// get rows and cols
T (*ap)[cols] = malloc(sizeof *ap * rows);
...
ap[i][j] = x;
...
free(ap);

Если вы не знаете количество столбцов во время компиляции и работаете с версией C, которая не поддерживает массивы переменной длины, вам нужно сделать что-то другое. Если вам нужны все элементы, которые будут выделены в непрерывном фрагменте (например, обычный массив), вы можете выделить память как 1D-массив и вычислить смещение 1D:

size_t rows, cols;
// get rows and columns
T *ap = malloc(sizeof *ap * rows * cols);
...
ap[i * rows + j] = x;
...
free(ap);

Если вам не нужно, чтобы память была непрерывной, вы можете следовать двухэтапному методу выделения:

size_t rows, cols;
// get rows and cols
T **ap = malloc(sizeof *ap * rows);
if (ap)
{
  size_t i = 0;
  for (i = 0; i < cols; i++)
  {
    ap[i] = malloc(sizeof *ap[i] * cols);
  }
}

ap[i][j] = x;

Поскольку распределение было двухэтапным процессом, освобождение также должно быть двухэтапным процессом:

for (i = 0; i < cols; i++)
  free(ap[i]);
free(ap);

Ответ 6

Вот рабочий код, который определяет подпрограмму make_3d_array для размещения многомерного 3D-массива с элементами N1, N2 и N3 в каждом измерении, а затем заполняет его случайными числами. Вы можете использовать обозначение A[i][j][k] для доступа к его элементам.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


// Method to allocate a 2D array of floats
float*** make_3d_array(int nx, int ny, int nz) {
    float*** arr;
    int i,j;

    arr = (float ***) malloc(nx*sizeof(float**));

    for (i = 0; i < nx; i++) {
        arr[i] = (float **) malloc(ny*sizeof(float*));

        for(j = 0; j < ny; j++) {
            arr[i][j] = (float *) malloc(nz * sizeof(float));
        }
    }

    return arr;
} 



int main(int argc, char *argv[])
{
    int i, j, k;
    size_t N1=10,N2=20,N3=5;

    // allocates 3D array
    float ***ran = make_3d_array(N1, N2, N3);

    // initialize pseudo-random number generator
    srand(time(NULL)); 

    // populates the array with random numbers
    for (i = 0; i < N1; i++){
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                ran[i][j][k] = ((float)rand()/(float)(RAND_MAX));
            }
        }
   }

    // prints values
    for (i=0; i<N1; i++) {
        for (j=0; j<N2; j++) {
            for (k=0; k<N3; k++) {
                printf("A[%d][%d][%d] = %f \n", i,j,k,ran[i][j][k]);
            }
        }
    }

    free(ran);
}

Ответ 7

//использовать новый вместо malloc, так как использование malloc приводит к утечкам памяти, введите здесь код

    int **adj_list = new int*[rowsize];       
    for(int i = 0; i < rowsize; ++i)    
    {

        adj_list[i] = new int[colsize];

    }

Ответ 8

Невозможно выделить все за один раз. Вместо этого создайте массив указателей, а затем для каждого указателя создайте для него память. Например:

int** array;
array = (int**)malloc(sizeof(int*) * 50);
for(int i = 0; i < 50; i++)
    array[i] = (int*)malloc(sizeof(int) * 50);

Конечно, вы также можете объявить массив как int* array[50] и пропустить первый malloc, но второй набор необходим для динамического выделения требуемого хранилища.

Можно взломать способ выделить его за один шаг, но для этого потребуется пользовательская функция поиска, но писать это так, чтобы она всегда работала, может раздражать. Примером может быть L(arr,x,y,max_x) arr[(y)*(max_x) + (x)], затем malloc - блок из 50 * 50 int или любой другой и доступ с использованием этого макроса L, например

#define L(arr,x,y,max_x) arr[(y)*(max_x) + (x)]

int dim_x = 50;
int dim_y = 50;

int* array = malloc(dim_x*dim_y*sizeof(int));

int foo = L(array, 4, 6, dim_x);

Но это гораздо более неприятно, если вы не знаете, что вы делаете с макросом препроцессора.

Ответ 9

int rows, columns;
/* initialize rows and columns to the desired value */

    arr = (int**)malloc(rows*sizeof(int*));
        for(i=0;i<rows;i++)
        {
            arr[i] = (int*)malloc(cols*sizeof(int));
        }

Ответ 10

Почему, коллеги, никто не обнаружил, что в сфере C нет точного решения? но только в С++ (точнее я имею в виду, что вышеупомянутые и другие решения сайта отличаются от настоящих C-многомерных массивов с: дополнительными объектами и, следовательно, дополнительной памятью и т.д.).

В С++ вы должны реализовать (всего несколько строк кода):

typedef double T;
class Matrix2D
{
public:
    Matrix2D(int, int);
   ~Matrix2D();
    T* operator[](int);
private:
    T* const  memory;
    const int rows;
    const int cols;
};

Matrix2D::Matrix2D(int r, int c) : rows(r), cols(c), memory(new T[r*c]) {}
Matrix2D::~Matrix2D() { delete[]  memory; }
T* Matrix2D::operator[](int row) {  return memory + cols*row;}

Это все для тех, кто использует такой код: a[i][j].

Но для точного сходства с C-многомерными массивами этот класс (Matrix2D) не имеет арифметики с двойным указателем, который будет использоваться следующим образом: (*(a+i))[j]. Это не сложно: реализовать внутренние clsss "doublePointer" с помощью арифметики и операторов разыменования. Но я бы предпочел реализовать для этих целей итератор (для перехода от строки к строке).

Более двух измерений? Вам просто нужны внутренние классы, которые реализуют соответствующий operator[]...[] of (n-1) -размер. Увы, рутина..