En C, je sais que je peux allouer dynamiquement un tableau à deux dimensions sur le tas en utilisant le code suivant:
int** someNumbers = malloc(arrayRows*sizeof(int*));
for (i = 0; i < arrayRows; i++) {
someNumbers[i] = malloc(arrayColumns*sizeof(int));
}
Clairement, cela crée en fait un tableau unidimensionnel de pointeurs vers un groupe de tableaux unidimensionnels séparés d'entiers, et "The System" peut comprendre ce que je veux dire quand je demande:
someNumbers[4][2];
Mais quand je déclare statiquement un tableau 2D, comme dans la ligne suivante ...:
int someNumbers[ARRAY_ROWS][ARRAY_COLUMNS];
... une structure similaire est-elle créée sur la pile ou est-elle complètement d'une autre forme? (c'est-à-dire est-ce un tableau 1D de pointeurs? Sinon, qu'est-ce que c'est, et comment les références sont-elles déterminées?)
De plus, quand j'ai dit «Le système», qu'est-ce qui est réellement responsable de le découvrir? Le noyau? Ou le compilateur C le trie-t-il lors de la compilation?
la source
malloc()
ne donnent pas un tableau à N dimensions. . Il en résulte des tableaux de pointeurs [vers des tableaux de pointeurs [...] pour séparer complètement les tableaux unidimensionnels . Consultez Allocation correcte de tableaux multidimensionnels pour voir comment allouer un tableau TRUE N-dimensionnel.Réponses:
Un tableau statique à deux dimensions ressemble à un tableau de tableaux - il est simplement disposé de manière contiguë en mémoire. Les tableaux ne sont pas la même chose que les pointeurs, mais comme vous pouvez souvent les utiliser de manière interchangeable, cela peut parfois être déroutant. Le compilateur effectue le suivi correctement, cependant, ce qui fait que tout s'aligne bien. Vous devez être prudent avec les tableaux 2D statiques comme vous le mentionnez, car si vous essayez d'en transmettre un à une fonction prenant un
int **
paramètre, de mauvaises choses vont se produire. Voici un exemple rapide:En mémoire ressemble à ceci:
exactement la même chose que:
Mais si vous essayez de passer
array1
à cette fonction:vous recevrez un avertissement (et l'application ne parviendra pas à accéder correctement au tableau):
Parce qu'un tableau 2D n'est pas le même que
int **
. La décomposition automatique d'un tableau en un pointeur ne va pour ainsi dire "qu'un niveau de profondeur". Vous devez déclarer la fonction comme:ou
Pour tout rendre heureux.
Ce même concept s'étend aux tableaux à n dimensions. Tirer parti de ce type d'activité amusante dans votre application ne fait généralement que le rendre plus difficile à comprendre. Alors soyez prudent là-bas.
la source
sizeof(int[100]) != sizeof(int *)
(à moins que vous ne trouviez une plate-forme avec100 * sizeof(int)
octets /int
, mais c'est une chose différente.La réponse est basée sur l'idée que C n'a pas vraiment avoir des tableaux 2D - il a des tableaux-de-tableaux. Lorsque vous déclarez ceci:
Vous demandez
someNumbers
d'être un tableau de 4 éléments, où chaque élément de ce tableau est de typeint [2]
(qui est lui-même un tableau de 2int
s).L'autre partie du puzzle est que les tableaux sont toujours disposés de manière contiguë en mémoire. Si vous demandez:
alors cela ressemblera toujours à ceci:
(4
sometype_t
objets disposés les uns à côté des autres, sans espace entre les deux). Donc, dans votresomeNumbers
tableau de tableaux, cela ressemblera à ceci:Et chaque
int [2]
élément est lui-même un tableau, qui ressemble à ceci:Donc, dans l'ensemble, vous obtenez ceci:
la source
int
dans le tableau de tableaux (par exemple en évaluanta[0]
ou&a[0][0]
) alors oui, vous pouvez compenser cela pour accéder séquentiellement à tousint
).en mémoire est égal à:
la source
En réponse à votre également: les deux, bien que le compilateur fasse la majeure partie du gros du travail.
Dans le cas de tableaux alloués statiquement, "The System" sera le compilateur. Il réservera la mémoire comme il le ferait pour n'importe quelle variable de pile.
Dans le cas du tableau malloc'd, "The System" sera l'implémenteur de malloc (le noyau généralement). Tout ce que le compilateur allouera est le pointeur de base.
Le compilateur va toujours gérer le type comme ce qu'il est déclaré être, sauf dans l'exemple donné par Carl où il peut trouver une utilisation interchangeable. C'est pourquoi si vous passez un [] [] à une fonction, il doit supposer qu'il s'agit d'un flat alloué statiquement, où ** est supposé être un pointeur vers un pointeur.
la source
malloc
est implémenté n'est pas spécifié par la norme et laissé à l'implémentation, resp. environnement. Pour les environnements autonomes, il est facultatif comme toutes les parties de la bibliothèque standard nécessitant des fonctions de liaison (c'est ce que les exigences entraînent réellement, pas littéralement ce que le standard déclare). Pour certains environnements hébergés modernes, il repose en effet sur les fonctions du noyau, soit le contenu complet, soit (par exemple Linux) comme vous l'avez écrit en utilisant à la fois stdlib et kernel-primitives. Pour les systèmes à un seul processus de mémoire non virtuelle, il ne peut s'agir que de stdlib.Supposons que nous avons
a1
eta2
défini et initialisées comme ci - dessous (C99):a1
est un tableau 2D homogène avec une disposition continue simple en mémoire et l'expression(int*)a1
est évaluée à un pointeur vers son premier élément:a2
est initialisé à partir d'un tableau 2D hétérogène et est un pointeur vers une valeur de typeint*
, c'est-à-dire que l'expression de déréférencement*a2
s'évalue en une valeur de typeint*
, la disposition de la mémoire n'a pas besoin d'être continue:Malgré une disposition de la mémoire et une sémantique d'accès totalement différentes, la grammaire du langage C pour les expressions d'accès aux tableaux est exactement la même pour les tableaux 2D homogènes et hétérogènes:
a1[1][0]
récupérera la valeur144
dua1
tableaua2[1][0]
récupérera la valeur244
dua2
tableauLe compilateur sait que l'expression d'accès pour
a1
opère sur le typeint[2][2]
, lorsque l'expression d'accès poura2
opère sur le typeint**
. Le code d'assemblage généré suivra la sémantique d'accès homogène ou hétérogène.Le code se bloque généralement au moment de l'exécution lorsque le tableau de type
int[N][M]
est casté puis accédé en tant que typeint**
, par exemple:la source
Pour accéder à un tableau 2D particulier, considérez la carte mémoire pour une déclaration de tableau comme indiqué dans le code ci-dessous:
Pour accéder à chaque élément, il suffit de passer le tableau qui vous intéresse en tant que paramètres à la fonction. Ensuite, utilisez le décalage pour la colonne pour accéder individuellement à chaque élément.
la source