Après avoir lu cette réponse sur le comportement indéfini et les points de séquence, j'ai écrit un petit programme:
#include <stdio.h>
int main(void) {
int i = 5;
i = (i, ++i, 1) + 1;
printf("%d\n", i);
return 0;
}
La sortie est 2
. Oh mon Dieu, je n'ai pas vu venir le décrément! Que se passe-t-il ici?
De plus, lors de la compilation du code ci-dessus, j'ai reçu un avertissement disant:
px.c: 5: 8: avertissement: l'opérande gauche de l'expression virgule n'a aucun effet
[-Wunused-value] i = (i, ++i, 1) + 1; ^
Pourquoi? Mais il sera probablement répondu automatiquement par la réponse à ma première question.
printf("2\n");
Réponses:
Dans l'expression
(i, ++i, 1)
, la virgule utilisée est l' opérateur virguleParce qu'il rejette son premier opérande, il n'est généralement utile que lorsque le premier opérande a des effets secondaires souhaitables . Si l'effet secondaire du premier opérande ne se produit pas, le compilateur peut générer un avertissement sur l'expression sans effet.
Ainsi, dans l'expression ci-dessus, l'extrême gauche
i
sera évalué et sa valeur sera ignorée. Ensuite++i
sera évalué et incrémentéi
de 1 et à nouveau la valeur de l'expression++i
sera ignorée, mais l'effet secondairei
est permanent . Ensuite1
sera évalué et la valeur de l'expression sera1
.C'est équivalent à
Notez que l'expression ci-dessus est parfaitement valide et n'invoque pas un comportement indéfini car il existe un point de séquence entre l'évaluation des opérandes gauche et droit de l'opérateur virgule.
la source
i
est initialisé avec5
. Regardez la déclaration de déclarationint i = 5;
.++i
, cette expression sera évaluée,i
sera incrémentée et cette valeur incrémentée sera la valeur de l'expression. Dans le cas dei++
, cette expression sera évaluée, l'ancienne valeur dei
sera la valeur de l'expression,i
sera incrémentée à tout moment entre le point de séquence précédent et suivant de l'expression.Citant de
C11
, chapitre6.5.17
, opérateur virguleDonc, dans votre cas,
est évalué comme
i
, est évalué comme une expression vide, valeur ignorée++i
, est évalué comme une expression vide, valeur ignorée1
valeur retournée.Donc, la déclaration finale ressemble à
et
i
arrive à2
. Je suppose que cela répond à vos deux questions,i
obtient une valeur 2?Remarque: FWIW, comme il y a un point de séquence présent après l'évaluation de l'opérande de gauche, une expression comme
(i, ++i, 1)
n'invoquera pas UB, comme on peut généralement le penser par erreur.la source
i
n'a clairement aucun effet! Cependant, je ne pense pas que ce soit si évident pour un gars qui ne connaît pas l'opérateur virgule (et je ne savais pas comment chercher de l'aide, à part poser une question). Dommage que j'ai eu tellement de votes négatifs! Je vais vérifier les autres réponses, puis décider lesquelles accepter. Merci! Belle réponse top btw.Analysons-le étape par étape.
Nous obtenons donc 2. Et la mission finale maintenant:
Tout ce qui était dans i avant il est écrasé maintenant.
la source
++i
ne contribue pas au résultat.int i = 0; for( ;(++i, i<max); )
Le résultat de
est
Pour
l'évaluation se produit de telle sorte que l'
,
opérateur rejette la valeur évaluée et retiendra juste la valeur la plus juste qui est1
Alors
la source
Vous trouverez de bonnes lectures sur la page wiki pour l' opérateur Virgule .
Fondamentalement, il
Cela signifie que
va, à son tour, évaluer
i
, rejeter le résultat, évalueri++
, rejeter le résultat, puis évaluer et retourner1
.la source
(void)exp; a= exp2;
alors que j'en avais juste besoina = exp, exp2;
)Vous devez savoir ce que fait l'opérateur virgule ici:
Votre expression:
La première expression
i
,, est évaluée, la deuxième expression++i
,, est évaluée et la troisième expression,,1
est renvoyée pour toute l'expression.Le résultat est donc:
i = 1 + 1
.Pour votre question bonus, comme vous le voyez, la première expression
i
n'a aucun effet, donc le compilateur se plaint.la source
La virgule a une priorité «inverse». C'est ce que vous obtiendrez des vieux livres et manuels C d'IBM (années 70/80). Donc, la dernière «commande» est ce qui est utilisé dans l'expression parent.
En C moderne, son utilisation est étrange mais très intéressante dans l'ancien C (ANSI):
Alors que toutes les opérations (fonctions) sont appelées de gauche à droite, seule la dernière expression sera utilisée comme résultat du conditionnel «while». Cela empêche la gestion de 'goto's pour conserver un bloc unique de commandes à exécuter avant la vérification de la condition.
EDIT: Cela évite également un appel à une fonction de gestion qui pourrait prendre en charge toute la logique aux opérandes de gauche et donc renvoyer le résultat logique. Rappelez-vous que nous n'avions pas de fonction en ligne dans le passé de C. Donc, cela pourrait éviter une surcharge d'appel.
la source