Jak są przechowywane tablice 3D w C?

Rozumiem, że tablice w C są przydzielane w porządku row-major. Dlatego dla tablicy 2 x 3:

0  1
2  3
4  5

Jest przechowywany w pamięci jako

0 1 2 3 4 5

Ale co jeśli mam tablicę 2 x 3 x 2:

0  1
2  3
4  5

I

6  7
8  9
10 11
Jak są one przechowywane w pamięci? Jest po prostu jak:
0 1 2 3 4 5 6 7 8 9 10 11
A może w inny sposób? Czy to zależy od czegoś?
Author: robintw, 2011-05-07

8 answers

Wszystkie "wymiary" są przechowywane kolejno w pamięci.

Rozważmy

    int arr[4][100][20];

Można powiedzieć, że arr[1] i arr[2] (typu int[100][20]) są sąsiadujące ze sobą
lub że arr[1][42] i arr[1][43] (typu int[20]) są sąsiadujące ze sobą
lub że arr[1][42][7] i arr[1][42][8] (Typu int) są sąsiadujące ze sobą

 17
Author: pmg,
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
2011-05-07 12:30:39

Na niskim poziomie nie ma czegoś takiego jak tablica wielowymiarowa. Istnieje tylko płaski blok pamięci, wystarczająco duży, aby pomieścić określoną liczbę elementów. W C Tablica wielowymiarowa jest koncepcyjnie tablicą, której elementy są również tablicami. Więc jeśli tak:

int array[2][3];

Koncepcyjnie kończy się na:

array[0] => [0, 1, 2]
array[1] => [0, 1, 2]

Powoduje to, że elementy są ułożone równolegle w pamięci, ponieważ array[0] i array[1] w rzeczywistości nie przechowują żadnych danych, są tylko odniesieniami do dwie wewnętrzne tablice. Zauważ, że oznacza to, że tylko wpisy [0, 1, 2] zajmują miejsce w pamięci. Jeśli rozszerzysz ten wzór do następnego wymiaru, zobaczysz, że:

int array[2][3][2];

...daje strukturę typu:

array[0] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]
array[1] => [0] => [0, 1]
            [1] => [0, 1]
            [2] => [0, 1]

Który kontynuuje układanie elementów kolejno w pamięci (jak wyżej, tylko [0, 1] wpisy faktycznie zajmują miejsce w pamięci, Wszystko inne jest tylko częścią odniesienia do jednego z tych wpisów). Jak widać, ten wzór będzie kontynuowany nie ważne, ile masz wymiarów.

I tylko dla Zabawy:

int array[2][3][2][5];

Daje:

array[0] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
array[1] => [0] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [1] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
            [2] => [0] => [0, 1, 2, 3, 4]
                   [1] => [0, 1, 2, 3, 4]
 26
Author: aroth,
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
2011-05-07 12:48:25

Tak, masz rację - są one przechowywane kolejno. Rozważ ten przykład:

#include <stdio.h>

int array3d[2][3][2] = {
  {{0, 1}, {2, 3}, {3, 4}},
  {{5, 6}, {7, 8}, {9, 10}}
};

int main()
{
  int i;
  for(i = 0; i < 12; i++) {
    printf("%d ", *((int*)array3d + i));
  }
  printf("\n");
  return 0;
}

Wyjście:

0 1 2 3 3 4 5 6 7 8 9 10

 13
Author: sje397,
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
2013-05-16 13:31:59

Tak, są po prostu przechowywane w następującej kolejności. Możesz to przetestować w ten sposób:

#include <stdio.h>

int main (int argc, char const *argv[])
{
  int numbers [2][3][4] = {{{1,2,3,4},{5,6,7,8},{9,10,11,12}}
                          ,{{13,14,15,16},{17,18,19,20},{21,22,23,24}}};

  int i,j,k;

  printf("3D:\n");
  for(i=0;i<2;++i)
    for(j=0;j<3;++j)
      for(k=0;k<4;++k)
        printf("%i ", numbers[i][j][k]);

  printf("\n\n1D:\n");
  for(i=0;i<24;++i)
    printf("%i ", *((int*)numbers+i));

  printf("\n");

  return 0;
}

Oznacza to,że dostępy do tablicy wielowątkowej o wymiarach (N,M, L) są przekształcane do dostępu jednowymiarowego w ten sposób:

array[i][j][k] = array[M*L*i + L*j + k]
 7
Author: Thies Heidecke,
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
2011-05-07 12:59:33

Myślę, że odpowiedziałeś na swoje pytanie. Tablice wielowymiarowe są przechowywane w porządku rząd-major.

Patrz specyfikacja ANSI C sekcja 3.3.2.1 (istnieje również konkretny przykład):

