En C et C ++, quelles méthodes peuvent empêcher une utilisation accidentelle de l'affectation (=) là où l'équivalence (==) est nécessaire?

15

En C et C ++, il est très facile d'écrire le code suivant avec une grave erreur.

char responseChar = getchar();
int confirmExit = 'y' == tolower(responseChar);
if (confirmExit = 1)
{
    exit(0);
}

L'erreur est que l'instruction if aurait dû être:

if (confirmExit == 1)

Comme codé, il se fermera à chaque fois, car l'affectation de la confirmExitvariable se produit, puis confirmExitest utilisée comme résultat de l'expression.

Existe-t-il de bons moyens pour éviter ce type d'erreur?

DeveloperDon
la source
39
Oui. Activez les avertissements du compilateur. Traitez les avertissements comme des erreurs et ce n'est pas un problème.
Martin York
9
Dans l'exemple donné, la solution est simple. Vous attribuez une valeur booléenne, utilisez donc comme une valeur booléenne: if (confirmExit).
Sécurisé
4
Le problème est que l'erreur a été commise par les "concepteurs" du langage C, lorsqu'ils ont choisi d'utiliser = pour l'opérateur d'affectation et == pour la comparaison d'égalité. ALGOL a utilisé: =, car ils voulaient spécifiquement utiliser = pour la comparaison d'égalité, et PASCAL et Ada ont suivi la décision ALGOL. (Il convient de noter que, lorsque le DoD a sollicité une entrée basée sur C dans le bake-off DoD1, qui a finalement donné Ada, Bell Labs a décliné, affirmant que "C n'était pas maintenant et ne serait jamais assez robuste pour le DoD critique pour la mission "On souhaite que le DoD et les sous-traitants aient écouté Bell Labs à ce sujet.)
John R. Strohm
4
@John, le choix des symboles n'est pas le problème, c'est le fait que les affectations sont également une expression qui renvoie la valeur assignée, autorisant soit a = bou à l' a == bintérieur d'un conditionnel.
Karl Bielefeldt

Réponses:

60

La meilleure technique consiste à augmenter le niveau d'avertissement de votre compilateur. Il vous avertira alors d'une affectation potentielle dans le cas conditionnel.

Assurez-vous de compiler votre code sans aucun avertissement (ce que vous devriez faire de toute façon). Si vous voulez être pédant, configurez votre compilateur pour traiter les avertissements comme des erreurs.

