112 votes

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 nécessite l'opérande d'avoir une modifiables objet de données: https://en.wikipedia.org/wiki/Increment_and_decrement_operators.

À partir de cela, je suppose qu'il donne de l'erreur de compilation car (a+b) est temporaire entier et n'est donc pas modifiable.

Est-ce la compréhension correcte? C'était ma première fois d'essayer une recherche sur un problème donc si il y avait quelque chose que je devrait avoir cherché veuillez en informer.

118voto

Bathsheba Points 23209

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

De manière informelle, en parlant, vous ne pouvez écrire ++foo si foo peut apparaître sur le côté gauche d'une expression d'affectation comme foo = bar. Puisque vous ne pouvez pas écrire a + b = bar, vous ne pouvez pas écrire ++(a + b) soit.

Il n'y a pas vraiment de raison pour a + b ne pouvait pas donner un temporaire de ce qui ++ peut fonctionner, et le résultat est la valeur de l'expression ++(a + b).

40voto

Christian Gibbons Points 2416

La norme C11 états dans la section 6.5.3.1

L'opérande de l'préfixe d'incrémentation ou de décrémentation exploitant doit avoir atomique, qualifiés ou non qualifiés réel ou de type pointeur, et doit être un modifiable lvalue

Et "modifiable lvalue" est décrit dans la section 6.3.2.1 paragraphe 1

Une lvalue est une expression (avec un objet de type autre que void) que potentiellement désigne un objet; si une lvalue ne désigne pas un objet lorsqu'il est évalué, le comportement est indéfini. Lorsqu'un un objet est dit d'avoir un type particulier, le type est spécifié par la lvalue utilisé pour désigner l'objet. Modifiable lvalue est une lvalue qui n'a pas de type tableau, n'a pas un type incomplète, ne pas avoir un const qualifiés de type, et s'il s'agit d'une structure ou d'une union, n'a pas de membre (y compris, de manière récursive, tout membre ou d'un élément de tous les contenus les agrégats ou les syndicats) avec un const qualifiés de type.

Donc, (a+b) n'est pas modifiable lvalue et n'est donc pas admissible pour le préfixe de l'opérateur d'incrémentation.

21voto

Roee Gavirel Points 4550

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

12voto

jgreve Points 931

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

Les modalités de la variable, d'un argument, variable temporaire et ainsi de suite deviendra plus clair lorsque vous vous renseignez sur C du modèle de mémoire (cela ressemble à une belle vue d'ensemble: https://www.geeksforgeeks.org/memory-layout-of-c-program/ ).

Le terme "rvalue" peut sembler opaque lorsque vous êtes juste de commencer, alors j'espère que le suivant aide à l'élaboration d'une intuition.

Lvalue/rvalue sont en train de parler sur les différents côtés du signe égal (opérateur d'affectation): lvalue = gauche (L minuscule, pas un "on") valeur r = à droite

Apprentissage un peu plus sur la façon dont C utilise de la mémoire (et enregistre) sera utile pour voir pourquoi la distinction est importante. Dans de larges coups de brosse, le compilateur crée une liste d'instructions en langage machine qui calcule le résultat de l'expression (la rvalue), puis met ce résultat quelque part (la lvalue). Imaginez un compilateur de traiter avec le fragment de code suivant:

x = y * 3

En assemblée de pseudo, il pourrait ressembler à quelque chose comme ce jouet exemple:

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 de ses -- homologue) besoin d'un "quelque part" pour modifier, essentiellement tout ce qui peut travailler comme une lvalue.

La compréhension de la C modèle de mémoire sera utile, car vous aurez ainsi une meilleure idée dans votre tête sur la façon dont les arguments sont passés à des fonctions et (éventuellement) la façon de travailler avec allocation dynamique de la mémoire, comme le malloc() de la fonction. Pour des raisons similaires, vous pouvez étudier un simple assemblage de programmation à un certain point pour avoir une meilleure idée de ce que le compilateur est en train de faire. Aussi, si vous utilisez gcc, le -S de l'option "Arrêter après l'étape de la compilation en elle-même, ne s'assemblent pas." peut être intéressante, mais je vous recommande de l'essayer sur un petit fragment de code).

Juste en aparté: Le ++ instruction a été autour depuis 1969 (bien qu'il a commencé en C prédécesseur, B):

(Ken Thompson) observation (a) que la traduction de ++x est plus petit que x=x+1."

Suivant que wikipedia référence vous amène à un intéressant article de Dennis Ritchie (le "R" en "K&R C") sur l'histoire du langage C, liée ici pour plus de commodité: http://www.bell-labs.com/usr/dmr/www/chist.html où vous pouvez rechercher pour "++".

6voto

Damon Points 26437

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

Maintenant, on pourrait dire "OK, c'est en effet la raison, mais il n'y a effectivement pas de *vrais* raison autre que cela", mais malheureusement le libellé de la façon dont l'opérateur travaille dans les faits n'a besoin que d'être le cas.

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

Évidemment, vous ne pouvez pas écrire E += 1 si E n'est pas une lvalue. Ce qui est dommage car on pourrait tout aussi bien dire: "incréments E par un" et être fait. Dans ce cas, l'application de l'opérateur sur un non-lvalue devrait (en principe) être parfaitement possible, au détriment de rendre le compilateur légèrement plus complexe.

Maintenant, la définition pourrait trivialement être reformulé (je pense qu'il n'est pas même à l'origine, mais C un héritage de B), mais cela changerait fondamentalement la langue à quelque chose qui n'est plus compatible avec les anciennes versions. Car l'avantage est plutôt petit mais les conséquences possibles sont énormes, ce n'est jamais arrivé et probablement ne va jamais se produire.

Si vous envisagez de C++ en plus de C (la question est marqué C, mais il y a une discussion à propos de la surcharge de l'opérateur), l'histoire devient encore plus compliqué. En C, il est difficile d'imaginer que cela pourrait être le cas, mais en C++ le résultat d' (a+b) pourrait très bien être quelque chose que vous ne pouvez pas incrément de tous, ou l'incrémentation pourraient avoir des effets secondaires importants (et pas seulement en ajoutant 1). Le compilateur doit être en mesure de faire face à cette situation, et de diagnostiquer les problèmes à mesure qu'ils surviennent. Sur une lvalue, c'est encore un peu trivial de vérifier. Pas pour toute sorte de hasard expression à l'intérieur d'une parenthèse que vous jetez à la pauvre chose.
Ce n'est pas une vraie raison pour laquelle il ne pouvait pas être fait, mais c'est sûr qu'il prête comme une explication de pourquoi les gens qui ont mis en oeuvre ce ne sont pas précisément enthousiastes à l'idée d'ajouter une telle fonctionnalité qui promet de très peu d'intérêt à très peu de gens.

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