Kolejne operatory indeksowe wyznaczają członka wielowymiarowy obiekt tablicy. Jeśli E jest tablicą n-wymiarową (n =2) o wymiarach i x j " x ... x " k, następnie E (używane jako inne niż lvalue) jest konwertowany na wskaźnik do (n -1)-wymiarowej tablicy z wymiary j "x"... x " K. Jeżeli do tej pointer jawnie lub niejawnie w wyniku subskrybowania, wynikiem jest tablica wskazująca na ( n -1)-wymiarową, która sama w sobie jest przekształcony w wskaźnik, jeśli jest używany jako inny niż lvalue. Następuje z tego, że tablice są przechowywane w kolejności wierszy-głównych (ostatni indeks dolny waha się najszybciej).

Dla Twojego przykładu, możesz po prostu wypróbować i zobaczyć - http://codepad.org/10ylsgPj

 6
Author: CodeButcher,
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
2011-05-07 12:34:54

Powiedzmy, że masz tablicę char arr[3][4][5]. Jest to tablica 3 tablic po 4 tablice po 5 znaków.

Dla uproszczenia powiedzmy, że wartość w arr[x][y][z] wynosi xyz, a w arr[1][2][3] przechowujemy 123.

Więc układ w pamięci to:

  |  00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15  16  17  18  19
--+--------------------------------------------------------------------------------   
00| 000 001 002 003 004 010 011 012 013 014 020 021 022 023 024 030 031 032 033 034 
20| 100 101 102 103 104 110 111 112 113 114 120 121 122 123 124 130 131 132 133 134 
40| 200 201 202 203 204 210 211 212 213 214 220 221 222 223 224 230 231 232 233 234

arr[0], arr[1] i arr[2] pojawiają się jeden po drugim, ale każdy element jest Typu char[4][5] (są to trzy wiersze w tabeli).

arr[x][0] - arr[x][3] się też jeden po drugim, a każdy element w nich jest typu char[5] (te są to cztery części każdej linii w tabeli, 000-004 jest jednym elementem arr[0][0])

arr[x][y][0] - arr[x][y][4] są 5 bajtów, które przychodzą jeden po drugim.

 3
Author: MByD,
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
2011-05-07 12:37:47

Aby odpowiedzieć na komentarz OP do głównego pytania (będzie to trochę długie, więc postanowiłem przejść z odpowiedzią, a nie komentarzem):

Tablice w C powinny być zadeklarowane jako array[ny][nx] gdzie ny i nx są liczbą elementów w kierunku y i X. Co więcej, Czy to oznacza, że moja tablica 3D powinna być zadeklarowana jako array[nz][ny][nx]?

W matematyce macierz MxN ma M wierszy i N kolumn. Typową notacją dla elementu macierzy jest a(i,j), 1<=i<=M, 1<=j<=N. Więc pierwszą macierzą w twoim pytaniu jest Matryca 3x2.

Rzeczywiście różni się od notacji zwykle używanej np. dla elementów GUI. Bitmapy 800x600 mają 800 pikseli w poziomie (wzdłuż osi X) i 600 pikseli w pionie (wzdłuż osi Y). Gdyby ktoś chciał opisać ją jako macierz, w notacji matematycznej byłaby to macierz 600x800 (600 wierszy, 800 kolumn).

Teraz wielowymiarowe tablice w C są przechowywane w pamięci w taki sposób, że a[i][j+1] jest obok a[i][j], podczas gdy a[i+1][j] jest N elementów od siebie. Zwykle mówi się, że" ostatni indeks dolny różni się najszybciej", lub często jako "przechowywany przez wiersze": wiersz (tzn. elementy o tym samym pierwszym indeksie) w macierzy 2-wymiarowej został umieszczony w pamięci, podczas gdy kolumna (ten sam drugi indeks) składa się z elementów leżących daleko od siebie. Ważne jest, aby wiedzieć ze względu na wydajność: dostęp do sąsiadujących elementów jest zwykle znacznie szybszy (ze względu na pamięci podręczne sprzętowe itp.), więc np. zagnieżdżone pętle powinny być zorganizowane w taki sposób, aby najbardziej wewnętrzna indeks.

Wracając do pytania: jeśli twój obraz mentalny (abstrakcja) tablicy 2D jest obrazem siatki we współrzędnych kartezjańskich, to tak, możesz myśleć o tym jak o array[NY][NX] W C. Jednak jeśli potrzebujesz opisać prawdziwe dane 2D lub 3D jako tablicę, wybór indeksów prawdopodobnie zależy od innych rzeczy: formatów danych, wygodnej notacji, wydajności itp. Na przykład, jeśli reprezentacja w pamięci dla bitmapy jest array[NX][NY] w formacie, z którym musisz pracować, zadeklarujesz to w ten sposób i może nawet nie musisz wiedzieć, że bitmapa staje się jakby "transponowana":) [11]}

 2
Author: Alexey Kukanov,
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
2011-05-07 20:54:24

Tablica 3d jest rozszerzoną tablicą 2d.

Na przykład mamy tablicę - int arr(3)(5)(6);

Jest to tablica, która składa się z dwóch tablic 2d, gdzie tablica będzie miała tablicę 2d zawierającą 4 wiersze i 3 kolumny.

 -3
Author: sukanta mondal,
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
2014-09-02 18:16:38