28 votes

Expressions de pointeurs : Utilisation de **ptr++, *++*ptr et ++**ptr

Je m'essaie à la littérature sur les pointeurs C. Dans l'une des illustrations, j'ai rencontré le code suivant.

# include <stdio.h>

int main()
{
     static int a[]={0,1,2,3,4};
     static int *p[]={a, a+1, a+2, a+3, a+4};

     int **ptr;

     ptr =p;

     **ptr++;
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     *++*ptr; 
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     ++**ptr;
     printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

     return 0;
}

Je reçois la sortie comme.

1 1 1
1 2 2
1 2 3

Je rencontre un problème pour justifier cette sortie. J'ai fait beaucoup de cases sur une copie pour faciliter la compréhension du problème. Je suis capable de justifier le résultat 1 1 1 mon problème commence avec la déclaration, *++*ptr .

En effet, les opérateurs unaires sont exécutés de droite à gauche. Donc, *ptr serait abordé en premier, puis la valeur à ptr serait incrémenté. Après cet incrément, je ne suis pas sûr de ce qui se passe, le livre dit que d'une manière ou d'une autre p est également incrémenté pour pointer vers le prochain élément de ce tableau. La sortie 1 2 2 ne peut être atteint que par l'augmentation de p .

Je ne suis pas sûr que ce genre de question ait sa place sur stackoverflow.
J'ai fait de mon mieux, j'ai gâché au moins 10 pages avec des cases dessinées dessus.

Toute clarification serait appréciée.

49voto

Grijesh Chauhan Points 28442

Rappelez-vous que le nom d'un tableau peut facilement se transformer en pointeur vers le premier élément dans la plupart des expressions (lire quelques exceptions où le nom du tableau ne se transforme pas en un pointeur vers le premier élément ? bien répondu par @ H 2 CO 3 ).
Pour une meilleure compréhension, considérez mes diagrammes :

Premièrement, supposons a stockées en mémoire comme suit.

  a 
+----+----+----+----+---+
|  0 |  1 | 2  | 3  | 4 |
+----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲
  |    |    |    |    | 
  a    a+1  a+2  a+3  a+3

Déclaration static int *p[] = {a, a+1, a+2, a+3, a+4}; crée un nouveau tableau de pointeurs vers des entiers, avec les valeurs suivantes :

p[0] == a
p[1] == a + 1
p[2] == a + 2
p[3] == a + 3
p[4] == a + 4

Maintenant, p peut également être supposé être stocké en mémoire, comme ci-dessous :

  p
+----+----+----+----+-----+
| a  |a +1| a+2| a+3| a+4 | 
+----+----+----+----+-----+
  ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |
  p    p+1  p+2  p+3  p+4

Après l'affectation ptr = p; les choses seront quelque chose comme ça :

  p                              a 
+----+----+----+----+-----+    +----+----+----+----+---+
| a  |a +1| a+2| a+3| a+4 |    |  0 |  1 | 2  | 3  | 4 |
+----+----+----+----+-----+    +----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |          |    |    |    |    | 
  p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
  ptr 

Notice: ptr points to first location in pointer array p[]

Expression : **ptr++ ;

Nous considérons maintenant l'expression **ptr++; avant la première instruction printf.

  1. ptr est égal à p qui est l'adresse du premier élément du tableau de pointeurs. Par conséquent, ptr pointe vers le premier élément p[0] dans un tableau (ou on peut dire ptr == &p[0] ).

  2. *ptr signifie p[0] et parce que p[0] est a donc *ptr est a ( donc *ptr == a ).

  3. Et parce que *ptr est a alors **ptr est *a == *(a + 0) == a[0] c'est-à-dire 0 .

  4. Note dans l'expression **ptr++; nous n'attribuons pas sa valeur à la variable lhs.
    Ainsi, l'effet de **ptr++; est simplement identique à ptr++; == ptr = ptr + 1 = p + 1
    De cette façon, après cette expression ptr pointant vers p[1] (ou on peut dire ptr == &p[1] ).

Imprimer-1 :

Avant le premier printf, les choses deviennent :

  p                              a 
