web-dev-qa-db-fra.com

Pourquoi c = ++ (a + b) donne-t-il une erreur de compilation?

Après des recherches, j'ai lu que l'opérateur d'incrémentation a besoin que l'opérande ait un objet de données modifiable: https://en.wikipedia.org/wiki/Increment_and_decrement_operators .

À partir de cela, je suppose que cela donne une erreur de compilation car (a+b) est un entier temporaire et n’est donc pas modifiable.

Cette compréhension est-elle correcte? C’était la première fois que j’essayais de rechercher un problème. Par conséquent, si je devais rechercher quelque chose, veuillez le signaler.

111
dng

C’est juste une règle, c’est tout et il est peut-être là pour (1) faciliter l’écriture des compilateurs C et (2) personne n’a convaincu le comité de normalisation C de l’assouplir.

De manière informelle, vous ne pouvez écrire ++foo que si foo peut apparaître à gauche d'une expression d'affectation telle que foo = bar. Puisque vous ne pouvez pas écrire a + b = bar, vous ne pouvez pas écrire ++(a + b).

Il n'y a pas de vraie raison pour que a + b ne puisse pas générer un temporaire sur lequel ++ puisse fonctionner, et le résultat obtenu est la valeur de l'expression ++(a + b).

117
Bathsheba

La norme C11 stipule à la section 6.5.3.1

L'opérande de l'opérateur d'incrément ou de décrément de préfixe doit être de type atomique, qualifié ou non qualifié, réel ou de type pointeur, et doit être une valeur modifiable.

Et "valeur modifiable" est décrite à la section 6.3.2.1, sous-section 1

