Pourquoi x [0]! = X [0] [0]! = X [0] [0] [0]?

149

J'étudie un peu le C ++ et je me bats avec des pointeurs. Je comprends que je peux avoir 3 niveaux de pointeurs en déclarant:

int *(*x)[5];

c'est *xdonc un pointeur vers un tableau de 5 éléments qui sont des pointeurs vers int. Je le sais aussi x[0] = *(x+0);, x[1] = *(x+1)et ainsi de suite ...

Alors, compte tenu de la déclaration ci-dessus, pourquoi x[0] != x[0][0] != x[0][0][0]?

Leo91
la source
58
x[0], x[0][0]Et x[0][0][0]ont différents types. Ils ne peuvent pas être comparés. Que voulez-vous dire !=?
bolov
4
@celticminstrel ils ne sont pas les mêmes: int **x[5]est un tableau de 5 éléments. Un élément est un pointeur vers un pointeur vers int`
bolov
5
@celticminstrel int** x[5]serait un tableau de cinq pointeurs pointant vers des pointeurs pointant vers int. int *(*x)[5]est un pointeur vers un tableau de cinq pointeurs qui pointent vers int.
emlai
5
@celticminstrel règle droite-gauche , règle spirale , C charabia ↔ anglais et votre » sur votre chemin pour devenir un programmeur trois étoiles :)
bolov
5
@ Leo91: Premièrement, vous avez deux niveaux de pointeurs ici, pas trois. Deuxièmement, qu'est-ce que cela x[0] != x[0][0] != x[0][0][0]signifie? Ce n'est pas une comparaison valide en C ++. Même si vous le divisez en x[0] != x[0][0]et x[0][0] != x[0][0][0]qu'il n'est toujours pas valide. Alors, que signifie votre question?
AnT

Réponses:

261

xest un pointeur vers un tableau de 5 pointeurs vers int.
x[0]est un tableau de 5 pointeurs vers int.
x[0][0]est un pointeur vers un int.
x[0][0][0]est un int.

                       x[0]
   Pointer to array  +------+                                 x[0][0][0]         
x -----------------> |      |         Pointer to int           +-------+
               0x500 | 0x100| x[0][0]---------------->   0x100 |  10   |
x is a pointer to    |      |                                  +-------+
an array of 5        +------+                        
pointers to int      |      |         Pointer to int                             
               0x504 | 0x222| x[0][1]---------------->   0x222                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x508 | 0x001| x[0][2]---------------->   0x001                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x50C | 0x123| x[0][3]---------------->   0x123                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x510 | 0x000| x[0][4]---------------->   0x000                    
                     |      |                                             
                     +------+                                             

Tu peux voir ça

  • x[0]est un tableau et sera converti en pointeur vers son premier élément lorsqu'il est utilisé dans une expression (à quelques exceptions près). Donnera donc x[0]l'adresse de son premier élément x[0][0]qui est 0x500.
  • x[0][0]contient l'adresse d'un intqui est 0x100.
  • x[0][0][0]contient une intvaleur de 10.

Alors, x[0]est égal &x[0][0]et donc &x[0][0] != x[0][0].
Par conséquent, x[0] != x[0][0] != x[0][0][0].

piratages
la source
Ce diagramme est un peu déroutant pour moi: 0x100devrait apparaître immédiatement à gauche de la boîte contenant 10, de la même manière qui 0x500apparaît à gauche de sa boîte. Au lieu d'être loin à gauche, et en dessous.
MM
@MattMcNabb; Je ne pense pas que cela devrait prêter à confusion, mais des changements selon votre suggestion pour plus de clarté.
haccks
4
@haccks - Mon plaisir :) La raison pour laquelle ce diagramme est génial est que vous n'avez même pas besoin de l'explication que vous avez donnée qui le suit. Ce diagramme lui-même est explicite qu'il répond déjà à la question. Le texte qui suit est simplement un bonus.
rayryeng
1
Vous pouvez également utiliser yed, un logiciel de création de diagrammes. Cela m'aide beaucoup à organiser mes pensées
rpax
@GrijeshChauhan J'utilise asciiflow pour les commentaires du code, yeD pour les présentations :)
rpax
133
x[0] != x[0][0] != x[0][0][0]

est, selon votre propre message,

*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`  