+----+----+----+----+-----+    +----+----+----+----+---+
| a  | a+1| a+2| a+3| a+4 |    |  0 |  1 | 2  | 3  | 4 |
+----+----+----+----+-----+    +----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |          |    |    |    |    | 
  p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
       ptr 

Notice: ptr is equals to  p + 1 that means it points to p[1]

Maintenant nous pouvons comprendre Premier printf :

  1. ptr - p sortie 1 parce que :
    ptr = p + 1 donc ptr - p == p + 1 - p == 1

  2. *ptr - a sortie 1 parce que :
    ptr = p + 1 donc *ptr == *(p + 1) == p[1] == a + 1
    Cela signifie : *ptr - a = a + 1 - a == 1

  3. **ptr sortie 1 parce que :
    *ptr == a + 1 du point-2
    Alors **ptr == *(a + 1) == a[1] == 1

Expression : *++*ptr ;

Après le premier printf, nous avons une expression *++*ptr; .

Comme nous savons du point 2 ci-dessus que *ptr == p[1] . Donc, ++*ptr (c'est-à-dire ++p[1] ) incrémentera p[1] à a + 2

Comprendre à nouveau, dans l'expression *++*ptr; nous n'assignons pas sa valeur à une variable lhs donc l'effet de *++*ptr; est juste ++*ptr; .

Maintenant, avant le deuxième printf, les choses deviennent :

  p                              a 
+----+----+----+----+-----+    +----+----+----+----+---+
| a  |a+2 | a+2| a+3| a+4 |    |  0 |  1 | 2  | 3  | 4 |
+----+----+----+----+-----+    +----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |          |    |    |    |    | 
  p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
       ptr 

Notice: p[1] became a + 2 

Imprimer-2 :

Maintenant nous pouvons comprendre Deuxièmement printf :

  1. ptr - p sortie 1 parce que :
    ptr = p + 1 donc ptr - p == p + 1 - p == 1

  2. *ptr - a sortie 2 parce que :
    ptr = p + 1 donc *ptr == *(p + 1) == p[1] == a + 2
    Cela signifie : *ptr - a == a + 2 - a == 2

  3. **ptr sortie 2 parce que :
    *ptr == a + 2 du point-2
    Alors **ptr == *(a + 2) == a[2] == 2

Expression : ++**ptr ;

Expression actuelle ++**ptr; avant le troisième printf.

Comme nous savons du point 3 ci-dessus que **ptr == a[2] . Donc ++**ptr == ++a[2] incrémentera a[2] à 3

Donc avant le troisième printf les choses deviennent :

  p                              a 
+----+----+----+----+-----+    +----+----+----+----+---+
| a  | a+2| a+2| a+3| a+4 |    |  0 |  1 | 3  | 3  | 4 |
+----+----+----+----+-----+    +----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |          |    |    |    |    | 
  p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
       ptr 

 Notice: a[2] = 3

Imprimé 3 :

Maintenant nous pouvons comprendre Troisièmement printf :

  1. ptr - p sortie 1 parce que :
    ptr = p + 1 donc ptr - p == p + 1 - p == 1

  2. *ptr - a sortie 2 parce que :
    ptr = p + 1 donc *ptr == *(p + 1) == p[1] == a + 2
    Cela signifie : *ptr - a = a + 2 - a == 2

  3. **ptr sorties 3 parce que :
    *ptr == a + 2 du point-2
    Alors **ptr == *(a + 2) == a[2] == 3

Modifier Note : La différence de deux pointeurs est de type ptrdiff_t et pour cela, le spécificateur de conversion correct est %td pas %d .

Un point supplémentaire :
Je souhaite ajouter que je crois que cela sera utile pour les nouveaux apprenants.

Supposons que nous ayons les deux lignes suivantes avec un 4 de plus th printf dans votre code avant return 0;

**++ptr;    // additional 
printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);  // fourth printf

On peut vérifier ce code de travail @ Codepade cette ligne produit 2 2 3 .

Expression : **++ptr ;

