Récemment, je suis tombé sur ce problème que je suis incapable de comprendre par moi-même.
Que signifient VRAIMENT ces trois expressions ?
*ptr++
*++ptr
++*ptr
J'ai essayé Ritchie. Mais malheureusement, il n'a pas pu suivre ce qu'il a dit sur ces 3 opérations.
Je sais qu'ils sont tous effectués pour incrémenter le pointeur / la valeur pointée. Je peux également deviner qu'il y a peut-être beaucoup de choses sur la préséance et l'ordre d'évaluation. Comme on incrémente d'abord le pointeur puis récupère le contenu de ce pointeur, on récupère simplement le contenu et ensuite incrémente le pointeur, etc. Comme vous pouvez le voir, je n'ai pas une compréhension claire de leurs opérations réelles , ce 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 qu'il s'imprime Hello
. Une dernière demande - Veuillez me donner des exemples du fonctionnement de chaque expression dans un extrait de code donné. Comme la plupart du temps, seul un simple paragraphe de théorie passe au-dessus de ma tête.
(*ptr)++
(les parenthèses sont nécessaires pour*ptr++
char* p
pointant vers une chaîne terminée valide de caractères uniques. Ensuite, ayez une fonctionfn(char ch)
qui imprime à la fois lech
paramètre et le caractère actuel pointés parp
. Invoquez maintenantfn(*p++);
Q:fn
imprime- t -il deux fois le même caractère ? Vous seriez étonné de voir combien de professeurs se trompent sur cette question.const char* p = "Hello";
Réponses:
Voici une explication détaillée qui, je l'espère, vous sera utile. Commençons par votre programme, car c'est le plus simple à expliquer.
La première déclaration:
déclare
p
comme un pointeur verschar
. Lorsque nous disons "pointeur vers unchar
", qu'est-ce que cela signifie? Cela signifie que la valeur dep
est l'adresse de achar
;p
nous indique où dans la mémoire il y a un espace réservé pour contenir un fichierchar
.L'instruction s'initialise également
p
pour pointer sur le premier caractère de la chaîne littérale"Hello"
. Pour les besoins de cet exercice, il est important de comprendrep
qu'il ne pointe pas vers la chaîne entière, mais uniquement vers le premier caractère'H'
,. Après tout,p
est un pointeur vers unchar
, pas vers la chaîne entière. La valeur dep
est l'adresse du'H'
in"Hello"
.Ensuite, vous configurez une boucle:
Que signifie la condition de boucle
*p++
? Trois choses sont à l'œuvre ici qui rendent cela déroutant (du moins jusqu'à ce que la familiarité s'installe):++
et indirection*
1. Préséance . Un rapide coup d'œil à la table de priorité des opérateurs vous indiquera que l'incrément de suffixe a une priorité plus élevée (16) que la déréférence / indirection (15). Cela signifie que l'expression complexe
*p++
va être regroupés comme suit:*(p++)
. C'est-à-dire que la*
pièce sera appliquée à la valeur de lap++
pièce. Alors commençons par lep++
rôle.2. Valeur de l'expression Postfix . La valeur de
p++
est la valeur d'p
avant l'incrément . Si tu as:la sortie sera:
car
i++
évaluei
avant l'incrément. De mêmep++
va évaluer la valeur actuelle dep
. Comme nous le savons, la valeur actuelle dep
est l'adresse de'H'
.Alors maintenant, la
p++
partie de*p++
a été évaluée; c'est la valeur actuelle dep
. Ensuite, la*
partie se produit.*(current value of p)
signifie: accéder à la valeur à l'adresse détenue parp
. Nous savons que la valeur à cette adresse est'H'
. Ainsi, l'expression est*p++
évaluée à'H'
.Maintenant, attendez une minute, dites-vous. Si
*p++
évalue à'H'
, pourquoi cela ne'H'
s'imprime pas dans le code ci-dessus? C'est là qu'interviennent les effets secondaires .3. Effets secondaires d'expression de Postfix . Le suffixe
++
a la valeur de l'opérande actuel, mais il a pour effet secondaire d'incrémenter cet opérande. Hein? Jetez à nouveau un coup d'œil à ceint
code:Comme indiqué précédemment, la sortie sera:
Quand
i++
est évalué dans le premierprintf()
, il évalue à 7. Mais le standard C garantit qu'à un moment donné avant que le second neprintf()
commence à s'exécuter, l' effet secondaire de l'++
opérateur aura eu lieu. C'est-à-dire, avant que le second neprintf()
se produise,i
aura été incrémenté du fait de l'++
opérateur dans le premierprintf()
. C'est d'ailleurs l'une des rares garanties que la norme donne sur le moment des effets secondaires.Dans votre code, lorsque l'expression
*p++
est évaluée, elle est évaluée à'H'
. Mais au moment où vous arrivez à ceci:cet effet secondaire embêtant s'est produit.
p
a été incrémenté. Whoa! Il ne pointe plus vers'H'
, mais vers un personnage passé'H'
: vers le'e'
, en d'autres termes. Cela explique votre sortie cockneyfied:D'où le refrain 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 commeTellement pour ça. Qu'en est-il du reste? Vous demandez la signification de ces derniers:
Nous venons de parler de la première, alors regardons la seconde:
*++ptr
.Nous avons vu dans notre explication précédente que l'incrément de suffixe
p++
a une certaine priorité , une valeur et un effet secondaire . L'incrément de préfixe++p
a le même effet secondaire que son homologue postfixe: il incrémente son opérande de 1. Cependant, il a une priorité et une valeur différentes .L'incrément de préfixe a une priorité inférieure à celle du suffixe; il a la priorité 15. En d'autres termes, il a la même priorité que l'opérateur de déréférence / indirection
*
. Dans une expression commece qui compte, ce n'est pas la priorité: les deux opérateurs sont identiques en priorité. L' associativité entre donc en jeu. L'incrément de préfixe et l'opérateur d'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 le plus à gauche*
,. En d'autres termes, l'expression va être groupée*(++ptr)
. Donc, comme pour*ptr++
mais pour une raison différente, ici aussi la*
pièce va être appliquée à la valeur de la++ptr
pièce.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émentation postfix. Disons que vous avez:
La sortie sera:
... différent de ce que nous avons vu avec l'opérateur postfix. De même, si vous avez:
la sortie sera:
Voyez-vous pourquoi?
Maintenant , nous arrivons à la troisième expression que vous avez parlé,
++*ptr
. C'est le plus délicat du lot, en fait. Les deux opérateurs ont la même priorité et la même associativité droite-gauche. Cela signifie que l'expression sera groupée++(*ptr)
. La++
pièce sera appliquée à la valeur de la*ptr
pièce.Donc, si nous avons:
le résultat étonnamment égoïste va être:
Quoi?! Ok, donc la
*p
partie va être évaluée'H'
. Ensuite, le++
entre en jeu, à quel point, il va être appliqué au'H'
pointeur, pas 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. certifiez que commechar
, et vous obtenez lachar
avec la valeur ASCII de 73:'I'
.Cela prend en compte les trois expressions que vous avez posées dans votre question. En voici une autre, mentionnée dans le premier commentaire de votre question:
Celui-là est intéressant aussi. Si tu as:
cela vous donnera cette sortie enthousiaste:
Que se passe-t-il? Encore une fois, c'est une question de priorité , de valeur d'expression et d' effets secondaires . En raison des parenthèses, la
*p
pièce 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, évalue à'H'
. Le reste de l'expression, la++
partie, est appliqué à cette valeur. Donc, dans ce cas,(*p)++
devient'H'++
.Quelle est la valeur de
'H'++
? Si vous avez dit'I'
, vous avez oublié (déjà!) Notre discussion sur la valeur par rapport aux effets secondaires avec incrément de suffixe. N'oubliez pas,'H'++
évalue à la valeur actuelle de'H'
. Donc, ce premierprintf()
va être imprimé'H'
. Ensuite, comme effet secondaire , cela'H'
va être incrémenté'I'
. Le second l'printf()
imprime'I'
. Et vous avez votre joyeux salut.D'accord, mais dans ces deux derniers cas, pourquoi ai-je besoin
Pourquoi ne puis-je pas avoir quelque chose comme
Parce que
"Hello"
est une chaîne littérale. Si vous essayez++*p
, vous essayez de changer le'H'
dans la chaîne en'I'
, créant ainsi toute la chaîne"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
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 lesp
points par pré- ou post-incrémentation ou décrémentation, car le nom du tableau fonctionne comme s'il s'agissait d'un pointeur constant. (Ce n'est pas ce que c'est réellement; c'est juste un moyen pratique de le regarder.)Pour résumer, voici les trois choses que vous avez posées:
Et en voici un quatrième, tout aussi amusant que les trois autres:
Le premier et le second planteront s'il
ptr
s'agit en fait d'un identifiant de tableau. Les troisième et quatrième planteront si ellesptr
pointent vers une chaîne littérale.Voilà. J'espère que tout est en cristal maintenant. Vous avez été un public formidable et je serai ici toute la semaine.
la source
Supposons que
ptr
pointe vers le i-ème élément du tableauarr
.*ptr++
évaluearr[i]
et définitptr
pour pointer vers le (i + 1) -ème élément dearr
. C'est équivalent à*(ptr++)
.*++ptr
définitptr
pour pointer vers le (i + 1) -ème élément dearr
et s'évalue versarr[i+1]
. C'est équivalent à*(++ptr)
.++*ptr
augmentearr[i]
de un et évalue à sa valeur accrue; le pointeurptr
n'est pas touché. C'est équivalent à++(*ptr)
.Il y en a aussi un autre, mais vous aurez besoin de parenthèses pour l'écrire:
(*ptr)++
augmentearr[i]
de un et s'évalue à sa valeur avant d'être augmentée; le pointeurptr
est à nouveau laissé intact.Le reste, vous pouvez le découvrir vous-même; il a également été répondu par @Jaguar.
la source
*ptr++ : post increment a pointer ptr
*++ptr : Pre Increment a pointer ptr
++*ptr : preincrement the value at ptr location
Lisez ici les opérateurs de pré-incrémentation et de post-incrémentation
Cela donnera
Hello
comme sortiela source
Hello
La condition dans votre boucle est mauvaise:
Est le même que
Et c'est faux, cela devrait être:
*ptr++
est le même que*(ptr++)
, qui est:*++ptr
est le même que*(++ptr)
, qui est:++*ptr
est le même que++(*ptr)
, qui est:la source
Vous avez raison sur la priorité, notez que le
*
a la priorité sur l'incrément de préfixe, mais pas sur l'incrément de suffixe. Voici comment ces ventilations:*ptr++
- en allant 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émenter le pointeur puis le déréférencer, car le préfixe et le déréférencement ont la même priorité et sont donc évalués dans l'ordre de droite à gauche++*ptr
- similaire à ce qui précède en termes de priorité, en allant à nouveau de droite à gauche pour déréférencer le pointeur, puis incrémenter ce sur quoi le pointeur pointe. Veuillez noter que dans votre cas, celui-ci entraînera un comportement non défini car vous essayez de modifier une variable en lecture seule (char* p = "Hello";
).la source
Je vais ajouter mon point de vue parce que si les autres réponses sont correctes, je pense qu'il leur manque quelque chose.
veux dire
Tandis que
veux dire
Il est important de comprendre que post incrémentation (et post décrémentation) signifie
Pourquoi est-ce important? Eh bien en C ce n'est pas si important. En C ++, cela
ptr
peut cependant être un type complexe comme un itérateur. Par exempleDans ce cas, parce que
it
c'est un type complexeit++
peut-être avoir des effets secondaires en raison de latemp
création. Bien sûr, si vous avez de la chance, le compilateur essaiera de jeter du code qui n'est pas nécessaire, mais si le constructeur ou le destructeur de l'itérateur fait quelque chose, ilit++
affichera ces effets lors de sa créationtemp
.Le court de ce que j'essaie de dire est Écrivez ce que vous voulez dire . Si vous voulez dire incrémenter ptr, n'écrivez
++ptr
pasptr++
. Si tu veux diretemp = ptr, ptr += 1, temp
alors écrisptr++
la source
C'est la même chose que:
Ainsi, la valeur de l'objet pointé par
ptr
est récupérée, puisptr
incrémentée.C'est la même chose que:
Ainsi, le pointeur
ptr
est incrémenté, puis l'objet pointé parptr
est lu.C'est la même chose que:
Ainsi, l'objet pointé par
ptr
est incrémenté;ptr
lui-même est inchangé.la source
postfix et prefix ont une priorité plus élevée que la déréférence, donc
* ptr ++ ici post incrémentation ptr puis pointant vers la nouvelle valeur de ptr
* ++ ptr ici Pre Increment fist puis pointant vers la nouvelle valeur de ptr
++ * ptr ici obtient d'abord la valeur de ptr pointant vers et incrémente cette valeur
la source
Expressions de pointeur: * ptr ++, * ++ ptr et ++ * ptr:
Remarque : les pointeurs doivent être initialisés et avoir une adresse valide. Parce que dans la RAM en dehors de notre programme (a.out), il y a beaucoup plus de programmes en cours d'exécution simultanément, c'est-à-dire que si vous essayez d'accéder à une mémoire qui n'était pas réservée à votre système d'exploitation, cela se produira par erreur de segmentation.
Avant d'expliquer cela, considérons un exemple simple?
analyser la sortie du code ci-dessus, j'espère que vous avez obtenu la sortie du code ci-dessus. Une chose est claire à partir du code ci-dessus est que le nom du pointeur ( ptr ) signifie que nous parlons d' adresse et que * ptr signifie que nous parlons d'abbout valeur / données.
CAS 1 : * ptr ++, * ++ ptr, * (ptr ++) et * (++ ptr):
Les 4 syntaxes mentionnées ci-dessus sont similaires,
address gets incremented
mais la façon dont l'adresse est incrémentée est différente.Remarque : pour résoudre une expression, découvrez combien d'opérateurs il y a dans l'expression, puis déterminez les priorités de l'opérateur. I plusieurs opérateurs ayant la même priorité vérifient alors l'ordre d'évolution ou d' associativité qui peut de droite (R) à gauche (L) ou de gauche à droite.
* ptr ++ : Ici 2 opérateurs sont là à savoir la de-reference (*) et ++ (increment). Les deux ont la même priorité, puis vérifiez l'associativité qui est de R à L.Commence donc à résoudre de droite à gauche, quels que soient les opérateurs qui viennent en premier.
* ptr ++ : first ++ est venu lors de la résolution de R à L, donc l'adresse est incrémentée mais son post-incrémentation.
* ++ ptr : Identique au premier ici aussi l'adresse est incrémentée mais son pré-incrémentation.
* (ptr ++) : Ici, il y a 3 opérateurs, parmi eux grouping () ayant la priorité la plus élevée, donc d'abord ptr ++ résolu c'est-à-dire que l'adresse est incrémentée mais postée.
* (++ ptr) : Identique au cas ci-dessus, ici aussi l'adresse est incrémentée mais pré-incrémentée.
CAS 2 : ++ * ptr, ++ (* ptr), (* ptr) ++:
mentionné ci-dessus, les 4 syntaxes sont similaires, en toutes les valeurs / données sont incrémentées, mais la façon dont la valeur est modifiée est différente.
++ * ptr : first * est venu lors de la résolution de R à L, donc la valeur est modifiée mais son pré-incrémentation.
++ (* ptr) : Identique au cas ci-dessus, la valeur est modifiée.
(* ptr) ++ : Ici, 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 c'est-à-dire que la valeur est incrémentée mais postée.
Remarque : ++ * ptr et * ptr = * ptr + 1 sont tous les deux identiques, dans les deux cas, la valeur est modifiée. ++ * ptr: une seule instruction (INC) est utilisée, la valeur est directement modifiée en un seul coup. * ptr = * ptr + 1: ici la première valeur est incrémentée (INC) puis assignée (MOV).
Pour comprendre tout ce qui précède, la syntaxe d'incrémentation sur le pointeur, considérons un code simple:
Dans le code ci-dessus, essayez de commenter / dé-commenter les commentaires et d'analyser les sorties.
Les pointeurs comme constants : il n'y a pas de moyens par lesquels vous pouvez rendre les pointeurs aussi constants, peu que je mentionne ici.
1) const int * p OR int const * p : Ici
value
est constante , l' adresse n'est pas constante c'est- à- dire où p pointe? Une adresse? Sur cette adresse quelle est la valeur? Une certaine valeur, non? Cette valeur est constante, vous ne pouvez pas modifier cette valeur mais où pointe le pointeur? Une adresse non? Il peut également pointer vers une autre adresse.Pour comprendre cela, considérons le code ci-dessous:
Essayez d'analyser la sortie du code ci-dessus
2) int const * p : il s'appelle '
**constant pointe**r
' ieaddress is constant but value is not constant
. Ici, vous n'êtes pas autorisé à modifier l'adresse mais vous pouvez modifier la valeur.Remarque : le pointeur constant (cas ci-dessus) doit s'initialiser lorsqu'il est déclarié.
Pour comprendre cela, vérifions un code simple.
Dans le code ci-dessus, si vous observez qu'il n'y a pas ++ * p ou * p ++ Vous pouvez penser que c'est un cas simple car nous ne changeons pas d'adresse ou de valeur mais cela produira une erreur. Pourquoi ? Raison que je mentionne dans les commentaires.
Alors, quelle est la solution de ce problème?
pour en savoir plus sur ce cas, considérons l'exemple ci-dessous.
3) const int * const p : ici l'adresse et la valeur sont constantes .
Pour comprendre cela, vérifions le code ci-dessous
la source
++*p
signifie que vous essayez d'incrémenter la valeur ASCII*p
dontvous ne pouvez pas incrémenter la valeur car c'est une constante, vous obtiendrez donc une erreur
quant à votre boucle while, la boucle s'exécute jusqu'à ce qu'elle
*p++
atteigne la fin de la chaîne où il y a un caractère'\0'
(NULL).Maintenant que vous
*p++
ignorez le premier caractère, vous n'obtiendrez votre sortie qu'à partir du deuxième caractère.Le code suivant ne produira rien car la boucle while a
'\0'
Le code suivant vous donnera la même sortie que le code suivant, c'est-à-dire ello.
...................................
la source