70 votes

La différence entre C et C++ en ce qui concerne le ++ opérateur

J'ai été faire les fous avec un peu de code et a vu quelque chose que je ne comprends pas le "pourquoi" de la.

int i = 6;
int j;

int *ptr = &i;
int *ptr1 = &j

j = i++;

//now j == 6 and i == 7. Straightforward.

Que faire si vous mettez de l'opérateur sur le côté gauche du signe d'égalité?

++ptr = ptr1;

est équivalent à

(ptr = ptr + 1) = ptr1; 

alors que

ptr++ = ptr1;

est équivalent à

ptr = ptr + 1 = ptr1;

Le postfix fonctionne une erreur de compilation et je le comprends. Vous avez une constante "ptr + 1" sur le côté gauche d'un opérateur d'affectation. Juste assez.

Le préfixe on compile et FONCTIONNE en C++. Oui, je comprends, c'est salissant et vous avez affaire à de la mémoire non allouée, mais il fonctionne et compile. En C, cela ne compile pas, le retour de la même erreur que le suffixe "lvalue requis comme opérande de gauche de mission". Ce qui se passe, peu importe comment il est écrit, élargi avec deux "=" opérateurs ou avec le "++ptr" syntaxe.

Quelle est la différence entre la façon dont les C gère une telle cession et comment C++ gère?

73voto

T.C. Points 22510

En C et C++, le résultat d' x++ est une rvalue, de sorte que vous ne pouvez pas lui attribuer.

En C, ++x est équivalent à x += 1 (C standard §6.5.3.1/p2; tous les C standard cites sont à WG14 N1570). En C++, ++x est équivalent à x += 1 si x n'est pas un bool (C++ standard §5.3.2 [expr.pré.incr]/p1; tous C++ standard de la cites sont à WG21 N3936).

En C, le résultat d'une expression d'affectation est une rvalue (C standard §6.5.16/p3):

Un opérateur d'affectation stocke une valeur dans l'objet désigné par le opérande de gauche. Une expression d'affectation a la valeur de la gauche opérande après l'affectation, mais n'est pas une lvalue.

Parce que ce n'est pas une lvalue, vous ne pouvez pas affecter: (C standard §6.5.16/p2)

Un opérateur d'affectation doit avoir une modifiables lvalue comme sa gauche l'opérande.

En C++, le résultat d'une expression d'affectation est une lvalue (C++ standard §5.17 [expr.cul]/p1):

L'opérateur d'affectation (=) et le composé des opérateurs d'assignation de tous les groupe de droite à gauche. Tous exigent une modifiables lvalue comme leur gauche l'opérande et le retour d'une lvalue se référant à l'opérande de gauche.

Donc, ++ptr = ptr1; est syntaxiquement correcte en C++ mais pas en C.

Cependant, avant C++11, ++ptr = ptr1; a un comportement indéfini, car il modifie ptr deux fois entre deux images de la séquence de points.

En C++11, le comportement de l' ++ptr = ptr1 devient bien définis. C'est plus clair si nous réécrire sous la forme

(ptr += 1) = ptr1;

Le post-C++11 standard C++ fournit (§5.17 [expr.cul]/p1)

Dans tous les cas, la cession est séquencé après le calcul de la valeur de la droite et de la gauche opérandes, et avant que la valeur de calcul de l'expression d'affectation. À l'égard d'un pour une période indéterminée-séquencé appel de la fonction, le fonctionnement d'un composé mission d'évaluation unique.

Donc, la cession effectuée par l' = est séquencé après la valeur de calcul de l' ptr += 1 et ptr1. La mission effectuée par l' += est séquencée avant de la valeur de calcul de ptr += 1, et tous les calculs requis par l' += sont nécessairement séquencée avant cette affectation. Ainsi, le séquençage ici est bien défini et il n'est pas un comportement indéfini.

17voto

Shafik Yaghmour Points 42198

En C, le résultat de la pré-et post-incrémentation sont rvalues et nous ne pouvons pas attribuer à une rvalue, nous avons besoin d'une lvalue(voir aussi: la Compréhension des lvalues et rvalues en C et C++) . On peut voir en allant sur le projet de norme C11 section 6.5.2.4 Postfix d'incrémentation et de décrémentation les opérateurs qui le dit (c'est moi qui souligne à l'avenir):

Le résultat de la postfix ++ opérateur est la valeur de la l'opérande. [...] Voir la discussion de l'additif opérateurs et composé cession pour plus d'informations sur les contraintes, les types, et les conversions et les effets des opérations sur les pointeurs. [...]

Donc le résultat de la post-incrémentation est une valeur qui est synonyme pour rvalue et nous pouvons confirmer cela en allant à la section 6.5.16 des opérateurs d'Affectation dont le paragraphe ci-dessus nous montre à approfondir la compréhension des contraintes et des résultats, il dit:

[...] Une expression d'affectation a la valeur de l'opérande gauche après le d'affectation, mais n'est pas une lvalue.[...]

ce qui confirme le résultat de la post-incrémentation n'est pas une lvalue.

Pour la pré-incrémentation, nous pouvons voir à partir de la section 6.5.3.1 Préfixe d'incrémentation et de décrémentation les opérateurs qui dit:

[...]Voir la discussion de l'additif opérateurs et composé d'affectation pour informations sur les contraintes, les types, les effets secondaires, et les conversions et les effets des opérations sur les pointeurs.