Une lvalue est une expression (avec un type d'objet autre que void) qui désigne potentiellement un objet; si une lvalue ne désigne pas un objet lors de son évaluation, le comportement est indéfini. Lorsqu'un objet est dit avoir un type particulier, le type est spécifié par la lvalue utilisée pour désigner l'objet. ne lvalue modifiable est une lvalue qui n’a pas de type tableau, n’a pas de type incomplet, n’a pas de type qualifié const et, s’il s’agit d’une structure ou d’une union, n’a aucun membre (y compris, récursivement, tout membre ou élément de tous les agrégats ou unions contenus) avec un type qualifié de const.

Donc, (a+b) n'est pas une valeur modifiable et n'est donc pas éligible pour l'opérateur d'incrément de préfixe.

40
Christian Gibbons

Vous avez raison. le ++ essaie d'affecter la nouvelle valeur à la variable d'origine. Donc, ++a prendra la valeur de a, y ajoute 1, puis le réattribue à a. Comme, comme vous l'avez dit, (a + b) est une valeur temporaire et non une variable avec une adresse mémoire assignée, l'affectation ne peut pas être effectuée.

21
Roee Gavirel

Je pense que vous avez surtout répondu à votre propre question. Je pourrais faire un petit changement à votre formulation et remplacer "variable temporaire" par "rvalue" comme C.Gibbons mentionné.

Les termes variable, argument, variable temporaire, etc., deviendront plus clairs au fur et à mesure que vous apprendrez le modèle de mémoire de C (cela ressemble à un aperçu de Nice: https://www.geeksforgeeks.org/memory-layout-of- programme c / ).

Le terme "rvalue" peut sembler opaque lorsque vous débutez, j'espère donc que ce qui suit vous aidera à développer une intuition à ce sujet.

Lvalue/rvalue parlent des différents côtés d’un signe égal (opérateur d’affectation): lvalue = côté gauche (L minuscule, pas un "un") rvalue = côté droit

Apprendre un peu sur la façon dont C utilise la mémoire (et les registres) sera utile pour comprendre pourquoi la distinction est importante. Dans larges coups de pincea, le compilateur crée une liste d'instructions en langage machine qui calculent le résultat d'une expression (la valeur rvalue), puis met ​​ce résultat quelque part (la valeur lvalue). Imaginez un compilateur traitant du fragment de code suivant:

x = y * 3

Dans Assembly, le pseudocode pourrait ​​ressembler à cet exemple de jouet:

load register A with the value at memory address y
load register B with a value of 3
multiply register A and B, saving the result in A
write register A to memory address x

L'opérateur ++ (et son homologue) a besoin d'un "quelque part" pour modifier, essentiellement tout ce qui peut fonctionner comme valeur.

Comprendre le modèle de mémoire C sera utile car vous aurez une meilleure idée de la façon dont les arguments sont transmis aux fonctions et (éventuellement) comment utiliser l'allocation de mémoire dynamique, comme la fonction malloc (). Pour des raisons similaires, vous pourriez étudier un peu la programmation d'assembly simple à un moment donné pour avoir une meilleure idée de ce que fait le compilateur. Aussi, si vous utilisez gcc , l’option - S "Arrêter après la phase de compilation proprement dite, ne pas assembler ". peut être intéressant (bien que je vous recommande de l'essayer sur un fragment de code petit).

En passant: l'instruction ++ existe depuis 1969 (bien qu'elle ait commencé dans le prédécesseur de C, B):

(Ken Thompson) a observé (était) que la traduction de ++ x était inférieure à celle de x = x + 1. "

Suite à cette référence dans wikipedia, Dennis Ritchie (le "R" dans "K & R C") vous présentera un article intéressant sur l'histoire du langage C, lié ici par commodité: http: //www.bell- labs.com/usr/dmr/www/chist.html où vous pouvez rechercher "++".

12
jgreve

La raison en est que la norme exige que l'opérande soit une valeur. L'expression (a+b) n'est pas une lvalue. L'application de l'opérateur d'incrémentation n'est donc pas autorisée.

Maintenant, on pourrait dire "OK, c'est bien la raison, mais il n'y a pas de * vraie * raison autre que celle-là", mais malheureusement, le libellé particulier de la manière dont l'opérateur fonctionne de manière factuelle ne exiger que ce soit le cas.

L'expression ++ E est équivalente à (E + = 1).

Bien évidemment, vous ne pouvez pas écrire E += 1 si E n'est pas une valeur. Ce qui est dommage car on aurait tout aussi bien pu dire: "incrémente E d'un" et être fait. Dans ce cas, appliquer l'opérateur sur une valeur autre que la valeur serait (en principe) tout à fait possible, au détriment de la complexité du compilateur.

Maintenant, la définition pourrait être trivialement reformulée (je pense que ce n'est même pas à l'origine C mais un héritage de B), mais cela changerait fondamentalement le langage en quelque chose qui n'est plus compatible avec ses versions antérieures. Étant donné que le bénéfice possible est plutôt faible mais que les implications possibles sont énormes, cela ne s'est jamais produit et ne se produira probablement jamais.

Si vous considérez C++ en plus de C (la question est étiquetée C, mais il y a eu des discussions sur les surcharges d'opérateurs), l'histoire devient encore plus compliquée. En C, il est difficile d’imaginer que cela puisse être le cas, mais en C++, le résultat de (a+b) pourrait très bien ne pas être incrémenté du tout, ou l’incrémentation pourrait avoir des effets secondaires très importants (non seulement en ajoutant 1 ). Le compilateur doit pouvoir faire face à cela et diagnostiquer les cas problématiques au fur et à mesure qu'ils surviennent. Sur une lvalue, c'est toujours un peu trivial à vérifier. Ce n'est pas le cas pour toute sorte d'expression au hasard à l'intérieur d'une parenthèse que vous jetez à la pauvre.
Ce n'est pas une vraie raison pour laquelle ne pourrait pas être fait, mais cela donne certainement comme explication pourquoi les personnes qui l'ont implémenté ne sont pas précisément extatique d'ajouter une telle fonctionnalité qui promet très peu d'avantages à très peu de gens.

6
Damon

++ essaie de donner la valeur à la variable d'origine et puisque (a + b) est une valeur temporaire, il ne peut pas effectuer l'opération. Et ce sont fondamentalement les règles des conventions de programmation en C qui facilitent la programmation. C'est ça.

3
Babu Chandermani

(a + b) correspond à une valeur rvalue, qui ne peut pas être incrémentée.

3
Casper B. Hansen

Lorsque ++ (a + b) expression est effectuée, alors par exemple:

int a, b;
a = 10;
b = 20;
/* NOTE :
 //step 1: expression need to solve first to perform ++ operation over operand
   ++ ( exp );
// in your case 
   ++ ( 10 + 20 );
// step 2: result of that inc by one 
   ++ ( 30 );
// here, you're applying ++ operator over constant value and it's invalid use of ++ operator 
*/
++(a+b);
2
Jeet Parikh