J'ai vu cette question dans un test dans lequel nous devons dire la sortie du code suivant.
#include<stdio.h>
int main(){
int k = 0;
while(+(+k--)!=0)
k=k++;
printf("%d\n", k);
return 0;
}
La sortie est -1
. Je ne sais pas pourquoi c'est la réponse, cependant.
Que signifie l'expression +(+k--)
en C?
k=k++
n'est pas défini, mais ce n'est pas indéfini car il n'est jamais exécuté en raison d'une condition obscurcie - je vote pour fermer en double de cette question .Réponses:
[Pour mémoire, j'ai modifié cette réponse de manière assez significative depuis qu'elle a été acceptée et mise aux voix. Cela dit toujours essentiellement les mêmes choses.]
Ce code est profondément, peut-être délibérément, déroutant. Il contient une instance étroitement évitée du comportement redouté non défini . Il est fondamentalement impossible de déterminer si la personne qui a construit cette question était très, très intelligente ou très, très stupide. Et la «leçon» que ce code pourrait prétendre vous enseigner ou vous interroger - à savoir que l'opérateur unaire plus ne fait pas grand-chose - n'est certainement pas assez importante pour mériter ce genre de mauvaise orientation subversive.
Il y a deux aspects confus du code, l'étrange condition:
et la déclaration démente qu'il contrôle:
Je vais d'abord couvrir la deuxième partie.
Si vous avez une variable comme
k
celle que vous souhaitez incrémenter de 1, C vous donne non pas une, pas deux, pas trois, mais quatre façons différentes de le faire:k = k + 1
k += 1
++k
k++
Malgré cette générosité (ou peut-être à cause de cela), certains programmeurs sont confus et crachent des contorsions comme
Si vous ne pouvez pas comprendre ce que cela est censé faire, ne vous inquiétez pas: personne ne le peut. Cette expression contient deux tentatives différentes de modifier
k
la valeur (lak =
partie et lak++
partie), et comme il n'y a pas de règle en C pour dire laquelle des tentatives de modification "gagne", une expression comme celle-ci est formellement indéfinie , ce qui signifie non seulement que il n'a pas de sens défini, mais que l'ensemble du programme qui le contient est suspect.Maintenant, si vous regardez très attentivement, vous verrez que dans ce programme particulier, la ligne
k = k++
n'est pas exécutée, car (comme nous allons le voir) la condition de contrôle est initialement fausse, donc la boucle s'exécute 0 fois . Donc , ce programme ne pourrait pas réellement être indéfini - mais il est toujours déroutant pathologiquement.Voir également ces réponses SO canoniques à toutes les questions concernant le comportement indéfini de ce type.
Mais vous n'avez pas posé de questions sur la
k=k++
partie. Vous avez posé des questions sur la première partie déroutante, la+(+k--)!=0
condition. Cela semble étrange, car il est étrange. Personne n'écrirait jamais un tel code dans un vrai programme. Il n'y a donc aucune raison d'apprendre à le comprendre. (Oui, c'est vrai, explorer les limites d'un système peut vous aider à en savoir plus sur ses subtilités, mais il y a une ligne assez claire dans mon livre entre les explorations imaginatives et suscitant la réflexion contre les explorations stupides et abusives, et cette expression est très clairement sur du mauvais côté de cette ligne.)Quoi qu'il en soit, examinons
+(+k--)!=0
. (Et après cela, oublions tout.) Toute expression comme celle-ci doit être comprise de l'intérieur. Je suppose que tu sais quoiEst-ce que. Il prend
k
la valeur actuelle de 'et la "retourne" au reste de l'expression, et il diminue plus ou moins simultanémentk
, c'est-à-dire qu'il stocke la quantiték-1
dansk
.Mais alors qu'est-ce que ça
+
fait? C'est un plus unaire , pas un plus binaire. C'est comme un moins unaire. Vous savez que le binaire moins fait la soustraction: l'expressionsoustrait b de a. Et vous savez que le moins unaire nie les choses: l'expression
vous donne le négatif d'un. Ce que fait unaire,
+
c'est ... essentiellement rien.+a
vous donnea
la valeur après avoir changé les valeurs positives en valeurs positives et négatives en négatives. Donc l'expressionvous donne tout ce
k--
qui vous a donné, c'est-à-direk
l'ancienne valeur.Mais nous n'avons pas fini, car nous avons
Cela prend tout ce
+k--
qui vous a été donné et s'applique+
de nouveau à lui. Donc, cela vous donne tout ce qui vous a+k--
donné, ce quik--
vous a donné, qui étaitk
l'ancienne valeur.Donc à la fin, la condition
fait exactement la même chose que la condition beaucoup plus ordinaire
aurait fait. (Il fait également la même chose que l'
while(+(+(+(+k--)))!=0)
aurait fait une condition d'apparence encore plus compliquée . Et ces parenthèses ne sont pas vraiment nécessaires; il fait également la même chose que l'while(+ + + +k--!=0)
aurait fait.)Même comprendre ce que la condition "normale"
est un peu délicat. Il y a en quelque sorte deux choses qui se passent dans cette boucle: Comme la boucle s'exécute potentiellement plusieurs fois, nous allons:
k--
, pour faire dek
plus en plus petit, mais aussiMais nous faisons la
k--
partie tout de suite, avant (ou en train de) décider de faire un autre voyage à travers la boucle. Et rappelez-vous quek--
"renvoie" l'ancienne valeur dek
, avant de la décrémenter. Dans ce programme, la valeur initiale dek
est 0. Ilk--
va donc "retourner" l'ancienne valeur 0, puis mettrek
à jour à -1. Mais le reste de la condition est!= 0
- mais comme nous venons de le voir, la première fois que nous avons testé la condition, nous avons obtenu un 0. Donc, nous ne ferons aucun voyage dans la boucle, donc nous n'essaierons pas d'exécuter le déclaration problématiquek=k++
du tout.En d'autres termes, dans cette boucle particulière, même si j'ai dit "qu'il se passe en quelque sorte deux choses", il s'avère que la chose 1 se produit une fois, mais la chose 2 se produit zéro fois.
En tout cas, j'espère qu'il est maintenant suffisamment clair pourquoi cette mauvaise excuse pour un programme finit par imprimer -1 comme valeur finale de
k
. Normalement, je n'aime pas répondre à des questions de quiz comme celle-ci - cela ressemble à de la triche - mais dans ce cas, comme je suis tellement bruyamment en désaccord avec le but de l'exercice, cela ne me dérange pas.la source
À première vue, il semble que ce code invoque un comportement non défini, mais ce n'est pas le cas.
Formater d'abord le code correctement:
Alors maintenant, nous pouvons voir que l'instruction
k=k++;
est à l'intérieur de la boucle.Voyons maintenant le programme:
Lorsque la condition de boucle est évaluée pour la première fois,
k
a la valeur 0. L'expressionk--
a la valeur actuelle dek
, qui est 0, etk
est décrémentée comme effet secondaire. Donc, après cette déclaration, la valeur dek
est -1.Le début
+
de cette expression n'a aucun effet sur la valeur, donc+k--
évalué à 0 et de même+(+k--)
à 0.Ensuite, l'
!=
opérateur est évalué. Puisque0!=0
est faux, le corps de la boucle n'est pas entré . Si le corps avait été entré, vous invoqueriez un comportement indéfini car lesk=k++
deux lit et écritk
sans point de séquence. Mais la boucle n'est pas entrée, donc pas d'UB.Enfin la valeur de
k
est imprimée qui est -1.la source
if (x != NULL) *x = 42;
Est-ce indéfini quandx == NULL
? Bien sûr que non. Un comportement indéfini ne se produit pas dans les parties de code qui ne sont pas exécutées. Le mot comportement est un indice. Le code qui n'est pas exécuté n'a aucun comportement, indéfini ou autre.k=k++
c'est qualitativement différent de*x=42
. Ce dernier est bien défini six
est un pointeur valide, mais le premier n'est pas défini quoi qu'il arrive. (Je concède que vous avez peut-être raison, mais encore une fois, je ne vais pas m'opposer à cela, et je suis de plus en plus préoccupé par le fait que nous ayons été magistralement contrôlés.)Voici une version de cela qui montre la priorité des opérateurs:
Les deux
+
opérateurs unaires ne font rien, donc cette expression est exactement équivalente àk--
. La personne qui a écrit ceci essayait probablement de jouer avec votre esprit.la source