Jak pracować z dynamicznymi tablicami wielowymiarowymi w C?

Czy ktoś wie jak Mogę używać dynamicznie przydzielanych tablic wielowymiarowych używając C? Czy to możliwe?

Author: Jonathan Leffler, 2009-05-27

9 answers

Z dynamiczną alokacją, używając malloc:

int** x;

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

//Writing values
x[0..(dimension1_max-1)][0..(dimension2_max-1)] = Value; 
[...]

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

To przydziela tablicę 2D wielkości dimension1_max * dimension2_max. Na przykład, jeśli chcesz mieć tablicę 640 * 480 (np. piksele obrazu), użyj dimension1_max = 640, dimension2_max = 480. Następnie można uzyskać dostęp do tablicy używając x[d1][d2] Gdzie d1 = 0..639, d2 = 0..479.

Ale wyszukiwanie w SO lub Google ujawnia również inne możliwości, na przykład w tym pytaniu SO

Zauważ, że tablica nie przydzieli sąsiadującego obszaru pamięci (640*480 bajtów), co mogłoby powodować problemy z funkcjami, które to zakładają. Tak więc, aby tablica spełniała warunek, zastąp powyższy blok malloc tym:

int** x;
int* temp;

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

[...]

free(temp);
free(x);
 80
Author: schnaader,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-24 10:56:35

Od C99, C ma tablice 2D z dynamicznymi granicami. Jeśli chcesz uniknąć tego, że takie bestie są przydzielane na stosie( co powinieneś), możesz je łatwo przydzielić jednym ruchem w następujący sposób

double (*A)[n] = malloc(sizeof(double[n][n]));
I to wszystko. Następnie można go łatwo używać, ponieważ są używane do tablic 2D z czymś takim jak A[i][j]. I nie zapomnij tego na końcu
free(A);

Randy Meyers napisał serię artykułów wyjaśniających tablice o zmiennej długości (VLAs) .

 82
Author: Jens Gustedt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-20 06:21:27

Podstawy

Tablice w c są deklarowane i dostępne za pomocą operatora []. Tak, że

int ary1[5];

Deklaruje tablicę 5 liczb całkowitych. Elementy są numerowane od zera, więc ary1[0] jest pierwszym elementem, a {[10] } jest ostatnim elementem. Uwaga 1: nie ma domyślnej inicjalizacji, więc pamięć zajmowana przez tablicę może początkowo zawierać cokolwiek. Note2: ary1[5] uzyskuje dostęp do pamięci w stanie nieokreślonym (który może nie być nawet Dostępny dla ciebie), więc nie rób tego to!

Tablice wielowymiarowe są zaimplementowane jako tablica tablic (of arrays (of ... ) ). Więc

float ary2[3][5];

Deklaruje tablicę 3 jednowymiarowych tablic po 5 liczb zmiennoprzecinkowych każda. Teraz ary2[0][0] jest pierwszym elementem pierwszej tablicy, {[13] } jest ostatnim elementem pierwszej tablicy, a ary2[2][4] jest ostatnim elementem ostatniej tablicy. Standard '89 wymaga, aby te dane były zgodne (sek. A8. 6. 2 na stronie 216 mojego K & R 2nd. ed.) ale wydaje się być agnostykiem na wyściółka.

Próbuje przejść dynamicznie w więcej niż jednym wymiarze

Jeśli nie znasz rozmiaru tablicy podczas kompilacji, będziesz chciał dynamicznie przydzielić tablicę. To kuszące, aby spróbować

double *buf3;
buf3 = malloc(3*5*sizeof(double));
/* error checking goes here */

Które powinno zadziałać, jeśli kompilator nie dokona zadanej alokacji (wstawia dodatkową przestrzeń między jednowymiarowymi tablicami). Może być bezpieczniej:

double *buf4;
buf4 = malloc(sizeof(double[3][5]));
/* error checking */
/ Align = "left" / Nie możesz pisać buf[i][j] ponieważ buf ma zły typ. Nie można też używać
double **hdl4 = (double**)buf;
hdl4[2][3] = 0; /* Wrong! */

Ponieważ kompilator oczekuje, że hdl4 będzie adresem adresu podwójnego. Nie można też używać double incomplete_ary4[][];, ponieważ jest to błąd;

Więc co możesz zrobić?
  • wykonaj arytmetykę wierszy i kolumn samodzielnie
  • przydzielaj i wykonuj pracę w funkcji
  • użyj tablicy wskaźników (mechanizm o którym mówi qrdl)

Do the math yourself

Po prostu Oblicz przesunięcie pamięci do każdego elementu tak:

  for (i=0; i<3; ++i){
     for(j=0; j<3; ++j){
        buf3[i * 5 + j] = someValue(i,j); /* Don't need to worry about 
                                             padding in this case */
     }
  }

Przydzielaj i wykonuj pracę w funkcji

Zdefiniuj funkcję, która przyjmuje wymagany rozmiar jako argument i postępuje normalnie

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

Oczywiście, w tym przypadku ary4 jest zmienną lokalną i nie można jej zwrócić: cała praca z tablicą musi być wykonana w funkcji, którą wywołujesz w funkcjach, które wywołuje ona .