également des points de 6.5.16 comme la post-incrémentation ne et, par conséquent, le résultat de la pré-incrémentation dans C est pas une lvalue.

En C++ post-incrémentation est aussi une rvalue, plus précisément un prvalue nous pouvons confirmer cela en allant à la section 5.2.6 d'Incrémentation et de décrémentation qui dit:

[...]Le résultat est un prvalue. Le type du résultat est la cv-sans réserve version du type de l'opérande[...]

À l'égard de la pré-incrémentation de C et de C++ diffèrent. En C, le résultat est une rvalue alors qu'en C++ le résultat est une lvalue ce qui explique pourquoi ++ptr = ptr1; fonctionne en C++ mais pas en C.

Pour le C++ c'est couvert dans la section 5.3.2 d'Incrémentation et de décrémentation qui dit:

[...]Le résultat est la mise à jour de l'opérande; c'est une lvalue, et c'est un peu-champ si l'opérande est un peu de champ.[...]

Pour comprendre si:

++ptr = ptr1;

est bien définie ou non en C++, nous avons besoin de deux approches différentes pour la C++11) et un pour le C++11.

Avant C++11 cette expression appelle un comportement indéfini, puisque c'est la modification de l'objet plus d'une fois au sein de la même séquence de point. Nous pouvons voir cela à un Pré C++11 projet de norme section 5 Expressions qui dit:

Sauf indication contraire, l'ordre d'évaluation des opérandes de l'individu les opérateurs et les sous-expressions des expressions individuelles, et de l'ordre dans les effets secondaires qui prendre place, est indéterminé.57) Entre le précédent et suivant de la séquence de point un scalaire objet est stocké valeur modifiée au plus une fois par l'évaluation d'une expression. En outre, l'état de la valeur doit être consulté pour déterminer le valeur à stocker. Les exigences de ce paragraphe sont remplies pour chaque admissibles de la commande de la sous-expressions d'un plein l'expression, sinon le comportement est indéfini. [ Exemple:

 i = v[i ++]; / / the behavior is undefined
 i = 7 , i++ , i ++; / / i becomes 9
 i = ++ i + 1; / / the behavior is undefined
 i = i + 1; / / the value of i is incremented

fin de l'exemple ]

Nous sommes incrémentation ptr , puis par la suite en lui attribuant, qui est d'apporter deux modifications et dans ce cas, la séquence se produit à la fin de l'expression après l' ;.

Pour C+11, nous devrions aller de rapport de défaut 637: des règles d'ordonnancement et de l'exemple de désaccord qui a été le rapport de défaut qui a abouti à:

i = ++i + 1;

devient de plus en plus, les comportements définis en C++11, alors qu'avant C++11 c'était un comportement indéfini. L'explication dans ce rapport est l'un des meilleurs que j'ai vu et le lire à plusieurs reprises était instructif et m'a aidé à comprendre beaucoup de concepts dans une nouvelle lumière.

La logique qui conduisent à cette expression devient ainsi défini comportement va comme suit:

  1. L'affectation d'effets secondaires est nécessaire pour être séquencés après la valeur des calculs à la fois de ses LHS et RHS (5.17 [expr.cul] l'alinéa 1).

  2. Le LHS (i) est une lvalue, donc sa valeur de calcul consiste à calculer l'adresse de je.

  3. Pour la valeur de calcul de la RHS (++i + 1), il est nécessaire d'abord de la valeur de calcul de la lvalue expression ++je puis faire une lvalue-à-rvalue de conversion sur le résultat. Cela garantit que l'incrémentation d'effets secondaires est séquencée avant le calcul de l'opération d'addition, qui à son tour est séquencée avant l'assignation des effets secondaires. En d'autres termes, il donne un ordre précis et la valeur finale de cette expression.

La logique est quelque peu similaire pour:

++ptr = ptr1;
  1. La valeur des calculs de la LHS et RHS sont séquencée avant l'assignation des effets secondaires.

  2. Le membre de droite est une lvalue, donc sa valeur de calcul consiste à calculer l'adresse de ptr1.

  3. Pour la valeur de calcul de la LHS (ptr++), il est nécessaire d'abord de la valeur de calcul de la lvalue expression ptr ++et ensuite faire une lvalue-à-rvalue de conversion sur le résultat. Cela garantit que l'incrémentation d'effets secondaires est séquencée avant l'assignation des effets secondaires. En d'autres termes, il donne un ordre précis et la valeur finale de cette expression.

Note

L'OP a dit:

Oui, je comprends, c'est salissant et vous avez à traiter avec des non alloué de mémoire, mais il fonctionne et compile.

Les pointeurs de tableau non les objets sont considérés comme des tableaux de taille un additif pour les opérateurs, je vais me permettre de citer le projet de norme C++ mais C11 a presque exactement le même texte. À partir de la section 5.7 Additif opérateurs:

Pour l'application de ces opérateurs, un pointeur vers un objet nonarray se comporte comme un pointeur vers le premier élément d'un tableau de de longueur avec le type de l'objet comme type d'élément.

et plus nous dit pointant vers un passé la fin d'un tableau est valide aussi longtemps que vous n'avez pas déréférencer le pointeur:

[...]Si le pointeur de l'opérande et le résultat pointez sur les éléments de le même objet de tableau, ou un au-delà du dernier élément du tableau l'objet, l'évaluation ne doit pas produire un dépassement de capacité; sinon, l' le comportement est indéfini.

donc:

++ptr ;

est toujours un pointeur valide.

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