qui est simplifié

*x != **x != ***x

Pourquoi devrait-il être égal?
Le premier est l'adresse d'un pointeur.
Le second est l'adresse d'un autre pointeur.
Et le troisième est une intvaleur.

deviantfan
la source
Je ne comprends pas ... si x [0], x [0] [0], x [0] [0] [0] équivaut à * (x + 0), * (x + 0 + 0) , * (x + 0 + 0 + 0), pourquoi devraient-ils avoir des adresses différentes?
Leo91
41
@ Leo91 l' x[0][0]est (x[0])[0], c'est *((*(x+0))+0)-à- dire non *(x+0+0). Le déréférencement se produit avant le second [0].
emlai
4
@ Leo91 x[0][0] != *(x+0+0)tout comme x[2][3] != x[3][2].
özg
@ Leo91 Le deuxième commentaire que vous avez "compris maintenant" a été supprimé. Vous ne comprenez pas quelque chose (qui pourrait être mieux expliqué dans la réponse), ou n'est-ce pas votre faute? (certaines personnes aiment supprimer les commentaires sans beaucoup de contenu informatif)
deviantfan
@deviantfan désolé, je ne comprends pas ce que tu veux dire. Je comprends les réponses ainsi que de nombreux commentaires qui ont aidé à clarifier le concept.
Leo91
50

Voici la disposition de la mémoire de votre pointeur:

   +------------------+
x: | address of array |
   +------------------+
            |
            V
            +-----------+-----------+-----------+-----------+-----------+
            | pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 |
            +-----------+-----------+-----------+-----------+-----------+
                  |
                  V
                  +--------------+
                  | some integer |
                  +--------------+

x[0]renvoie "adresse du tableau",
x[0][0]renvoie "pointeur 0",
x[0][0][0] renvoie «un entier».

Je crois que cela devrait être évident maintenant, pourquoi ils sont tous différents.


Ce qui précède est assez proche pour une compréhension de base, c'est pourquoi je l'ai écrit comme je l'ai écrit. Cependant, comme le souligne à juste titre haccks, la première ligne n'est pas précise à 100%. Alors voici tous les petits détails:

D'après la définition du langage C, la valeur de x[0]est l'ensemble du tableau de pointeurs entiers. Cependant, les tableaux sont quelque chose avec lequel vous ne pouvez vraiment rien faire en C.Vous manipulez toujours leur adresse ou leurs éléments, jamais le tableau entier dans son ensemble:

  1. Vous pouvez passer x[0]à l' sizeofopérateur. Mais ce n'est pas vraiment une utilisation de la valeur, son résultat dépend uniquement du type.

  2. Vous pouvez prendre son adresse qui donne la valeur de x, c'est-à-dire "adresse du tableau" avec le type int*(*)[5]. En d'autres termes:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x

  3. Dans tous les autres contextes , la valeur de x[0]se décompose en un pointeur vers le premier élément du tableau. Autrement dit, un pointeur avec la valeur "adresse du tableau" et le type int**. L'effet est le même que si vous aviez converti xen un pointeur de type int**.

En raison de la décroissance du pointeur de tableau dans le cas 3., toutes les utilisations de x[0]aboutissent finalement à un pointeur qui pointe le début du tableau de pointeurs; l'appel printf("%p", x[0])imprimera le contenu des cellules de mémoire étiquetées comme "adresse du tableau".