Parce que ptr est égal à p + 1 après l'incrément ++ opération ptr devient p + 2 (ou on peut dire ptr == &p[2] ).
Après cette opération de double déférence ** \==> **(p + 2) == *p[2] == *(a + 2) == a[2] == 3 .
Maintenant, encore une fois parce que nous n'avons pas d'opération d'affectation dans cette déclaration, l'effet de l'expression **++ptr; est juste ++ptr; .

Donc, chose après expression **++ptr; devient comme ci-dessous dans la figure :

  p                              a 
+----+----+----+----+-----+    +----+----+----+----+---+
| a  | a+2| a+2| a+3| a+4 |    |  0 |  1 | 3  | 3  | 4 |
+----+----+----+----+-----+    +----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲          ▲    ▲    ▲    ▲    ▲
  |    |    |    |    |          |    |    |    |    | 
  p    p+1  p+2  p+3  p+4        a    a+1  a+2  a+3  a+3
            ptr 

 Notice: ptr is equals to  p + 2 that means it points to p[2] 

Impression 4 :

Envisager Forth printf que j'ai ajouté en question :

  1. ptr - p sortie 2 parce que :
    ptr = p + 2 donc ptr - p == p + 2 - p == 2

  2. *ptr - a sortie 2 parce que :
    ptr = p + 2 donc *ptr == *(p + 2) == p[2] == a + 2
    Cela signifie : *ptr - a = a + 2 - a == 2

  3. **ptr sorties 3 parce que :
    *ptr == a + 2 à partir du point 2 ci-dessus
    Alors **ptr == *(a + 2) == a[2] == 3

8voto

Carl Norum Points 114072

Si vous compilez avec quelques avertissements (clang n'a même pas exigé de drapeaux), vous verrez que votre programme a trois éléments étrangers. * opérateurs. En simplifiant vos expressions les plus folles, vous obtenez :

ptr++;
++*ptr;
++**ptr;

Et à partir de là, vous devriez être en mesure de voir ce qui se passe assez clairement :

  1. ptr++ juste des incréments ptr donc il pointe vers le deuxième élément de p . Après cette opération ptr - p sera toujours 1 .

  2. ++*ptr incrémente ce qui est pointé par ptr . Cela change le deuxième élément de p pour pointer vers le troisième élément de a plutôt que la seconde (à laquelle il a été initialisé). Cela fait de *ptr - a égal à 2 . De même, **ptr est le 2 de a .

  3. ++**ptr incrémente ce qui est pointé par ce qui est pointé par ptr . Cela incrémente le troisième élément de a ce qui en fait un 3 .

2voto

Kevin Points 23308

Rappelez-vous que ++ a une priorité plus élevée que * donc quand vous le faites **ptr++ qui incrémente le pointeur et déréférence l'ancienne valeur, ce qui ne fera rien d'autre que de se planter si ce n'est pas un pointeur-à-pointeur valide, et votre compilateur (au moins avec les avertissements activés) devrait vous avertir d'un résultat inutilisé.

 static int a[]={0,1,2,3,4};

 static int *p[]={a, a+1, a+2, a+3, a+4};

 int **ptr;

 ptr = p; // ptr = &(p[0]); *ptr = a; **ptr = 0.

 **ptr++; // ptr = &(p[1]); *ptr = a+1; **ptr = 1
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

 *++*ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 2; p = {a, a+2, a+2, a+3, a+4} 
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

 ++**ptr; // ptr = &(p[1]); *ptr = a+2; **ptr = 3; a = {0, 1, 3, 3, 4}
 printf("%d %d %d\n", ptr-p, *ptr-a, **ptr);

2voto

abiessu Points 1252

El int* à l'adresse ptr a été incrémenté par la déclaration *++*ptr; (en fait par le ++*ptr la partie principale * est une déréférence inutilisée). Ainsi, l'expansion de int *p[] devrait maintenant ressembler à ceci :

int *p[]={a, a+2, a+2, a+3, a+4};

La finale ++**ptr; a maintenant incrémenté la valeur à l'adresse a+2 Le tableau original ressemblera donc à ceci :

int a[]={0,1,3,3,4};

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X