Question Quels sont les avantages de l'affectation des écrasements et de la vérification des erreurs sur une seule ligne?


Cette question est inspirée par cette question, qui comporte l'extrait de code suivant.

int s;
if((s = foo()) == ERROR)
    print_error();

Je trouve ce style difficile à lire et sujet aux erreurs (comme le montre la question initiale - il a été provoqué par l'absence de parenthèses autour de l'affectation). J'écrirais plutôt ce qui suit, qui est en fait plus court en termes de caractères.

int s = foo();
if(s == ERROR)
    print_error();

Ce n'est pas la première fois que j'ai vu cet idiome, et je devine qu'il y a des raisons (peut-être historiques) que ce soit si souvent utilisé. Quelles sont ces raisons?


19
2018-05-23 13:50


origine


Réponses:


Lorsque vous écrivez une boucle, il est parfois souhaitable d'utiliser la première forme, comme dans cet exemple célèbre de K & R:

int c;

while ((c = getchar()) != EOF) {
    /* stuff */
}

Il n'y a pas de manière élégante d'écrire ceci sans répétition:

int c = getchar();

while (c != EOF) {
    /* stuff */
    c = getchar();
}

Ou:

int c;

for (c = getchar(); c != EOF; c = getchar()) {
    /* stuff */
}

Maintenant que l'affectation à c est répété, le code est plus enclin aux erreurs, car il faut garder les deux instructions en synchronisation.

Il faut donc pouvoir apprendre à lire et à écrire facilement le premier formulaire. Et vu cela, il semble logique d'utiliser le même formulaire dans if conditions aussi.

J'ai tendance à utiliser le premier formulaire principalement parce que je le trouve facile à lire - comme quelqu'un d'autre l'a dit, il associe de plus près l'appel de fonction et le test de valeur de retour.


13
2018-05-23 14:45



Je pense que pour des raisons hystériques, les premiers compilateurs n’ont pas été si malins à optimiser. En le plaçant sur une seule ligne en tant qu’expression unique, le compilateur obtient une indication que la même valeur extraite de foo () peut être testée plutôt que de charger spécifiquement la valeur à partir de s.

Je préfère la clarté de votre deuxième exemple, avec l'assignation et le test effectués plus tard. Un compilateur moderne n'aura aucun problème à optimiser ceci en registres, évitant des charges inutiles de la mémoire.


15
2018-05-23 13:54



Je fais une tentative consciente de combiner les deux si possible. La taille de la "pénalité" n'est pas suffisante pour surmonter l'avantage dans la clarté, l'OMI.

L'avantage de la clarté vient d'un fait: pour une fonction comme celle-ci, vous devriez toujours penser à appeler la fonction et à tester la valeur de retour en tant que unique action qui ne peut pas être divisée en deux parties ("atomique", si vous voulez). Vous devriez jamais appeler une telle fonction sans tester immédiatement sa valeur de retour.

Si vous séparez les deux (du tout), la probabilité que vous sautiez parfois complètement la valeur de retour est beaucoup plus grande. D'autres fois, vous insérez accidentellement du code entre l'appel et le test de la valeur de retour qui dépend de la réussite de cette fonction. Si vous combinez toujours tout cela en une seule déclaration, cela élimine (presque) toute possibilité de tomber dans ces pièges.


5
2018-05-23 14:03



J'irais toujours pour la seconde. Il est plus facile à lire, il n'y a pas de risque d'omettre les parenthèses autour de l'affectation et il est plus facile de parcourir un débogueur.


4
2018-05-23 14:33



Je trouve souvent que la séparation de l'affectation dans une ligne différente fait que les fenêtres de surveillance ou "locales" se comportent mieux par rapport à la présence et à la valeur correcte de "s", du moins dans les versions non optimisées.

Il permet également d'utiliser séparément les lignes d'attribution et de test (encore une fois dans les versions non optimisées), ce qui peut être utile si vous ne voulez pas vous lancer dans le désassemblage ou la vue mixte.

YMMV par compilateur et débogueur et pour les compilations optimisées, bien sûr.


1
2018-05-23 14:55



Personnellement, je préfère que les affectations et les tests soient sur des lignes différentes. C'est moins syntaxiquement compliqué, moins sujet aux erreurs et plus facile à comprendre. Il permet également au compilateur de vous fournir des emplacements d’erreur / avertissement plus précis et facilite souvent le débogage.

Cela me permet aussi de faire plus facilement des choses comme:

int rc = function();

DEBUG_PRINT(rc);

if (rc == ERROR) {
    recover_from_error();
} else {
    keep_on_going(rc);
}

Je préfère ce style tellement que dans le cas des boucles je préfère:

while (1) {
    int rc = function();
    if (rc == ERROR) {
        break;
    }
    keep_on_going(rc);
}

que faire la cession dans le while conditionnel. Je n'aime vraiment pas que mes tests aient des effets secondaires.


1
2018-05-23 21:06



Je préfère souvent la première forme. Je ne pouvais pas dire exactement pourquoi, mais cela a quelque chose à voir avec la sémantique impliquée.

Le deuxième style me semble plus comme 2 opérations distinctes. Appelez la fonction puis faites quelque chose avec le résultat, 2 choses différentes. Dans le premier style, c'est une unité logique. Appeler la fonction, enregistrer le résultat temporaire et éventuellement gérer le cas d'erreur.

Je sais que c'est assez vague et loin d'être complètement rationnel, alors je vais utiliser l'un ou l'autre en fonction de l'importance de la variable enregistrée ou du test élémentaire.


0
2018-05-23 14:21



Je crois que la clarté devrait toujours être prioritaire sur les optimisations ou les «simplifications» basées uniquement sur la quantité de caractères saisis. Cette croyance m'a empêché de faire beaucoup de bêtises.

Séparer l'assignation et la comparaison rend à la fois plus clair et moins sujet aux erreurs, même si la duplication de la comparaison peut introduire une erreur de temps en temps. Entre autres choses, les parenthèses deviennent rapidement difficiles à distinguer et tout garder sur une seule ligne introduit davantage de parenthèses. De plus, le fractionnement limite les instructions à la recherche d'une seule valeur ou à son affectation.

Cependant, si vous vous attendez à ce que les gens qui lisent votre code soient plus à l’aise avec l’idiome d’une ligne, il est suffisamment répandu pour ne pas causer de problèmes à la plupart des programmeurs. C les programmeurs seront certainement au courant, même ceux qui pourraient trouver cela gênant.


0
2017-10-07 17:05