cmaster - réintégrer monica
la source
1
x[0]n'est pas l'adresse du tableau.
haccks le
1
@haccks Oui, suivant la lettre de la norme, ce x[0]n'est pas l'adresse du tableau, c'est le tableau lui-même. J'ai ajouté une explication approfondie de cela, et de la raison pour laquelle j'ai écrit que x[0]c'est "l'adresse du tableau". J'espère que tu aimes.
cmaster
graphiques impressionnants qui l'expliquent parfaitement bien!
MK
"Cependant, les tableaux sont quelque chose avec lequel vous ne pouvez vraiment rien faire en C." -> Exemple de compteur: printf("%zu\n", sizeof x[0]);indique la taille du tableau, pas la taille d'un pointeur.
chux
@ chux-ReinstateMonica Et j'ai continué en disant "Vous manipulez toujours soit leur adresse ou leurs éléments, jamais le tableau entier dans son ensemble", suivi du point 1 de l'énumération où je parle de l'effet de sizeof x[0]...
cmaster - rétablir monica le
18
  • x[0]déréférence le pointeur le plus à l'extérieur ( pointeur vers le tableau de taille 5 du pointeur vers int) et aboutit à un tableau de taille 5 du pointeur vers int;
  • x[0][0]déréférence le pointeur le plus à l'extérieur et indexe le tableau, résultant en un pointeur vers int;
  • x[0][0][0] déréférence tout, ce qui donne une valeur concrète.

Au fait, si jamais vous vous sentez confus par ce que signifient ces types de déclarations, utilisez cdecl .

d125q
la source
11

Considérons les expressions étape par étape x[0], x[0][0]etx[0][0][0] .

Tel que xdéfini de la manière suivante

int *(*x)[5];

alors expression x[0]est un tableau de type int *[5]. Tenez compte du fait que l'expression x[0]est équivalente à l'expression *x. C'est déréférencer un pointeur vers un tableau, nous obtenons le tableau lui-même. Dénotons-le comme y c'est-à-dire que nous avons une déclaration

int * y[5];

L'expression x[0][0]est équivalente à y[0]et a un type int *. Dénotons-le comme z c'est-à-dire que nous avons une déclaration

int *z;

expression x[0][0][0]équivaut à expression y[0][0]qui à son tour équivaut à expression z[0]et a un type int.

Donc nous avons

x[0] a le type int *[5]

x[0][0] a le type int *

x[0][0][0] a le type int

Ce sont donc des objets de types différents et par le biais de tailles différentes.

Exécuter par exemple

std::cout << sizeof( x[0] ) << std::endl;
std::cout << sizeof( x[0][0] ) << std::endl;
std::cout << sizeof( x[0][0][0] ) << std::endl;
Vlad de Moscou
la source
10

La première chose que je dois dire

x [0] = * (x + 0) = * x;

x [0] [0] = * (* (x + 0) + 0) = * * x;

x [0] [0] [0] = * (* (* (x + 0) + 0)) = * * * x;

Donc * x ≠ * * x ≠ * * * x

De l'image suivante, tout est clair.

  x[0][0][0]= 2000

  x[0][0]   = 1001

  x[0]      = 10

entrez la description de l'image ici

C'est juste un exemple, où la valeur de x [0] [0] [0] = 10

et l'adresse de x [0] [0] [0] est 1001

cette adresse est stockée dans x [0] [0] = 1001

et l'adresse de x [0] [0] est 2000

et cette adresse est stockée à x [0] = 2000

Donc x [0] [0] [0] x [0] [0] x [0]

.

ÉDITIONS

Programme 1:

{
int ***x;
x=(int***)malloc(sizeof(int***));
*x=(int**)malloc(sizeof(int**));
**x=(int*)malloc(sizeof(int*));
***x=10;
printf("%d   %d   %d   %d\n",x,*x,**x,***x);
printf("%d   %d   %d   %d   %d",x[0][0][0],x[0][0],x[0],x,&x);
}

Production

142041096 142041112 142041128 10
10 142041128 142041112 142041096 -1076392836

Programme 2:

{
int x[1][1][1]={10};
printf("%d   %d   %d   %d \n ",x[0][0][0],x[0][0],x[0],&x);
}

