Confusion de pointeur variable dans un tableau 2D

J’ai un doute de base sur les tableaux 2D (langage C). Considérons une déclaration d’un tableau 2D comme suit

int array[3][5]; 

Maintenant, quand je fais ce qui suit, la sortie des deux printf ci-dessous est la même:

 printf("%u\n", array); printf("%u\n", *(array)); 

Maintenant, quand essayer de ce qui suit:

 printf("%u\n", array+1); printf("%u\n", *(array)+1); 

Les sorties sont différentes. Je comprends que la 2ème printf se réfère au tableau [0] [1] et le premier au tableau [1] [0]. Comment cela marche-t-il? tableau est un pointeur vers quoi?

Merci d’avance

Les tableaux ne sont pas des pointeurs. Ignorez les réponses, livres ou tutoriels qui tentent de vous dire le contraire.

Une expression de type tableau, dans la plupart des contextes, est convertie (au moment de la compilation) en un pointeur sur le premier élément du tableau. Les exceptions sont:

  • L’opérande de sizeof ( sizeof arr donne la taille du tableau, pas la taille d’un pointeur)
  • L’opérande de unary & ( &arr donne l’adresse du tableau, pas de son premier élément – même emplacement mémoire, type différent). Ceci est particulièrement pertinent pour votre exemple.
  • Un littéral de chaîne dans un initialiseur utilisé pour initialiser un object de tableau ( char s[6] = "hello"; ne copie pas l’adresse du littéral de chaîne, il copie sa valeur)

Un tableau à 2 dimensions n’est ni plus ni moins qu’un tableau de tableaux. Il existe d’autres structures de données qui peuvent être utilisées avec la même syntaxe x[y][z] , mais ce ne sont pas de véritables tableaux à deux dimensions. Le vôtre est.

L’opérateur d’indexation [] est défini en termes d’arithmétique de pointeur. x[y] signifie *(x+y) .

Le comportement de votre code découle de ces règles.

Lisez la section 6 de la FAQ comp.lang.c. C’est la meilleure explication de ce que j’ai vu.

Et n’utilisez pas "%u" pour imprimer les valeurs de pointeur; convertir en void* et utiliser "%p" .

 printf("%p\n", (void*)array); printf("%p\n", (void*)*(array)); 

Je vais essayer de vous donner une explication techniquement correcte pour que vous sachiez ce qui se passe. Pas vraiment compliqué, mais effectivement contre-intuitif.

Intro:

En C, il y a des “lvalues” qui représentent essentiellement des objects “assignables” qui ont une place quelque part dans la mémoire, et des “rvalues” qui représentent, ainsi, des valeurs “conceptuelles” (il n’est pas nécessaire de les placer nulle part en particulier).

Par exemple, si vous définissez int a = 5; , alors a est une lvalue de type int et une valeur 5. En outre, elle peut être interprétée comme (ou plutôt convertie en) une valeur rvalue de type int. On sait toujours qu’une telle valeur est égale à 5, mais elle ne contiendrait plus les informations concernant la position d’ a s en mémoire.

Certaines expressions ont besoin de valeurs (comme le côté gauche de operator =, car vous devez affecter un object), et d’autres, de valeurs (comme operator +, car vous n’avez besoin que des valeurs intégrales lorsque vous ajoutez, ou du côté droit de operator =). . Si une expression nécessite une valeur rvalue mais que vous passez une valeur lvalue, elle est convertie en une valeur rvalue.

De plus, seules les valeurs rvalues ​​sont transmises aux fonctions en C (ce qui signifie que C est ssortingctement appel par valeur, et non par appel).

Quelques exemples:

 int a = 1; a; // a is an lvalue of type int and value 1 a = a+3; // here the `a` is converted to an rvalue of type int and value 1, then after the addition there's an assignment, on the lhs there's an lvalue `a` and an rvalue `4` 

La conversion d’une valeur en valeur est généralement sortingviale et imperceptible (c’est comme si vous preniez le nombre 5 sur une étagère appelée a ). Les tableaux sont fondamentalement l’exception ici.

Le gros problème: il n’ya pas de valeurs de type tableau dans C. Il existe des lvalues ​​et rvalues ​​de pointeur, des lvalues ​​et des rvalues ​​entières, des lvalues ​​et des rvalues ​​de structure, etc., mais uniquement des tableaux de lvalue. Lorsque vous essayez de convertir une lvalue de type tableau en rvalue, vous n’avez plus de tableau, vous avez un pointeur sur le premier membre du tableau. C’est la racine de la confusion concernant les tableaux en C (et C ++).

Explication:

  • array
  • *(array)
  • array+1
  • *(array)+1

array est une valeur de type int[3][5] (tableau de 3 ints de 5 ints). Lorsque vous essayez de le transmettre à une fonction, elle reçoit un pointeur de type int (*)[5] (pointeur sur un tableau de 5 ints), car c’est ce qui rest après la conversion de lvalue en rvalue.

*(array) est plus difficile. D’abord, la valeur lvalue-rvalue est exécutée, ce qui donne une valeur rvalue de type int(*)[5] , puis l’ operator* prend cette valeur rauf et renvoie une valeur lvalue de type int[5] , que vous essayez ensuite de transmettre à la fonction. Par conséquent, il est à nouveau converti en une valeur, aboutissant à un int* .

array+1 force le tableau à être converti en une valeur rvalue de type int(*)[5] et que cette valeur est incrémentée de un, ainsi (conformément aux règles de l’arithmétique de pointeur), le pointeur se déplace de 1 * sizeof(int[5]) octets en avant.

*(array)+1 : voir 2 points avant, mais la valeur finale du type int* est incrémentée, toujours par les règles de l’arithmétique du pointeur, de 1 * sizeof(int) .

Aucun mystère ici!

Les tableaux 2D en C sont déroutants.
tableau et tableau sont le même pointeur, mais ne sont pas du même type.
array est de type int [3] [5] (qui est un tableau de taille 5 sur int [3] tableaux).
* tableau est la première ligne du tableau, qui est de type int [3].
tableau + 1 signifie tableau plus un élément. Un élément de array est int [3], c’est donc 12 octets en avant.
* array + 1 signifie * array plus un élément. Un élément de * array est int, il faut donc 4 octets en avant.

Vous pouvez comprendre de cette façon:

tableau pointe sur 3 lignes de 5 colonnes chacune

quand vous faites le tableau + 1, la ligne change et vous passez à la première ligne. Vous devriez essayer d’accéder avec * (tableau + 1).

quand vous faites * (tableau), vous pointez sur la 0ème ligne et * (tableau) +1 avance dans la colonne pour que l’élément soit tableau [0] [1]

L’incrément d’unité dans les pointeurs correspond à la taille du type de données.