Récemment, j'ai rencontré ce problème que je suis incapable de comprendre par moi-même.
Que signifient ces trois expressions VRAIMENT?
*ptr++
*++ptr
++*ptr
J'ai essayé Ritchie. Mais malheureusement, il n'a pas pu suivre ce qu'il a dit à propos de ces opérations.
Je sais qu'ils sont tous effectués pour incrémenter le pointeur/la valeur indiquée. J'imagine aussi qu'il peut y avoir beaucoup de choses concernant la priorité et l'ordre d'évaluation. Comme on incrémente d'abord le pointeur, puis extrait le contenu de ce pointeur, on extrait simplement le contenu, puis incrémente le pointeur, etc. etc. Comme vous pouvez le constater, je ne comprends pas bien leurs opérations réelles , que je voudrais effacer dès que possible. Mais je suis vraiment perdu quand j'ai la chance de les appliquer dans des programmes. Par exemple:
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
me donne cette sortie:
Ello
Mais je m'attendais à ce que cela affiche Hello
. Une dernière demande - Donnez-moi des exemples de la manière dont chaque expression fonctionne dans un extrait de code donné. Comme la plupart du temps, seul un simple paragraphe de théorie me survole.
Voici une explication détaillée qui, je l’espère, sera utile. Commençons par votre programme, car c’est le plus simple à expliquer.
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
La première déclaration:
const char* p = "Hello";
déclare p
comme un pointeur sur char
. Quand on dit "pointeur sur une char
", qu'est-ce que cela signifie? Cela signifie que la valeur de p
est l'adresse d'un char
; p
nous indique où, dans la mémoire, un espace est réservé pour contenir un char
.
L'instruction initialise également p
pour pointer vers le premier caractère de la chaîne littérale "Hello"
. Pour les besoins de cet exercice, il est important de comprendre p
comme pointant non pas vers la chaîne entière, mais uniquement vers le premier caractère, 'H'
. Après tout, p
est un pointeur sur un char
, pas sur la chaîne entière. La valeur de p
est l'adresse du 'H'
dans "Hello"
.
Ensuite, vous créez une boucle:
while (*p++)
Que signifie la condition de boucle *p++
? Trois choses sont au travail ici qui rendent cette énigme (au moins jusqu'à ce que la familiarité s'installe):
++
et indirection *
1. Priorité. Un coup d’œil rapide sur la table de priorité pour les opérateurs vous dira que l’incrémentation postfixée a une priorité plus élevée (16) que dereference/indirection (15). Cela signifie que l'expression complexe *p++
va être regroupée comme suit: *(p++)
. C'est-à-dire que la partie *
sera appliquée à la valeur de la partie p++
. Prenons donc la partie p++
en premier.
2. Valeur d'expression Postfix. La valeur de p++
est la valeur de p
avant l’incrément. Si tu as:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
la sortie sera:
7
8
parce que i++
est évalué à i
avant l'incrément. De même, p++
va évaluer la valeur actuelle de p
. Comme nous le savons, la valeur actuelle de p
est l'adresse de 'H'
.
Alors maintenant, la partie p++
de *p++
a été évaluée; c'est la valeur actuelle de p
. Ensuite, la partie *
se produit. *(current value of p)
signifie: accéder à la valeur à l'adresse contenue par p
. Nous savons que la valeur à cette adresse est 'H'
. Donc, l'expression *p++
est évaluée à 'H'
.
Maintenant, attendez une minute, vous dites. Si *p++
est évalué à 'H'
, pourquoi est-ce que 'H'
ne s'imprime pas dans le code ci-dessus? C'est là que effets secondaires entrent en jeu.
. Effets secondaires de l'expression postfixée. Le suffixe ++
a le valeur de l'opérande en cours, mais il a le effet secondaire de l'incrémenter. Hein? Examinez à nouveau le code int
:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
Comme indiqué précédemment, le résultat sera:
7
8
Lorsque i++
est évalué dans la première printf()
, il est évalué à 7. Mais le standard C garantit que, avant la seconde printf()
commence à s'exécuter, le effet secondaire de l'opérateur ++
aura eu lieu. C'est-à-dire qu'avant que la seconde printf()
ne se produise, i
aura été incrémenté à la suite de l'opérateur ++
dans la première printf()
. C'est d'ailleurs l'une des rares garanties données par la norme sur le calendrier des effets secondaires.
Dans votre code, donc, lorsque l'expression *p++
est évaluée, elle est évaluée à 'H'
. Mais au moment où vous arrivez à ceci:
printf ("%c", *p)
cet effet secondaire embêtant s'est produit. p
a été incrémenté. Whoa! Il ne désigne plus 'H'
, mais un caractère après 'H'
: au 'e'
, autrement dit. Cela explique votre sortie cockneyfied:
Ello
D'où le choeur de suggestions utiles (et précises) dans les autres réponses: pour imprimer la prononciation reçue "Hello"
et non son homologue cockney, vous avez besoin de quelque chose comme:
while (*p)
printf ("%c", *p++);
Tellement pour ça. Qu'en est-il du reste? Vous posez des questions sur la signification de celles-ci:
*ptr++
*++ptr
++*ptr
Nous venons de parler du premier, regardons donc le second: *++ptr
.
Nous avons vu dans notre précédente explication que l'incrément postfixé p++
avait un certain précédence, un valeur et un effet secondaire. Le préfixe incrément ++p
a le même effet secondaire que son équivalent postfix: il incrémente son opérande de 1. Cependant, il a un différent precedence et un autre - valeur.
L'incrément de préfixe a une priorité inférieure à celle de postfixe; il a la priorité 15. En d'autres termes, il a la même priorité que l'opérateur de déréférencement/indirection *
. Dans une expression comme
*++ptr
ce qui compte, ce n’est pas la priorité: les deux opérateurs sont identiques en priorité. Donc associativité entre en action. L'incrément de préfixe et l'opérateur indirection ont une associativité droite-gauche. En raison de cette associativité, l'opérande ptr
va être groupé avec l'opérateur le plus à droite ++
avant l'opérateur plus à gauche, *
. En d'autres termes, l'expression va être groupée *(++ptr)
. Donc, comme avec *ptr++
mais pour une raison différente, ici aussi la partie *
sera appliquée à la valeur de la partie ++ptr
.
Alors, quelle est cette valeur? La valeur de l'expression d'incrément de préfixe est la valeur de l'opérande après l'incrément. Cela en fait une bête très différente de l'opérateur d'incrément de postfix. Disons que vous avez:
int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);
La sortie sera:
8
8
... différent de ce que nous avons vu avec l'opérateur postfix. De même, si vous avez:
const char* p = "Hello";
printf ("%c ", *p); // note space in format string
printf ("%c ", *++p); // value of ++p is p after the increment
printf ("%c ", *p++); // value of p++ is p before the increment
printf ("%c ", *p); // value of p has been incremented as a side effect of p++
la sortie sera:
H e e l // good dog
Tu vois pourquoi?
Nous arrivons maintenant à la troisième expression sur laquelle vous avez posé la question, ++*ptr
. C'est le plus délicat du lot, en fait. Les deux opérateurs ont la même priorité et une associativité droite-gauche. Cela signifie que l'expression sera groupée ++(*ptr)
. La partie ++
sera appliquée à la valeur de la partie *ptr
.
Donc si nous avons:
char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);
le résultat étonnamment égoïste va être:
I
Quoi?! Ok, la partie *p
va donc être évaluée à 'H'
. Ensuite, le ++
entre en jeu. À ce stade, il sera appliqué au 'H'
, pas au pointeur du tout! Que se passe-t-il lorsque vous ajoutez 1 à 'H'
? Vous obtenez 1 plus la valeur ASCII de 'H'
, 72; vous obtenez 73. Représentez cela en tant que char
, et vous obtenez le char
avec la valeur ASCII de 73: 'I'
.
Cela répond aux trois expressions que vous avez posées dans votre question. Voici un autre, mentionné dans le premier commentaire à votre question:
(*ptr)++
Celui-là est intéressant aussi. Si tu as:
char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);
cela vous donnera cette sortie enthousiaste:
HI
Que se passe-t-il? Encore une fois, il s’agit de précédence, valeur d’expression, et effets secondaires. En raison des parenthèses, la partie *p
est traitée comme une expression principale. Les expressions primaires l'emportent sur tout le reste; ils sont évalués en premier. Et *p
, comme vous le savez, est évalué à 'H'
. Le reste de l'expression, la partie ++
, est appliquée à cette valeur. Donc, dans ce cas, (*p)++
devient 'H'++
.
Quelle est la valeur de 'H'++
? Si vous avez dit 'I'
, vous avez déjà oublié notre discussion sur la valeur par rapport aux effets secondaires avec incrémentation postfixée. Rappelez-vous que 'H'++
est évalué à valeur actuelle de'H'
. Donc, cette première printf()
va imprimer 'H'
. Ensuite, en tant que effet secondaire, ce 'H'
va être incrémenté de 'I'
. La seconde printf()
imprime ce 'I'
. Et vous avez votre salut joyeux.
D'accord, mais dans ces deux derniers cas, pourquoi ai-je besoin
char q[] = "Hello";
char* p = q;
Pourquoi je ne peux pas juste avoir quelque chose comme
/*const*/ char* p = "Hello";
printf ("%c", ++*p); // attempting to change string literal!
Parce que "Hello"
est un littéral de chaîne. Si vous essayez ++*p
, vous essayez de remplacer le 'H'
dans la chaîne par 'I'
, pour obtenir la chaîne complète "Iello"
. En C, les littéraux de chaîne sont en lecture seule; tenter de les modifier appelle un comportement indéfini. "Iello"
n'est pas défini en anglais également, mais ce n'est qu'une coïncidence.
Inversement, vous ne pouvez pas avoir
char p[] = "Hello";
printf ("%c", *++p); // attempting to modify value of array identifier!
Pourquoi pas? Parce que dans ce cas, p
est un tableau. Un tableau n'est pas une valeur l modifiable; vous ne pouvez pas changer où p
pointe par incrémentation ou décrémentation préalable ou postérieure, car le nom du tableau fonctionne comme s'il s'agissait d'un pointeur constant. (Ce n'est pas ce que c'est en réalité; c'est juste un moyen pratique de le regarder.)
En résumé, voici les trois questions que vous avez posées:
*ptr++ // effectively dereferences the pointer, then increments the pointer
*++ptr // effectively increments the pointer, then dereferences the pointer
++*ptr // effectively dereferences the pointer, then increments dereferenced value
Et voici un quatrième, tout aussi amusant que les trois autres:
(*ptr)++ // effectively forces a dereference, then increments dereferenced value
La première et la seconde planteront si ptr
est en fait un identifiant de tableau. Les troisième et quatrième planteront si ptr
pointe sur un littéral de chaîne.
Voilà. J'espère que tout est en cristal maintenant. Vous avez été un excellent public et je serai là toute la semaine.
Supposons que ptr
pointe sur le i-ème élément du tableau arr
.
*ptr++
est évalué à arr[i]
et définit ptr
pour qu'il pointe vers le (i + 1) -ième élément de arr
. Cela équivaut à *(ptr++)
.
*++ptr
définit ptr
pour qu'il pointe vers le (i + 1)-ème élément de arr
et est évalué à arr[i+1]
. Cela équivaut à *(++ptr)
.
++*ptr
augmente arr[i]
de un et est évalué à sa valeur accrue; le pointeur ptr
est laissé intact. Cela équivaut à ++(*ptr)
.
Il y en a aussi un autre, mais vous aurez besoin de parenthèses pour l'écrire:
(*ptr)++
augmente arr[i]
de un et est évalué à sa valeur avant d'être augmenté; le pointeur ptr
est à nouveau laissé intact.Le reste, vous pouvez trouver vous-même; @Jaguar a également répondu à cette question.
*ptr++ : post increment a pointer ptr
*++ptr : Pre Increment a pointer ptr
++*ptr : preincrement the value at ptr location
Lire ici à propos des opérateurs de pré-incrémentation et de post-incrémentation
Cela donnera Hello
en sortie
int main()
{
const char *p = "Hello";
while(*p)
printf("%c",*p++);//Increment the pointer here
return 0;
}
La condition dans votre boucle est mauvaise:
while(*p++)
printf("%c",*p);
Est le même que
while(*p)
{
p++;
printf("%c",*p);
}
Et c'est faux, cela devrait être:
while(*p)
{
printf("%c",*p);
p++;
}
*ptr++
est identique à *(ptr++)
, qui est:
const char *ptr = "example";
char value;
value = *ptr;
++ptr;
printf("%c", value); // will print 'e'
*++ptr
est identique à *(++ptr)
, qui est:
const char *ptr = "example";
char value;
++ptr;
value = *ptr;
printf("%c", value); // will print 'x'
++*ptr
est identique à ++(*ptr)
, qui est:
const char *ptr = "example";
char value;
value = *ptr;
++value;
printf("%c", value); // will print 'f' ('e' + 1)
Vous avez raison en ce qui concerne la priorité, notez que le *
a la priorité sur l’incrément de préfixe, mais pas sur l’incrément de postfix. Voici comment ces ventilations:
*ptr++
- aller de gauche à droite, déréférencer le pointeur, puis incrémenter la valeur du pointeur (pas ce à quoi il pointe, en raison de la priorité de postfix sur le déréférencement)
*++ptr
- incrémente le pointeur puis le déréférence, car les préfixes et les déréférences ont la même priorité et sont donc évalués dans l'ordre, de droite à gauche.
++*ptr
- similaire au précédent en termes de priorité, allant encore de droite à gauche dans l'ordre pour déréférencer le pointeur, puis incrémenter ce à quoi le pointeur pointe. Veuillez noter que dans votre cas, cela entraînera un comportement indéfini car vous essayez de modifier une variable en lecture seule (char* p = "Hello";
).
Je vais ajouter mon point de vue car, même si les autres réponses sont correctes, je pense qu’il manque quelque chose.
v = *ptr++
veux dire
temp = ptr;
ptr = ptr + 1
v = *temp;
Tandis que
v = *++ptr
veux dire
ptr = ptr + 1
v = *ptr
Il est important de comprendre que post incrément (et post décrément) signifie
temp = ptr // Temp created here!!!
ptr = ptr + 1 // or - 1 if decrement)
v = *temp // Temp destroyed here!!!
Pourquoi est-ce important? Eh bien en C ce n'est pas si important. En C++, cependant, ptr
peut être un type complexe comme un itérateur. Par exemple
for (std::set<int>::iterator it = someSet.begin(); it != someSet.end(); it++)
Dans ce cas, étant donné que it
est un type complexe, it++
peut avoir des effets secondaires en raison de la création de temp
. Bien sûr, si vous avez de la chance, le compilateur essaiera de jeter le code inutile, mais si le constructeur ou le destructeur de l'itérateur fait quelque chose, it++
affichera ces effets lorsqu'il créera temp
.
Le bref de ce que j'essaie de dire est écris ce que tu veux dire . Si vous voulez dire incrémenter ptr alors écrivez ++ptr
et non ptr++
. Si vous voulez dire temp = ptr, ptr += 1, temp
alors écrivez ptr++
*ptr++ // 1
C'est pareil que:
tmp = *ptr;
ptr++;
Ainsi, la valeur de l'objet pointé par ptr
est récupérée, puis ptr
est incrémenté.
*++ptr // 2
C'est pareil que:
++ptr;
tmp = *ptr;
Ainsi, le pointeur ptr
est incrémenté, puis l'objet pointé par ptr
est lu.
++*ptr // 3
C'est pareil que:
++(*ptr);
Donc, l'objet pointé par ptr
est incrémenté; ptr
est inchangé.
postfixe et préfixe a une priorité plus élevée que déréférencement donc
* ptr ++ ici post increment ptr puis pointant vers la nouvelle valeur de ptr
* ++ ptr here Pre Increment poing puis pointant vers la nouvelle valeur de ptr
++ * ptr ici en premier obtiennent la valeur de ptr pointant et incrémentant celle-ci
Expressions de pointeur: * ptr ++, * ++ ptr et ++ * ptr:
Note: les pointeurs doivent être initialisés et doivent avoir une adresse valide. Parce que dans RAM, mis à part notre programme (a.out), il y a beaucoup plus de programmes exécutés simultanément, c'est-à-dire que si vous essayez d'accéder à de la mémoire qui ne vous a pas été réservée, la défaillance de la segmentation le fera.
Avant d’expliquer cela, considérons un exemple simple?
#include<stdio.h>
int main()
{
int num = 300;
int *ptr;//uninitialized pointer.. must be initialized
ptr = #
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
*ptr = *ptr + 1;//*ptr means value/data on the address.. so here value gets incremented
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
/** observe here that "num" got changed but manually we didn't change, it got modified by pointer **/
ptr = ptr + 1;//ptr means address.. so here address got incremented
/** char pointer gets incremented by 1 bytes
Integer pointer gets incremented by 4 bytes
**/
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}
analyser la sortie du code ci-dessus, j'espère que vous avez obtenu la sortie du code ci-dessus. Une chose est claire du code ci-dessus est que le nom du pointeur (ptr) signifie que nous parlons de adresse et * ptr signifie que nous parlons de valeur/data.
CASE 1: * ptr ++, * ++ ptr, * (ptr ++) et * (++ ptr):
les 4 syntaxes mentionnées ci-dessus sont similaires, dans tout address gets incremented
, mais la façon dont l'adresse est incrémentée est différente.
Note: pour résoudre une expression, déterminez le nombre d'opérateurs présents dans l'expression, puis recherchez priorités d'opérateur. Plusieurs opérateurs ayant la même priorité vérifient ensuite l'ordre d'évolution ou l'associativité pouvant aller de droite (R) à gauche (L) ou de gauche à droite.
* ptr ++: ici 2 opérateurs sont à savoir de-référence (*) et ++ (incrément). Les deux ont la même priorité, puis vérifiez l'associativité qui va de R à L. Alors commence à résoudre de droite à gauche, quel que soit l'opérateur qui vient en premier.
* ptr ++: le premier ++ est venu lors de la résolution de R à L, l'adresse est donc incrémentée mais sa post-incrémentation.
* ++ ptr: comme le premier, l'adresse est incrémentée mais son incrémentation préalable.
* (ptr ++): Ici, il y a 3 opérateurs, parmi eux grouping () ayant la priorité la plus élevée. Donc, le premier ptr ++ résolu, c'est-à-dire que l'adresse est incrémentée mais postée.
* (++ ptr): Identique à la casse ci-dessus, l'adresse est également incrémentée mais pré-incrémentée.
CASE 2: ++ * ptr, ++ (* ptr), (* ptr) ++:
les 4 syntaxes mentionnées ci-dessus sont similaires, dans toutes les valeurs/données sont incrémentées, mais la valeur des modifications est différente.
++ * ptr: le premier * est apparu lors de la résolution de R à L, la valeur est modifiée mais son incrémentation préalable.
++ (* ptr): comme dans le cas précédent, la valeur est modifiée.
(* ptr) ++: il y a 3 opérateurs, parmi eux grouping () ayant la priorité la plus élevée, Inside () * ptr est là, donc le premier * ptr est résolu lorsque la valeur est incrémentée mais post.
Note: ++ * ptr et * ptr = * ptr + 1 sont identiques, dans les deux cas, la valeur est modifiée . ++ * ptr: une seule instruction (INC) est utilisée, la valeur est modifiée directement en une seule fois . * ptr = * ptr + 1: ici la première valeur est incrémentée (INC) puis assignée (MOV).
Pour comprendre toutes les syntaxes d’incrémentation ci-dessus différentes sur le pointeur, considérons un code simple:
#include<stdio.h>
int main()
{
int num = 300;
int *ptr;
ptr = #
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
*ptr++;//address changed(post increment), value remains un-changed
// *++ptr;//address changed(post increment), value remains un-changed
// *(ptr)++;//address changed(post increment), value remains un-changed
// *(++ptr);//address changed(post increment), value remains un-changed
// ++*ptr;//value changed(pre increment), address remains un-changed
// (*ptr)++;//value changed(pre increment), address remains un-changed
// ++(*ptr);//value changed(post increment), address remains un-changed
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}
Dans le code ci-dessus, essayez de commenter/dé-commenter des commentaires et d'analyser les sorties.
Pointeurs en tant que constants: il n’existe aucun moyen de créer des pointeurs aussi constants. Quelques-uns que je mentionne ici.
1) const int * p OR int const * p: Ici value
est constant, adresse n'est pas constante i.e où p pointe-t-il? Une adresse? Sur cette adresse, quelle est la valeur? Une valeur non? Cette valeur est constante, vous ne pouvez pas modifier cette valeur mais où le pointeur pointe-t-il? Une adresse non? Il peut aussi indiquer une autre adresse.
Pour comprendre cela, considérons le code ci-dessous:
#include<stdio.h>
int main()
{
int num = 300;
const int *ptr;//constant value, address is modifible
ptr = #
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
*ptr++;//
// *++ptr;//possible bcz you are trying to change address which is possible
// *(ptr)++;//possible
// *(++ptr);//possible
// ++*ptr;//not possible bcz you trying to change value which is not allowed
// (*ptr)++;//not possible
// ++(*ptr);//not possible
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}
Essayez d'analyser la sortie du code ci-dessus
2) int const * p: cela s'appelle '**constant pointe**r
' i.e address is constant but value is not constant
. Ici, vous n'êtes pas autorisé à changer l'adresse, mais vous pouvez modifier la valeur.
Note: le pointeur constant (au-dessus de la casse) doit être initialisé pendant la déclinaison elle-même.
Pour comprendre cela, vérifions le code simple.
#include<stdio.h>
int main()
{
int x = 300;
int* const p;
p = &x;
printf("x = %d p =%p and *p = %d\n",num,p,*p);
}
Dans le code ci-dessus, si vous constatez qu'il n'y a pas de ++ * p ou * p ++, vous pouvez donc penser qu'il s'agit d'un cas simple, car nous ne changeons pas d'adresse ni de valeur, mais cela produira une erreur. Pourquoi ? Raison je mentionne dans les commentaires.
#include<stdio.h>
int main()
{
int x = 300;
/** constant pointer must initialize while decaring itself **/
int* const p;//constant pointer i.e its pointing to some address(here its pointing to garbage), it should point to same address(i.e garbage ad
dress only
p = &x;// but here what we are doing ? we are changing address. we are making p to point to address of x instead of garbage address.
printf("x = %d p =%p and *p = %d\n",num,p,*p);
}
Alors, quelle est la solution de ce problème?
int* const p = &x;
pour en savoir plus sur ce cas, considérons l'exemple ci-dessous.
#include<stdio.h>
int main()
{
int num = 300;
int *const ptr = #//constant value, address is modifible
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
*ptr++;//not possible
// *++ptr;//not possible bcz you are trying to change address which is not possible
// *(ptr)++;//not possible
// *(++ptr);//not possible
// ++*ptr;// possible bcz you trying to change value which is allowed
// (*ptr)++;// possible
// ++(*ptr);// possible
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}
3) const int * const p: Ici l'adresse et la valeur sont constantes.
Pour comprendre cela, vérifions le code ci-dessous
#include<stdio.h>
int main()
{
int num = 300;
const int* const ptr = #//constant value,constant address
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
*ptr++;//not possible
++*ptr;//not possible
printf(" num = %d ptr = %p and data on ptr : %d \n",num,ptr,*ptr);
}