Production

10   -1074058436   -1074058436   -1074058436 
apm
la source
3
Votre réponse est trompeuse. x[0]ne contient pas d'adresse de fourmi. C'est un tableau. Il se désintégrera pour pointer vers son premier élément.
haccks du
Ummm ... qu'est-ce que ça veut dire? Votre modification est comme une cerise sur le gâteau pour votre mauvaise réponse. Cela n'a aucun sens
haccks le
@haccks Si elle n'utilise que des pointeurs, cette réponse sera correcte. Il y aura quelques changements dans la section d'adresse lors de l'utilisation de Array
apm
7

Si vous deviez afficher les tableaux dans une perspective du monde réel, cela apparaîtrait comme suit:

x[0]est un conteneur de fret rempli de caisses.
x[0][0]est une seule caisse, pleine de boîtes à chaussures, dans le conteneur de fret.
x[0][0][0]est une seule boîte à chaussures à l'intérieur de la caisse, à l'intérieur du conteneur de fret.

Même s'il s'agissait de la seule boîte à chaussures dans la seule caisse du conteneur de fret, il s'agit toujours d'une boîte à chaussures et non d'un conteneur de fret

David Facultatif Courtenay
la source
1
ne serait-ce pas x[0][0]une seule caisse pleine de morceaux de papier sur lesquels sont écrits les emplacements des boîtes à chaussures?
wchargin
4

Il y a un principe en C ++ pour que: une déclaration d'une variable indique exactement la manière d'utiliser la variable. Considérez votre déclaration:

int *(*x)[5];

qui peut être réécrit comme (pour plus de clarté):

int *((*x)[5]);

En raison du principe, nous avons:

*((*x)[i]) is treated as an int value (i = 0..4)
 (*x)[i] is treated as an int* pointer (i = 0..4)
 *x is treated as an int** pointer
 x is treated as an int*** pointer

Par conséquent:

x[0] is an int** pointer
 x[0][0] = (x[0]) [0] is an int* pointer
 x[0][0][0] = (x[0][0]) [0] is an int value

Vous pouvez donc comprendre la différence.

Nghia Bui
la source
1
x[0]est un tableau de 5 pouces, pas un pointeur. (il peut se transformer en pointeur dans la plupart des contextes, mais la distinction est ici importante).
MM
D'accord, mais vous devriez dire: x [0] est un tableau de 5 pointeurs int *
Nghia Bui
Pour donner la bonne dérivation par @MattMcNabb: *(*x)[5]est un int, ainsi (*x)[5]est un int *, *xest donc un (int *)[5], xest donc un *((int *)[5]). Autrement dit, xest un pointeur vers un tableau de 5 pointeurs vers int.
wchargin
2

Vous essayez de comparer différents types par valeur

Si vous prenez les adresses, vous pourriez obtenir plus de ce que vous attendez

Gardez à l'esprit que votre déclaration fait une différence

 int y [5][5][5];

permettrait aux comparaisons que vous voulez, puisque y, y[0], y[0][0], y[0][0][0]aurait différentes valeurs et types , mais la même adresse

int **x[5];

n'occupe pas d'espace contigu.

xet x [0]ont la même adresse, mais x[0][0]et x[0][0][0]sont chacun à des adresses différentes

Glenn Teitelbaum
la source
2
int *(*x)[5]est différent deint **x[5]
MM
2

Être pun pointeur: vous empilez des déréférences avec p[0][0], ce qui équivaut à *((*(p+0))+0).

En notation C référence (&) et déréférence (*):

p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])

Est équivalent à:

p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0

Regardez ça, le & * peut être refactoré, il suffit de le supprimer:

