Utilisation par inadvertance de = au lieu de ==

Il paraît que

if (x=y) { .... } 

au lieu de

 if (x==y) { ... } 

est une racine de nombreux maux.

Pourquoi tous les compilateurs ne la marquent-ils pas comme une erreur au lieu d’un avertissement configurable?

Je suis intéressé à trouver des cas où la construction if (x=y) est utile.

La plupart du temps, les compilateurs s’efforcent de restr compatibles avec les versions antérieures.

Changer leur comportement en ce qui concerne les erreurs jettera le code légit existant, et même commencer à lancer des avertissements à ce sujet causera des problèmes avec les systèmes automatiques qui gardent une trace du code en le compilant automatiquement et en recherchant les erreurs et les avertissements.

C’est un mal pour lequel nous sums assez coincés avec des guichets automatiques, mais il existe des moyens de les contourner et d’en réduire les dangers.

Exemple:

  void *ptr = calloc(1, sizeof(array)); if (NULL = ptr) { // some error } 

Cela provoque une erreur de compilation.

une construction utile est par exemple:

 char *pBuffer; if (pBuffer = malloc(100)) { //continue to work here } 

modifier:
comme mentionné précédemment, et voté à plusieurs resockets maintenant, je pourrais append que ce n’est pas un style spécialement bon, mais vu assez souvent pour dire que c’est utile. J’ai aussi vu cela avec du new , mais cela me fait plus mal à la poisortingne.

un autre exemple, moins controversé, pourrait être:

 while (pointer = getNextElement(context)) { //go for it, use the pointer to the new segment of data } 

ce qui implique que la fonction getNextElement() renvoie NULL lorsqu’il n’y a pas d’élément suivant, de sorte que la boucle est sortie.

Réponse simple: Une opération d’affectation, telle que x = y, a une valeur identique à la nouvelle valeur affectée dans x. Vous pouvez utiliser cela directement dans une comparaison, donc au lieu de

x = y; if (x) ...

tu peux écrire

if (x = y) ...

C’est moins de code à écrire (et à lire), ce qui est parfois une bonne chose, mais de nos jours, la plupart des gens s’accordent pour dire qu’il devrait être écrit d’une autre manière pour améliorer la lisibilité. Par exemple, comme ceci:

if ((x = y) != 0) ...

Voici un exemple réaliste. Supposons que vous souhaitiez allouer de la mémoire avec malloc et voyez si cela a fonctionné. Cela peut être écrit étape par étape comme ceci:

p = malloc(4711); if (p != NULL) printf("Ok!");

La comparaison avec NULL est redondante, vous pouvez donc la récrire comme ceci:

p = malloc(4711); if (p) printf("Ok!");

Mais puisque l’opération d’assignation a une valeur utilisable, vous pouvez placer l’affectation complète dans la condition if:

if (p = malloc(4711)) printf("Ok!");

Cela fait la même chose, mais est plus concis.

Parce que ce n’est pas illégal (en C ou C ++ de toute façon) et parfois utile …

 if ( (x = read(blah)) > 0) { // now you know how many bits/bytes/whatever were read // and can use that info. Esp. if you know, say 30 bytes // are coming but only got 10 } 

La plupart des compilateurs dégagent une véritable puanteur si vous ne mettez pas de toute façon des parenthèses autour de la tâche, ce que j’aime bien.

À propos des utilisations valides de if (i = 0)

Le problème est que vous prenez le problème à l’envers. La notation “if” ne consiste pas à comparer deux valeurs comme dans d’autres langues.

L’instruction “if” en C / C ++ attend toute expression qui se traduira par une valeur booléenne ou par une valeur null / non-null. Cette expression peut inclure une comparaison de deux valeurs et / ou peut être beaucoup plus complexe.

Par exemple, vous pouvez avoir:

 if(i >> 3) { std::cout << "i is less than 8" << std::endl } 

Ce qui prouve que, en C / C ++, l'expression if n'est pas limitée à == et =. N'importe quoi fera l'affaire, tant qu'il peut être évalué comme vrai ou faux (C ++), ou zéro non nul (C / C ++).

Une autre utilisation valide de C ++:

 if(MyObject * pObject = dynamic_cast(pInterface)) { pObject->doSomething() ; } 

Et ce sont des utilisations simples de l'expression if (notez que cela peut également être utilisé dans la ligne de déclaration de la boucle for). Des utilisations plus complexes existent.

À propos des utilisations avancées de if (i = 0) en C ++ [Cité de moi-même]

Après avoir découvert un duplicata de cette question à Dans quel cas si (a = b) est une bonne idée? , J’ai décidé de compléter cette réponse par un bonus supplémentaire, c’est-à-dire l’injection de variable dans une étendue, ce qui est possible en C ++ car if évaluera son expression, y compris une déclaration de variable, au lieu de se limiter à comparer deux opérandes comme elle est faite dans d'autres langues:

Alors, citant de moi - même :

Une autre utilisation serait d'utiliser ce qu'on appelle l'injection de variables C ++. En Java, il y a ce mot-clé cool:

 synchronized(p) { // Now, the Java code is synchronized using p as a mutex } 

En C ++, vous pouvez le faire aussi. Je n'ai pas le code exact en tête (ni l'article exact de DDJ où je l'ai découvert), mais cette simple définition devrait suffire à des fins de démonstration:

 #define synchronized(lock) \ if (auto_lock lock_##__LINE__(lock)) synchronized(p) { // Now, the C++ code is synchronized using p as a mutex } 

C’est la même chose, en mélangeant l’injection avec if et for déclaration, vous pouvez déclarer une macro primitive foreach (si vous voulez une foreach de force indussortingelle, utilisez boost).

Voir les articles suivants pour une implémentation moins naïve, plus complète et plus robuste:

Combien d'erreurs de ce genre se produisent vraiment?

Rarement. En fait, je ne me suis pas encore souvenu de l'un d'entre eux et je suis professionnel depuis 8 ans. Je suppose que c'est arrivé, mais ensuite, en 8 ans, j'ai produit une quantité non négligeable d'insectes. C'est juste que ce genre de bugs n'est pas arrivé assez pour que je me souvienne d'eux avec frustration.

En C, vous aurez plus de bugs à cause des dépassements de tampon, comme:

 void doSomething(char * p) { strcpy(p, "Hello World, how are you \?\n") ; } void doSomethingElse() { char buffer[16] ; doSomething(buffer) ; } 

En fait, Microsoft a été brûlé si dur à cause de cela, ils ont ajouté un avertissement dans Visual C ++ 2008 dépréciant strcpy !!!

Comment pouvez-vous éviter la plupart des erreurs?

La toute première "protection" contre cette erreur consiste à "inverser" l'expression: comme vous ne pouvez pas affecter de valeur à une constante, ceci:

 if(0 = p) // ERROR : It should have been if(0 == p). WON'T COMPILE ! 

Ne pas comstackr.

Mais je trouve cette solution plutôt médiocre, car elle essaie de dissimuler derrière un style ce qui devrait être une pratique de programmation générale, à savoir: Toute variable qui n’est pas supposée changer doit être constante.

Par exemple, au lieu de:

 void doSomething(char * p) { if(p == NULL) // POSSIBLE TYPO ERROR return ; size_t length = strlen(p) ; if(length == 0) // POSSIBLE TYPO ERROR printf("\"%s\" length is %i\n", p, length) ; else printf("the ssortingng is empty\n") ; } 

En essayant de "const" autant de variables que possible, vous éviterez la plupart des erreurs de frappe, y compris celles qui ne figurent pas dans les expressions "if":

 void doSomething(const char * const p) // CONST ADDED HERE { if(p == NULL) // NO TYPO POSSIBLE return ; const size_t length = strlen(p) ; // CONST ADDED HERE if(length == 0) // NO TYPO POSSIBLE printf("\"%s\" length is %i\n", p, length) ; else printf("the ssortingng is empty\n") ; } 

Bien sûr, ce n’est pas toujours possible (certaines variables devant changer), mais j’ai trouvé que la plupart des variables que j’utilise sont des constantes (je ne les initialise que une fois, puis ne les lis que).

Conclusion

Habituellement, je vois du code utilisant la notation if (0 == p), mais sans la notation const.

Pour moi, c'est comme avoir une poubelle pour les objects recyclables et une autre pour les objects non recyclables, et ensuite, les jeter ensemble dans le même récipient.

Donc, ne perdez pas une habitude de style en espérant que cela améliorera votre code. Ça ne va pas. Utilisez autant que possible les constructions de langage, ce qui signifie, dans ce cas, utiliser à la fois la notation if (0 == p), si disponible, et le mot-clé const autant que possible.

L’idiome ‘if (0 = x)’ est presque inutile car il n’aide pas lorsque les deux côtés sont des variables (‘if (x = y)’) et la plupart du temps (tous?) Vous devriez utiliser des variables constantes plutôt que des nombres magiques.

Deux autres raisons pour lesquelles je n’utilise jamais cet idiome, à mon humble avis, cela rend le code moins lisible et, pour être honnête, je trouve que le single ‘=’ est la racine de très peu de mal. Si vous testez minutieusement votre code (ce que nous faisons tous évidemment), ce type d’erreur de syntaxe se produit très rapidement.

De nombreux compilateurs détecteront cela et vous avertiront, mais seulement si le niveau d’avertissement est suffisamment élevé.

Par exemple:

  ~> gcc -c -Wall foo.c
 foo.c: dans la fonction 'foo':
 foo.c: 5: warning: suggère des parenthèses autour de l'affectation utilisée comme valeur de vérité

Idiom standard C pour itérer:

 list_elem* curr; while ( (curr = next_item(list)) != null ) { /* ... */ } 

Cela dépend du langage. Java le signale comme une erreur, car seules les expressions booléennes peuvent être utilisées à l’intérieur de la parenthèse if (et à moins que les deux variables ne soient booléennes, auquel cas l’affectation est également un booléen).

En C, il est assez courant de tester des pointeurs renvoyés par malloc ou si, après un fork, nous sums dans le processus parent ou enfant:

 if ( x = (X*) malloc( sizeof(X) ) { // malloc worked, pointer != 0 if ( pid = fork() ) { // parent process as pid != 0 

Les compilateurs C / C ++ avertiront avec un niveau d’avertissement suffisamment élevé si vous le demandez, mais cela ne peut pas être considéré comme une erreur si le langage le permet. Sauf si, encore une fois, vous demandez au compilateur de traiter les avertissements comme des erreurs.

Lorsqu’ils comparent avec des constantes, certains auteurs suggèrent d’utiliser la variable test constante == afin que le compilateur détecte si l’utilisateur oublie le deuxième signe d’égalité.

 if ( 0 == variable ) { // the comstackr will complaint if you mistakenly // write =, as you cannot assign to a constant 

Quoi qu’il en soit, vous devriez essayer de comstackr avec les parameters d’avertissement les plus élevés possibles.

Est-ce vraiment une erreur si commune? J’en ai appris moi-même lorsque j’ai appris le C et, en tant qu’enseignant, j’ai parfois mis en garde mes élèves et leur ai dit que c’était une erreur courante, mais je l’ai rarement vue dans le code réel, même parmi les débutants. Certainement pas plus souvent que d’autres erreurs commises par des opérateurs, telles que l’écriture de “&&” à la place de “||”.

Donc, la raison pour laquelle les compilateurs ne la marquent pas comme une erreur (à part qu’il s’agisse d’un code parfaitement valide) est peut-être que ce n’est pas la racine de très nombreux maux.

Je pense que les concepteurs de langages C et C ++ ont remarqué qu’il n’était pas vraiment utile de l’ interdire parce que

  • Les compilateurs peuvent en avertir s’ils veulent quand même
  • La refuser appendait des cas particuliers à la langue et supprimerait une fonctionnalité possible.

Il n’y a aucune complexité impliquée dans le permettre. C ++ dit simplement qu’une expression implicitement convertible en bool est requirejse. En C, il existe des cas utiles détaillés par d’autres réponses. En C ++, ils vont encore plus loin et autorisent celui-ci en plus:

 if(type * t = get_pointer()) { // .... } 

Ce qui limite en réalité la scope de t au seul if et à ses corps.

Essayez de regarder

 if( life_is_good() ) enjoy_yourself(); 

comme

 if( tmp = life_is_good() ) enjoy_yourself(); 

Une partie de cela a à voir avec le style personnel et les habitudes. Je suis agnostique à lire if (kConst == x) ou if (x == kConst). Je n’utilise pas la constante de gauche parce qu’historiquement, je ne fais pas cette erreur et j’écris le code comme je le dirais ou aimerais le lire. Je considère cela comme une décision personnelle faisant partie de la responsabilité personnelle d’être un ingénieur conscient de lui-même et en amélioration. Par exemple, j’ai commencé à parsingr les types de bugs que je créais et à remodeler mes habitudes pour ne pas les créer – comme une constante à gauche, mais avec d’autres éléments.

Cela dit, les avertissements du compilateur sont historiquement assez merdiques et même si ce problème est connu depuis des années, je ne l’ai pas vu dans un compilateur de production jusqu’à la fin des années 80. J’ai également constaté que le fait de travailler sur des projets portables permettait de nettoyer beaucoup mon C, étant donné que différents compilateurs et différents goûts (c.-à-d. Avertissements) et différentes différences sémantiques subtiles.

Il existe de nombreuses utilisations intéressantes de l’opérateur d’affectation dans une déclaration conditionnelle, et il serait très pénible de voir les avertissements concernant chacun d’eux tout le temps. Ce qui serait bien serait une fonction dans votre IDE qui vous permettrait de mettre en évidence tous les endroits où l’affectation a été utilisée à la place d’un contrôle d’égalité – ou – après avoir écrit quelque chose comme ceci:

 if (x = y) { 

puis cette ligne clignote plusieurs fois. Assez pour vous dire que vous avez fait quelque chose qui n’est pas tout à fait standard, mais pas tant que c’est ennuyant.

Personnellement, j’estime qu’il s’agit de l’exemple le plus utile.

Disons que vous avez une fonction read() qui renvoie le nombre d’octets lus et que vous devez l’utiliser dans une boucle. C’est beaucoup plus simple à utiliser

 while((count = read(foo)) > 0) { //Do stuff } 

que d’essayer d’obtenir l’assignation de la tête de la boucle, ce qui entraînerait des choses comme

 while(1) { count = read(foo); if(!(count > 0)) break; //... } 

ou

 count = read(foo); while(count > 0) { //... count = read(foo); } 

La première construction est inconfortable et la seconde répète le code de manière désagréable.

À moins, bien sûr, que j’ai manqué un shiny idiome pour cela …

L’affectation conditionnelle est légale C et C ++, et tout compilateur qui ne le permet pas n’est pas un vrai compilateur C ou C ++. J’espère que tout langage moderne non conçu pour être explicitement compatible avec le langage C (comme le fut le C ++) le considérerait comme une erreur.

Il existe des cas où cela permet des expressions concises, telles que idiomatic while (*dest++ = *src++); copier une chaîne en C, mais dans l’ensemble, ce n’est pas très utile, et j’estime que c’est une erreur dans la conception du langage. D’après mon expérience, il est facile de commettre cette erreur et difficile à détecter lorsque le compilateur n’émet aucun avertissement.

  if ((k==1) || (k==2)) is a conditional if ((k=1) || (k=2) ) is BOTH a conditional AND an assignment statement 
  • Voici l’explication *

Comme dans la plupart des langues, C fonctionne de la plus haute à la périphérie par ordre de priorité des opérateurs.

Il essaie d’abord de définir k sur 1 et y parvient.

 Result: k = 1 and Boolean = 'true' 

Next: il met k à 2 et réussit.

 Result: k = 2 and Boolean = 'true' 

Suivant: il évalue (true || true)

 Result: k still = 2, and Boolean = true 

Enfin, il résout ensuite le conditionnel: If (true)

  Result: k = 2 and the program takes the first branch. 

En près de 30 ans de programmation, je n’ai pas trouvé de raison valable d’utiliser cette construction, bien que, s’il en existe une, elle a probablement à voir avec la nécessité de dissimuler délibérément votre code.

Lorsque l’un de nos nouveaux collaborateurs a un problème, c’est l’une des choses que je recherche, tout en évitant de coller un terminateur sur une chaîne, de copier une instruction de débogage d’un endroit à un autre et de ne pas changer le ‘% i en’. % s ‘pour correspondre au nouveau champ qu’ils déversent.

Ceci est assez courant dans notre boutique car nous basculons constamment entre C / Oracle et PL / SQL; si (k = 1) EST la syntaxe correcte en PL / SQL.

Vous avez demandé pourquoi cela était utile, mais continuez à remettre en question les exemples fournis par les gens. C’est utile parce que c’est concis.

Oui, tous les exemples qui l’utilisent peuvent être ré-écrits – sous forme de morceaux de code plus longs.

C’est très courant avec les constructions de boucle “de bas niveau” en C / C ++, comme avec les copies:

 void my_strcpy(char *dst, const char *src) { while((*dst++ = *src++) != '\0') { // Note the use of extra parentheses, and the explicit compare. /* DO NOTHING */ } } 

Bien sûr, les affectations sont très courantes avec les boucles for:

 int i; for(i = 0; i < 42; ++i) { printf("%d\n", i); } 

Je crois qu'il est plus facile de lire les devoirs quand ils sont en dehors de if

 char *newssortingng = malloc(strlen(src) * sizeof(char)); if(newssortingng == NULL) { fprintf(stderr, "Out of memory, d00d! Bailing!\n"); exit(2); } // Versus: if((newssortingng = malloc(strlen(src) * sizeof(char))) == NULL) // ew... 

Assurez-vous que la tâche est évidente, thuogh (comme dans les deux premiers exemples). Ne le cache pas.

En ce qui concerne les utilisations accidentelles ... cela ne m’arrive pas beaucoup. Une sauvegarde commune consiste à placer votre variable (lvalues) dans la partie droite de la comparaison, mais cela ne fonctionne pas bien avec des choses comme:

 if(*src == *dst) 

parce que les deux oprands à == sont des lvalues!

Quant aux compilateurs ... qui peut les blâmer? Écrire des compilateurs est difficile, et vous devriez quand même écrire des programmes parfaits pour le compilateur (vous vous souvenez de GIGO?). Certains compilateurs (les plus connus bien sûr) fournissent une vérification intégrée du style de la lint , mais ce n'est certainement pas nécessaire. Certains navigateurs ne valident pas chaque octet de code HTML et javascript, alors pourquoi les compilateurs?

Il existe plusieurs tactiques pour vous aider à repérer ceci: l’une est laide, l’autre est généralement une macro. Cela dépend vraiment de la façon dont vous lisez votre langue parlée (de gauche à droite, de droite à gauche).

Par exemple:

 if ((fp = fopen("foo.txt", "r") == NULL)) 

Contre:

 if (NULL == (fp = fopen(...))) 

Parfois, il peut être plus facile de lire / écrire (en premier) ce que vos tests permettent, ce qui facilite la tâche de repérer une tâche par rapport à un test. Faites ensuite venir la plupart des personnes comp.lang.c qui détestent ce style avec passion.

Donc, nous apportons assert ():

 #include  ... fp = fopen("foo.txt", "r"); assert(fp != NULL); ... 

lorsque vous êtes au milieu ou à la fin d’un ensemble compliqué de conditions, assert () est votre ami. Dans ce cas, si FP == NULL, un abort () est généré et la ligne / le fichier du code incriminé est transmis.

Donc si vous oups:

 if (i = foo) 

au lieu de

 if (i == foo) 

suivi par

 assert (i > foo + 1) 

… vous remarquerez rapidement de telles erreurs.

J’espère que cela t’aides 🙂

En résumé, il est parfois utile d’inverser les arguments lors du débogage. Assert () est votre ami de longue durée et peut être désactivé dans les indicateurs de compilation dans les versions de production.

Je n’ai eu cette typo qu’une fois au cours de mes 15 années de développement. Je ne dirais pas que c’est au sumt de ma liste de choses à surveiller. J’évite aussi cette construction de toute façon.

Notez également que certains compilateurs (celui que j’utilise) émettent un avertissement sur ce code. Les avertissements peuvent être traités comme des erreurs pour tout compilateur digne de ce nom. Ils peuvent également être ignorés.

Placer la constante du côté gauche d’une comparaison est une programmation défensive. Bien sûr, vous ne ferez jamais l’erreur stupide d’oublier cet extra, mais qui sait pour le type AUTRE.

Le langage de programmation D le signale comme une erreur. Pour éviter le problème de vouloir utiliser la valeur plus tard, il autorise des déclarations similaires à celles que C ++ autorise avec for boucles for .

 if(int i = some_fn()) { another_fn(i); } 

Le compilateur ne le signalera pas comme une erreur car il est valide C / C ++. Mais ce que vous pouvez faire (du moins avec Visual C ++), c’est d’augmenter le niveau d’avertissement afin de le signaler comme un avertissement, puis de demander au compilateur de traiter les avertissements comme des erreurs. Ceci est néanmoins une bonne pratique pour que les développeurs n’ignorent pas les avertissements.

Si vous aviez réellement voulu dire = au lieu de == alors vous devez être plus explicite à ce sujet. par exemple

 if ((x = y) != 0) 

Théoriquement, vous êtes censé pouvoir faire ceci:

 if ((x = y)) 

pour ignorer l’avertissement, mais cela ne semble pas toujours fonctionner.

C’est pourquoi il vaut mieux écrire:

0 == CurrentItem

Au lieu de:

CurrentItem == 0

de sorte que le compilateur vous avertisse si vous tapez = au lieu de ==.

Comme indiqué dans d’autres réponses, il existe des cas où l’utilisation d’une affectation dans une condition offre un morceau de code bref, mais lisible, qui fait ce que vous voulez. En outre, de nombreux compilateurs à jour vous préviendront s’ils voient une affectation dans laquelle ils s’attendent à une condition. (Si vous êtes un partisan de l’approche du développement basée sur l’avertissement zéro, vous l’avez déjà vue.)

Une habitude que j’ai développée et qui m’empêche de me faire piquer (du moins dans les langages C-ish) est que si l’une des deux valeurs que je compare est une constante (ou sinon une valeur légale), je le mets sur le côté gauche du comparateur: if (5 == x) { whatever(); } if (5 == x) { whatever(); } Ensuite, si je tape accidentellement if (5 = x) , le code ne sera pas compilé.

Échantillon RegEx

 RegEx r;

 if (((= r nouveau RegEx ("\ w *)). IsMatch ()) {
    // ... fais quelque chose ici
 }
 else if ((r = new RegEx ("\ d *")). IsMatch ()) {
    // ... fais quelque chose ici
 }

assigner un test de valeur

 int i = 0;
 si ((i = 1) == 1) {
    // 1 est égal à i qui a été affecté à une valeur int 1
 }
 autre {
    //?
 }

En pratique, je ne le fais pas, mais un bon conseil est de le faire:

 if ( true == $x ) 

Dans le cas où vous omettez un égal, l’affectation de $x à true renverra évidemment une erreur.