web-dev-qa-db-fra.com

Comment les tableaux 3D sont-ils stockés en C?

Je comprends que les tableaux en C sont alloués dans l'ordre des lignes principales. Par conséquent, pour un tableau 2 x 3:

0  1
2  3
4  5

Est stocké en mémoire sous la forme

0 1 2 3 4 5

Cependant, que se passe-t-il si j'ai un tableau 2 x 3 x 2:

0  1
2  3
4  5

et

6  7
8  9
10 11

Comment sont-ils stockés en mémoire? Est juste consécutif comme:

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

Ou est-ce d'une autre manière? Ou cela dépend-il de quelque chose?

38
robintw

Toutes les "dimensions" sont stockées consécutivement en mémoire.

Considérer

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

vous pouvez dire ça arr[1] et arr[2] (de type int[100][20]) sont contigus
ou ça arr[1][42] et arr[1][43] (de type int[20]) sont contigus
ou ça arr[1][42][7] et arr[1][42][8] (de type int) sont contigus

18
pmg

À un niveau bas, il n’existe pas de tableau multidimensionnel. Il n'y a qu'un bloc de mémoire plat, suffisamment grand pour contenir un nombre donné d'éléments. En C, un tableau multidimensionnel est conceptuellement un tableau dont les éléments sont également des tableaux. Donc si vous le faites:

int array[2][3];

Conceptuellement, vous vous retrouvez avec:

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

Il en résulte que les éléments sont disposés de manière contiguë en mémoire, car array[0] et array[1] ne contient en fait aucune donnée, ce ne sont que des références aux deux tableaux internes. Notez que cela signifie que seul le [0, 1, 2] les entrées occupent réellement de l'espace en mémoire. Si vous étendez ce motif à la dimension suivante, vous pouvez voir que:

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

... vous donnera une structure comme:

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

Qui continue à disposer les éléments consécutivement en mémoire (comme ci-dessus, seul le [0, 1] les entrées occupent réellement de l'espace en mémoire, tout le reste n'est qu'une partie d'une référence à l'une de ces entrées). Comme vous pouvez le voir, ce motif continuera quel que soit le nombre de dimensions dont vous disposez.

Et juste pour le plaisir:

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

Vous donne:

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
aroth

Oui, vous avez raison - ils sont stockés consécutivement. Considérez cet exemple:

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

Sortie:

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

13
sje397

Oui, ils sont simplement stockés dans un ordre consécutif. Vous pouvez tester cela comme ceci:

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

Cela signifie que les accès à un tableau multi-indexé avec des dimensions (N, M, L) sont transformés en accès unidimensionnels comme ceci:

array[i][j][k] = array[M*L*i + L*j + k]
8
Thies Heidecke

Je pense que vous avez répondu à votre propre question. Les tableaux multidimensionnels sont stockés dans l'ordre des lignes principales.

Voir la section 3.3.2.1 de la spécification ANSI C (il existe également un exemple spécifique):

Les opérateurs d'indice successifs désignent un membre d'un objet de tableau multidimensionnel. Si E est un tableau à n dimensions (n ​​= 2) avec des dimensions ixj "x ... x" k, alors E (utilisé comme autre qu'une valeur l) est converti en un pointeur vers un tableau à (n -1) dimensions avec les dimensions j "x ... x" k. Si l'opérateur unaire * est appliqué à ce pointeur explicitement ou implicitement à la suite de la souscription, le résultat est le tableau dimensionnel pointé vers (n -1), qui lui-même est converti en pointeur s'il est utilisé autrement que comme une valeur l . Il en résulte que les tableaux sont stockés dans l'ordre des lignes principales (le dernier indice varie le plus rapidement).

Pour votre exemple, vous pouvez simplement l'essayer et voir - http://codepad.org/10ylsgPj

6
CodeButcher

Disons que vous avez un tableau char arr[3][4][5]. Il s'agit d'un tableau de 3 tableaux de 4 tableaux de 5 caractères.

Pour simplifier, disons que la valeur de arr[x][y][z] est xyz et dans arr[1][2][3] nous stockons 123.

La disposition en mémoire est donc:

  |  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] et arr[2] arrivent les uns après les autres, mais chaque élément du est de type char[4][5] (ce sont les trois lignes du tableau).

arr[x][0] - arr[x][3] viennent également les uns après les autres, et chaque élément est de type char[5] (ce sont les quatre parties de chaque ligne du tableau, 000 - 004 est un élément de arr[0][0])

arr[x][y][0] - arr[x][y][4] sont 5 octets qui se succèdent.

3
MByD

Pour répondre au commentaire d'OP à la question principale (ce sera un peu long, j'ai donc décidé d'y aller avec une réponse, pas un commentaire):

Les tableaux en C doivent être déclarés comme array[ny][nx]ny et nx sont le nombre d'éléments dans la direction y et x. De plus, cela signifie-t-il que ma matrice 3D doit être déclarée comme array[nz][ny][nx]?

En mathématiques, une matrice MxN a M lignes et N colonnes. Une notation habituelle pour un élément de matrice est a(i,j), 1<=i<=M, 1<=j<=N. La première matrice de votre question est donc une matrice 3x2.

En effet, elle est différente de la notation généralement utilisée pour par exemple Éléments GUI. Un bitmap 800x600 a 800 pixels horizontalement (le long de l'axe X) et 600 pixels verticalement (le long de l'axe Y). Si certains voudraient le décrire comme une matrice, en notation mathématique, ce serait une matrice 600x800 (600 lignes, 800 colonnes).

Maintenant, les tableaux multidimensionnels en C sont stockés en mémoire de telle manière que a[i][j+1] Est à côté de a[i][j] Tandis que a[i+1][j] Est à N éléments. On dit généralement que "le dernier indice varie le plus rapidement", ou souvent comme "stocké par lignes": une ligne (c'est-à-dire des éléments avec le même premier index) dans une matrice à 2 dimensions a été placée de façon contiguë en mémoire tandis qu'une colonne (même deuxième index ) sont constitués d'éléments éloignés les uns des autres. Il est important de savoir pour des considérations de performances: l'accès aux éléments voisins est généralement beaucoup plus rapide (en raison des caches HW, etc.), par exemple, les boucles imbriquées doivent être organisées de telle sorte que la plus interne itère sur le dernier index.

Retour à la question: si votre image mentale (abstraction) d'un tableau 2D est celle d'un réseau en coordonnées cartésiennes, alors oui, vous pouvez le considérer comme array[NY][NX] En C. Cependant, si vous avez besoin de décrire de vrais Données 2D ou 3D sous forme de tableau, le choix des index dépend probablement d'autres choses: formats de données, notation pratique, performances, etc. Par exemple, si la représentation en mémoire d'un bitmap est array[NX][NY] Dans un format vous devez travailler avec, vous le déclarerez de cette façon, et peut-être n'avez-vous même pas besoin de savoir que le bitmap devient une sorte de "transposé" :)

2
Alexey Kukanov