p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)
Luciano
la source
Qu'essayez-vous de montrer avec tout après votre première phrase? Vous avez juste de nombreuses variantes p == p . &(&p[0])[0]est différent dep[0][0]
MM
Le gars a demandé pourquoi 'x [0]! = X [0] [0]! = X [0] [0] [0]' quand x est un pointeur, non? J'ai essayé de lui montrer qu'il pouvait être piégé par la notation C du déréférencement (*) quand il empilait les [0]. Donc, c'est un essai pour lui montrer la notation correcte pour que x soit égal à x [0], en référençant à nouveau x [0] avec &, et ainsi de suite.
Luciano
1

Les autres réponses sont correctes, mais aucune d'entre elles ne souligne l'idée qu'il est possible que les trois contiennent la même valeur et qu'elles sont donc incomplètes.

La raison pour laquelle cela ne peut pas être compris à partir des autres réponses est que toutes les illustrations, bien qu'utiles et certainement raisonnables dans la plupart des circonstances, ne couvrent pas la situation où le pointeur xpointe vers lui-même.

C'est assez facile à construire, mais clairement un peu plus difficile à comprendre. Dans le programme ci-dessous, nous verrons comment nous pouvons forcer les trois valeurs à être identiques.

REMARQUE: le comportement de ce programme n'est pas défini, mais je le publie ici uniquement comme une démonstration intéressante de quelque chose que les pointeurs peuvent faire, mais ne devraient pas .

#include <stdio.h>

int main () {
  int *(*x)[5];

  x = (int *(*)[5]) &x;

  printf("%p\n", x[0]);
  printf("%p\n", x[0][0]);
  printf("%p\n", x[0][0][0]);
}

Cela se compile sans avertissements en C89 et C99, et la sortie est la suivante:

$ ./ptrs
0xbfd9198c
0xbfd9198c
0xbfd9198c

Fait intéressant, les trois valeurs sont identiques. Mais cela ne devrait pas être une surprise! Tout d'abord, décomposons le programme.

Nous déclarons xcomme un pointeur vers un tableau de 5 éléments où chaque élément est de type pointeur vers int. Cette déclaration alloue 4 octets sur la pile d'exécution (ou plus selon votre implémentation; sur ma machine, les pointeurs sont de 4 octets), donc xfait référence à un emplacement mémoire réel. Dans la famille de langages C, le contenu de xn'est que des ordures, quelque chose qui reste d'une utilisation précédente de l'emplacement, donc xlui-même ne pointe nulle part - certainement pas vers l'espace alloué.

Donc, naturellement, nous pouvons prendre l'adresse de la variable xet la mettre quelque part, c'est donc exactement ce que nous faisons. Mais nous allons continuer et le mettre dans x lui-même. Puisque &xa un type différent de celui x, nous devons faire un casting pour ne pas recevoir d'avertissements.

Le modèle de mémoire ressemblerait à ceci:

0xbfd9198c
+------------+
| 0xbfd9198c |
+------------+

Ainsi, le bloc de mémoire de 4 octets à l'adresse 0xbfd9198ccontient le modèle de bits correspondant à la valeur hexadécimale 0xbfd9198c. Assez simple.

Ensuite, nous imprimons les trois valeurs. Les autres réponses expliquent à quoi chaque expression fait référence, donc la relation devrait être claire maintenant.

Nous pouvons voir que les valeurs sont les mêmes, mais seulement dans un sens de très bas niveau ... leurs modèles de bits sont identiques, mais le type de données associé à chaque expression signifie que leurs valeurs interprétées sont différentes. Par exemple, si nous avons imprimé en x[0][0][0]utilisant la chaîne de format%d , nous obtiendrions un nombre négatif énorme, donc les «valeurs» sont, en pratique, différentes, mais le modèle de bits est le même.

C'est en fait très simple ... dans les diagrammes, les flèches pointent simplement vers la même adresse mémoire plutôt que vers des adresses différentes. Cependant, alors que nous avons pu forcer un résultat attendu hors d'un comportement non défini, c'est juste cela - non défini. Ce n'est pas du code de production, mais simplement une démonstration par souci d'exhaustivité.