Tablica wskaźników

Rozważ to:

double **hdl5 = malloc(3*sizeof(double*));
/* Error checking */
for (i=0; i<3; ++i){
   hdl5[i] = malloc(5*sizeof(double))
   /* Error checking */
}

Teraz hdl5 wskazuje na tablicę wskaźników każdy z nich wskazuje na tablicę sobowtórów. Fajnym bitem jest to, że możesz użyć dwuwymiarowej tablicy, aby uzyskać dostęp do tej struktury---hdl5[0][2] dostaje środkowy element pierwszego wiersza---ale to nie jest inny rodzaj obiektu niż dwuwymiarowa tablica zadeklarowana przez double ary[3][5];.

Ta struktura jest bardziej elastyczna niż tablica dwuwymiarowa (ponieważ wiersze nie muszą być tej samej długości), ale dostęp do niej będzie na ogół wolniejszy i wymaga więcej pamięci (potrzebujesz miejsce w klasyfikacji średniej punktów).

Zauważ, że ponieważ nie ustawiłem żadnych strażników, będziesz musiał śledzić rozmiar wszystkich tablic samodzielnie.

Arytmetyka

C nie zapewnia wsparcia dla matematyki wektorowej, macierzowej czy tensorowej, będziesz musiał ją zaimplementować samodzielnie lub wprowadzić bibliotekę.

Mnożenie przez Skaler oraz dodawanie i odejmowanie tablic o tej samej randze są łatwe: wystarczy zapętlić elementy i wykonać operację w miarę upływu czasu. Inner produkty są podobnie proste.

Produkty zewnętrzne oznaczają więcej pętli.

 53
Author: dmckee --- ex-moderator kitten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-07 17:54:26

Jeśli znasz liczbę kolumn w czasie kompilacji, jest to dość proste:

#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

Możesz traktować ap jak dowolną tablicę 2D:

ap[i][j] = x;

Kiedy skończysz, deallokujesz to jako

free(ap);

Jeśli nie znasz liczby kolumn w czasie kompilacji, ale pracujesz z kompilatorem C99 lub kompilatorem C2011, który obsługuje tablice o zmiennej długości, to nadal jest to dość proste:

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

Jeśli nie znasz liczby kolumn w czasie kompilacji i pracujesz z wersją C, który nie obsługuje tablic o zmiennej długości, musisz zrobić coś innego. Jeśli chcesz, aby wszystkie elementy zostały przydzielone w ciągłym fragmencie (jak zwykła tablica), możesz przydzielić pamięć jako tablicę 1D i obliczyć przesunięcie 1D:

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

Jeśli nie potrzebujesz, aby pamięć była sąsiadująca, możesz zastosować dwuetapową metodę alokacji:

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;
Ponieważ alokacja była procesem dwuetapowym, dealokacja również musi być procesem dwuetapowym:
for (i = 0; i < cols; i++)
  free(ap[i]);
free(ap);
 11
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-09 18:19:50

Malloc się nada.

 int rows = 20;
 int cols = 20;
 int *array;

  array = malloc(rows * cols * sizeof(int));

Aby uzyskać pomoc, zapoznaj się z poniższym artykułem: -

Http://courses.cs.vt.edu / ~ cs2704/spring00/Mcquain/Notes/4up / Managing2DArrays.pdf

 0
Author: Rahul Tripathi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-09 16:24:51

Oto roboczy kod, który definiuje podprogram make_3d_array do przydzielania wielowymiarowej tablicy 3D z N1, N2 i N3 elementów w każdym wymiarze, a następnie zapełnia go przypadkowymi liczbami. Możesz użyć notacji A[i][j][k], aby uzyskać dostęp do jej elementów.

#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);
}
 0
Author: Rodrigo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-31 21:18:13

Nie da się przeznaczyć całości za jednym zamachem. Zamiast tego utwórz tablicę wskaźników, a następnie dla każdego wskaźnika utwórz dla niej pamięć. Na przykład:

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

Oczywiście można również zadeklarować tablicę jako {[2] } i pominąć pierwszy malloc, ale drugi zestaw jest potrzebny do dynamicznego przydzielania wymaganego miejsca.

Możliwe jest zhakowanie sposobu przydzielania go w jednym kroku, ale wymagałoby to niestandardowej funkcji wyszukiwania, ale napisanie tego w taki sposób, że to zawsze będzie działać może być irytujące. Przykładem może być L(arr,x,y,max_x) arr[(y)*(max_x) + (x)], Następnie malloc blok 50 * 50 ints lub cokolwiek i dostęp za pomocą tego makra L, np.

#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);

Ale to jest o wiele groźniejsze, chyba że znasz efekty tego, co robisz z makrem preprocesora.

 -1
Author: slugonamission,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-09 16:22:37
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));
        }
 -2
Author: Raj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-09 16:47:00

// Użyj new zamiast malloc, ponieważ użycie malloc prowadzi do wycieków pamięci 'wpisz tutaj kod

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

        adj_list[i] = new int[colsize];

    }
 -2
Author: Arkaprabha Banerjee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-14 05:56:09