L'utilisation des conditions Yoda (en mettant la constante sur le côté gauche) était une autre technique qui était populaire il y a une dizaine d'années. Mais ils rendent le code plus difficile à lire (et donc à maintenir en raison de la façon non naturelle dont ils lisent (sauf si vous êtes Yoda)) et n'offrent pas plus d'avantages que d'augmenter le niveau d'avertissement (qui a également des avantages supplémentaires de plus d'avertissements).

Les avertissements sont vraiment des erreurs logiques dans le code et doivent être corrigés.

Martin York
la source
2
Allez-y avec les avertissements, pas les conditions Yoda - vous pouvez d'abord oublier de faire la constante conditionnelle aussi facilement que vous pouvez oublier de le faire ==.
Michael Kohne
8
J'ai eu une agréable surprise que la réponse la plus votée à cette question soit «transformer les avertissements du compilateur en erreurs». J'attendais l' if (0 == ret)horreur.
James
2
@James: ... sans oublier que cela ne fonctionnera pas a == b!!
Emilio Garavaglia du
6
@EmilioGaravaglia: Il existe une solution de contournement simple: écrivez 0==a && 0==b || 1==a && 1==b || 2==a && 2==b || ...(répétez pour toutes les valeurs possibles). N'oubliez pas l' ...|| 22==a && 22==b || 23==a && 24==b || 25==a && 25==b ||erreur obligatoire ... sinon les programmeurs de maintenance ne s'amuseront pas.
user281377
10

Vous pouvez toujours faire quelque chose de radical comme tester votre logiciel. Je ne parle même pas de tests unitaires automatisés, juste les tests que chaque développeur expérimenté fait par habitude en exécutant son nouveau code deux fois, une fois confirmant la sortie et une fois non. C'est la raison pour laquelle la plupart des programmeurs considèrent qu'il ne s'agit pas d'un problème.

Karl Bielefeldt
la source
1
Facile à faire lorsque le comportement de votre programme est complètement déterministe.
James
3
Ce problème particulier peut être difficile à tester. J'ai vu des gens mordre rc=MethodThatRarelyFails(); if(rc = SUCCESS){plus d'une fois, surtout si la méthode échoue uniquement dans des conditions difficiles à tester.
Gort the Robot
3
@StevenBurnap: C'est à cela que servent les objets fantaisie. Un test approprié comprend le test des modes de défaillance.
Jan Hudec
Ceci est la bonne réponse.
Courses de légèreté avec Monica le
6

Une manière traditionnelle d'empêcher l'utilisation incorrecte des affectations dans l'expression consiste à placer la constante à gauche et la variable à droite.

if (confirmExit = 1)  // Unsafe

if (1=confirmExit)    // Safe and detected at compile time.

Le compilateur signalera une erreur pour l'affectation illégale à une constante similaire à la suivante.

.\confirmExit\main.cpp:15: error: C2106: '=' : left operand must be l-value

La condition if révisée serait:

  if (1==confirmExit)    

Comme le montrent les commentaires ci-dessous, cela est considéré par beaucoup comme une méthode inappropriée.

DeveloperDon
la source
13
Il convient de noter que faire les choses de cette façon vous rendra assez impopulaire.
riwalk
11
Veuillez ne pas recommander cette pratique.
James
5
Cela ne fonctionne pas non plus si vous devez comparer deux variables l'une à l'autre.
dan04
Certaines langues permettent en fait d'assigner 1 à une nouvelle valeur (oui, je sais que le Q est un combat c / c ++)
Matsemann
4

Je suis d'accord avec tout le monde qui dit "avertissements du compilateur" mais je veux ajouter une autre technique: les revues de code. Si vous avez une politique de révision de tout le code qui est validé, de préférence avant qu'il ne soit validé, il est probable que ce genre de chose sera détecté lors de la révision.

MatrixFrog
la source
Je ne suis pas d'accord. Surtout = au lieu de == peut facilement passer en revue le code.
Simon
2

Tout d'abord, augmenter vos niveaux d'avertissement ne fait jamais de mal.

Si vous ne voulez pas que votre conditionnel teste le résultat d'une affectation dans l'instruction if elle-même, alors avoir travaillé avec de nombreux programmeurs C et C ++ au fil des ans et n'avoir jamais entendu cela en comparant la constante en premier if(1 == val) était une mauvaise chose, vous pourrait essayer cette construction.

Si votre chef de projet approuve votre décision, ne vous inquiétez pas de ce que les autres pensent. La vraie preuve est de savoir si vous ou quelqu'un d'autre pouvez donner un sens à votre code dans des mois et des années à partir de maintenant.

Si vous aviez l'intention de tester le résultat d'une affectation, cependant, l'utilisation d'avertissements plus élevés aurait [probablement aurait] pu affecter l'affectation à une constante.

octopusgrabbus
la source
Je lève un sourcil sceptique envers tout programmeur non débutant qui voit un Yoda conditionnel et ne le comprend pas immédiatement. C'est juste un flip, pas difficile à lire, et certainement pas aussi mauvais que certains commentaires le prétendent.
underscore_d
@underscore_d Chez la plupart de mes employeurs, les affectations au sein d'un conditionnel étaient mal vues. L'idée était qu'il valait mieux séparer le devoir du conditionnel. La raison d'être plus clair, même au détriment d'une autre ligne de code, était le facteur d'ingénierie de maintien. J'ai travaillé dans des endroits qui avaient de grandes bases de code et beaucoup de maintenance en retard. Je comprends parfaitement que quelqu'un pourrait vouloir attribuer une valeur et se connecter à la condition résultant de l'affectation. Je vois que cela se fait plus souvent en Perl et, si l'intention est claire, je suivrai le modèle de conception de l'auteur.
octopusgrabbus
Je pensais aux conditions Yoda seules (comme votre démo ici), pas à l'affectation (comme dans l'OP). Cela ne me dérange pas le premier, mais je n'aime pas beaucoup le second non plus. La seule forme que j'utilise délibérément est if ( auto myPtr = dynamic_cast<some_ptr>(testPtr) ) {qu'elle évite de garder une nullptrportée inutile si le transtypage échoue - ce qui est probablement la raison pour laquelle C ++ a cette capacité limitée à attribuer dans un conditionnel. Pour le reste, oui, une définition devrait avoir sa propre ligne, je dirais - beaucoup plus facile à voir en un coup d'œil et moins sujette à des glissements de tête assortis.
underscore_d
@underscore_d Réponse modifiée en fonction de vos commentaires. Bon point.
octopusgrabbus
1

Toujours en retard à la fête, mais l'analyse du code statique est la clé ici

La plupart des IDE fournissent désormais SCA au-delà de la vérification syntaxique du compilateur, et d'autres outils sont disponibles, y compris ceux qui implémentent les directives MISRA (*) et / ou CERT-C.

Déclaration: Je fais partie du groupe de travail MISRA C, mais je poste à titre personnel. Je suis également indépendant de tout fournisseur d'outils

Andrew
la source
-2

Utilisez simplement l'affectation à gauche, les avertissements du compilateur peuvent vous aider, mais vous devez vous assurer d'obtenir le bon niveau, sinon vous serez submergé d'avertissements inutiles ou on ne vous dira pas ceux que vous souhaitez voir.

Lama inversé
la source
4
Vous seriez surpris du peu d'avertissements inutiles générés par les compilateurs modernes. Vous ne pensez peut-être pas qu'un avertissement est important, mais dans la plupart des cas, vous vous trompez.
Kristof Provost
Consultez le livre, "Writing Solid Code" amazon.com/Writing-Solid-Microsoft-Programming-Series/dp/… . Cela commence par une excellente discussion sur les compilateurs et sur les avantages que nous pourrions tirer des messages d'avertissement et d'une analyse statique encore plus approfondie.
DeveloperDon
@KristofProvost J'ai réussi à obtenir Visual Studio pour me donner 10 copies du même avertissement exact pour la même ligne de code, puis lorsque ce problème a été `` résolu '', il en a résulté 10 avertissements identiques sur la même ligne de code indiquant que le la méthode originale était meilleure.
Lama inversé