Dans une situation raisonnable, vous utiliserez mallocpour créer le tableau de 5 pointeurs int et à nouveau pour créer les entiers pointés dans ce tableau.mallocrenvoie toujours une adresse unique (sauf si vous n'avez plus de mémoire, auquel cas elle renvoie NULL ou 0), vous n'aurez donc jamais à vous soucier des pointeurs auto-référentiels comme celui-ci.

J'espère que c'est la réponse complète que vous recherchez. Vous ne devriez pas vous attendre x[0], x[0][0]et x[0][0][0]être égal, mais ils pourraient l'être si vous y êtes forcé. Si quelque chose vous a dépassé, faites-le moi savoir pour que je puisse clarifier!

Purag
la source
Je dirais qu'une autre utilisation étrange de pointeurs que j'ai jamais vue.
haccks du
@haccks Ouais, c'est assez bizarre, mais quand on le décompose, c'est tout aussi basique que les autres exemples. Il se trouve que c'est juste un cas où les modèles de bits sont tous les mêmes.
Purag du
Votre code provoque un comportement indéfini. x[0]ne représente pas réellement un objet valide du type correct
MM
@MattMcNabb c'est indéfini, et je suis très clair à ce sujet en fait. Je ne suis pas d'accord sur le type. xest un pointeur vers un tableau, nous pouvons donc utiliser l' []opérateur pour spécifier un décalage à partir de ce pointeur et le déréférencer. Qu'est-ce qui est étrange là-bas? Le résultat de x[0]est un tableau, et C ne se plaint pas si vous imprimez cela en utilisant %ppuisque c'est ainsi qu'il est implémenté en dessous de toute façon.
Purag
Et compiler cela avec le -pedanticdrapeau ne produit aucun avertissement, donc C est bien avec les types ...
Purag
0

Le type de int *(*x)[5]est int* (*)[5]ie un pointeur vers un tableau de 5 pointeurs vers des entiers.

  • xest l'adresse du premier tableau de 5 pointeurs vers les entiers (une adresse de type int* (*)[5])
  • x[0]l'adresse du premier tableau de 5 pointeurs vers ints (même adresse avec le type int* [5]) (offset adresse x par 0*sizeof(int* [5])ie index * taille-du-type-pointé et déréférencement)
  • x[0][0] est le premier pointeur vers un int dans le tableau (la même adresse avec le type int* ) (adresse de décalage x par 0*sizeof(int* [5])et déréférencement, puis par 0*sizeof(int*)et déréférence)
  • x[0][0][0]est le premier entier pointé par le pointeur vers un int (adresse de décalage x par 0*sizeof(int* [5])et déréférencement et décalage de cette adresse par 0*sizeof(int*)et déréférence et décalage de cette adresse par 0*sizeof(int)et déréférence)

Le type de int *(*y)[5][5][5]est int* (*)[5][5][5]ie un pointeur vers un tableau 3D de pointeurs 5x5x5 vers les entiers

  • x est l'adresse du premier tableau 3D de pointeurs 5x5x5 vers les entiers de type int*(*)[5][5][5]
  • x[0]est l'adresse du premier tableau 3D de pointeurs 5x5x5 vers ints (adresse de décalage x par 0*sizeof(int* [5][5][5])et déréférencement)
  • x[0][0]est l'adresse du premier tableau 2D de pointeurs 5x5 vers ints (adresse de décalage x de 0*sizeof(int* [5][5][5])et déréférencement puis décalage de cette adresse de 0*sizeof(int* [5][5]))
  • x[0][0][0]est l'adresse du premier tableau de 5 pointeurs vers les entiers (adresse de décalage x de 0*sizeof(int* [5][5][5])et déréférencement et décalage de cette adresse de 0*sizeof(int* [5][5])et décalage de cette adresse de 0*sizeof(int* [5]))
  • x[0][0][0][0]est le premier pointeur vers un entier dans le tableau (adresse de décalage x de 0*sizeof(int* [5][5][5])et déréférencement et décalage de cette adresse de 0*sizeof(int* [5][5])et décalage de cette adresse de0*sizeof(int* [5]) et décalage de cette adresse de 0*sizeof(int*)et déréférence)
  • x[0][0][0][0][0]est le premier entier pointé par le pointeur vers un int (adresse de décalage x de 0*sizeof(int* [5][5][5])et déréférencement et décalage de cette adresse 0*sizeof(int* [5][5])et décalage de cette adresse de 0*sizeof(int* [5])et décalage de cette adresse de 0*sizeof(int*)et déréférence et décalage de cette adresse de0*sizeof(int) et déréférence)

Quant à la désintégration du tableau:

void function (int* x[5][5][5]){
  printf("%p",&x[0][0][0][0]); //get the address of the first int pointed to by the 3d array
}

Cela équivaut à passer int* x[][5][5]ou int* (*x)[5][5]c'est -à- dire qu'ils se désintègrent tous vers ce dernier. C'est pourquoi vous n'obtiendrez pas d'avertissement du compilateur pour l'utilisation x[6][0][0]dans la fonction, mais vous le ferez x[0][6][0]parce que les informations de taille sont conservées

void function (int* (*x)[5][5][5]){
  printf("%p",&x[0][0][0][0][0]); //get the address of the first int pointed to by the 3d array
}
  • x[0] est l'adresse du premier tableau 3D de pointeurs 5x5x5 vers les entiers
  • x[0][0] est l'adresse du premier tableau 2D de pointeurs 5x5 vers les entiers
  • x[0][0][0] est l'adresse du premier tableau de 5 pointeurs vers les entiers
  • x[0][0][0][0] est le premier pointeur vers un int dans le tableau
  • x[0][0][0][0][0] est le premier int pointé par le pointeur vers un int

Dans le dernier exemple, il est sémantiquement beaucoup plus clair à utiliser *(*x)[0][0][0]que x[0][0][0][0][0], car le premier et le dernier [0]ici sont interprétés comme un déréférencement de pointeur plutôt qu'un index dans un tableau multidimensionnel, en raison du type. Ils sont cependant identiques car(*x) == x[0] quelle soit la sémantique. Vous pouvez également utiliser *****x, ce qui donnerait l'impression que vous déréférencer le pointeur 5 fois, mais il est en fait interprété exactement de la même manière: un décalage, une déréférence, une déréférence, 2 décalages dans un tableau et une déréférence, uniquement à cause du type vous appliquez l'opération à.

Essentiellement, lorsque vous [0]ou *un *à un type non tableau, il s'agit d'un décalage et d'une déréférence en raison de l'ordre de priorité de*(a + 0) .

Lorsque vous [0]ou* un *type de tableau, c'est un offset puis une déréférence idempotente (la déréférence est résolue par le compilateur pour donner la même adresse - c'est une opération idempotente).

Lorsque vous [0]ou* un type avec un type de tableau 1d, c'est un décalage puis une déréférence

Si vous [0]ou** un type de tableau 2D, il s'agit uniquement d'un décalage, c'est-à-dire d'un décalage puis d'un déréférencement idempotent.

Si vous [0][0][0]ou*** un type de tableau 3D, alors c'est un décalage + déréférencement idempotent puis un déréférencement offset + idempotent puis un déréférencement offset + idempotent puis un déréférencement. Le véritable déréférencement se produit uniquement lorsque le type de tableau est entièrement supprimé.

Pour l'exemple du int* (*x)[1][2][3]type est déroulé dans l'ordre.

  • x a un type int* (*)[1][2][3]
  • *xa un type int* [1][2][3](offset 0 + déréférencement idempotent)
  • **xa un type int* [2][3](offset 0 + déréférencement idempotent)
  • ***xa un type int* [3](offset 0 + déréférencement idempotent)
  • ****xa un type int*(offset 0 + déréférence)
  • *****xa le type int(décalage 0 + déréférence)
Lewis Kelsey
la source