À quoi sert un bloc 'if (0)' dans un bloc if-else?

141

Ma question porte sur la ligne que j'ai mentionnée dans le sujet et que je peux voir à de nombreux endroits dans le code de production.

Le code global ressemble à ceci:

if (0) {
    // Empty braces
} else if (some_fn_call()) {
    // actual code
} else if (some_other_fn_call()) {
    // another actual code
    ...
} else {
    // default case
}

Les autres branches ne sont pas pertinentes pour ma question. Je me demande quel est le sens de mettre if (0)ici. Les accolades sont vides, donc je ne pense pas qu'il soit censé commenter un bloc de code. Est-ce que cela force le compilateur à faire une optimisation ou ses intentions sont-elles différentes?

J'ai essayé de rechercher ce cas explicite ici sur SO et sur Internet, mais sans succès. Il y a des questions similaires sur JavaScript, mais pas C. Il y a une autre question, que se passe-t-il quand un zéro est attribué dans une condition `if`? , mais il traite de l'affectation de zéro à une variable, pas de l'utilisation de «if (0)» elle-même.

Zzaponka
la source
2
Cette déclaration ne semble pas pertinente. Générez du code d'assemblage avec et sans cette déclaration et vous verrez ce qui se passe sous le capot.
haccks
2
Il est possible que ce soit un code généré automatiquement.
freakish

Réponses:

91

J'utilise parfois ceci pour la symétrie afin que je puisse déplacer l'autre else if{ librement avec mon éditeur sans avoir à me soucier du premierif .

Sémantiquement le

if (0) {
    // Empty braces
} else 

part ne fait rien et vous pouvez compter sur les optimiseurs pour le supprimer.

PSkocik
la source
239
Opinion personnelle: Bien que cela puisse être la raison pour laquelle il est écrit tel quel, je pense que c'est une mauvaise justification. Le code est lu plus souvent qu'il n'est écrit, et ce code inutile ne fait qu'augmenter la charge d'analyse pour le lecteur.
user694733
13
@ user694733: Vous pourriez soutenir que le if elsepréfixe commun à tous les chemins de code significatifs aligne bien les conditions et facilite leur analyse. (C'est subjectif, cependant, et dépendrait beaucoup de ce qui est vraiment à l'intérieur des conditions et des blocs de code.)
M Oehm
72
Je ne pense pas qu'il y if (0) {..}ait de problème de parsabilité / lisibilité. Cela devrait être évident pour quiconque connaît un peu C. Ce n'est pas un problème. Le problème est la question de suivi après l'avoir lu: "Pourquoi diable est-ce alors?" À moins que ce ne soit à des fins de débogage / temporaires (c'est-à-dire que l'intention est «d'activer» ce ifbloc plus tard), je recommanderais la suppression complète. Fondamentalement, "lire" un tel code provoquerait probablement une "pause" inutile pour le lecteur sans raison valable. Et c'est une raison suffisante pour le supprimer.
PP
77
On dirait que cela nuit définitivement à la lisibilité. C'était si mauvais que cela a envoyé ce programmeur au SO pour lui demander à quoi cela servait. Pas bon signe.
Vectorjohn
26
Même en utilisant ce modèle, je ne sais pas si vous pouvez "vous déplacer else ifdans l'éditeur sans souci" car les conditions peuvent ne pas être mutuellement exclusives, auquel cas l' ordre est important. Personnellement, je n'utiliserais ifet effectuerais un retour anticipé , en extrayant la chaîne logique vers une fonction distincte si nécessaire.
John Wu
105

Cela peut être utile s'il y a des #ifdéclarations, ala

   if (0)
   {
       // Empty block
   }
#if TEST1_ENABLED
   else if (test1())
   {
      action1();
   }
#endif
#if TEST2_ENABLED
   else if (test2())
   {
      action2();
   }
#endif

etc.

Dans ce cas, tous les tests (et tous) peuvent être #ifsupprimés et le code se compilera correctement. Presque tous les compilateurs supprimeront la if (0) {}pièce. Un simple générateur automatique pourrait générer un code comme celui-ci, car il est légèrement plus facile à coder - il n'a pas à considérer le premier bloc activé séparément.

CSM
la source
5
Dans de nombreux cas, une chaîne if/ else ifn'est pas tant utilisée comme un arbre de décision, mais plutôt comme une construction «agir sur la première condition de correspondance», où la condition qui se trouve avoir la priorité la plus élevée n'est pas particulièrement «spéciale». Même si je n'avais pas vu if(0)utilisé comme un moyen de permettre à toutes les branches réelles d'avoir une syntaxe cohérente, j'aime la syntaxe cohérente qu'elle facilite.
supercat du
1
Ce n'est même pas utile dans ce cas car vous pouvez obtenir le même effet sans: il vous suffit de diviser la else ifligne en deux et de mettre la garde du préprocesseur entre les deux.
Konrad Rudolph
1
@KonradRudolph que je ne suis pas; comment l'écririez-vous?
JiK
1
@JiK Je supprimerais la if (0)branche et reformaterais le reste de manière à ce qu'il elsesoit sur sa propre ligne, entouré d'un garde dans le sens de #if TEST1_ENABLED && TEST2_ENABLED.
Konrad Rudolph
5
@KonradRudolph c'est bien si vous voulez doubler le nombre de gardes et tripler le nombre de conditions de garde mentionnées, je suppose.
hobbs
44

J'ai vu un modèle similaire utilisé dans le code généré. Par exemple, en SQL, j'ai vu des bibliothèques émettre la whereclause suivante .

where 1 = 1

Cela rend vraisemblablement plus facile d'ajouter simplement d'autres critères, car tous les critères supplémentaires peuvent être ajoutés au début au andlieu d'une vérification supplémentaire pour voir s'il s'agit du premier critère ou non.

Fleurs de Seth
la source
4
Le 1=1est également "utile" car vous pouvez toujours ajouter le wheredevant, sans condition. Sinon, vous devrez vérifier s'il est vide, et si c'est le cas, éviter de générer la whereclause.
Bakuriu
2
De plus, la plupart des bases de données "supprimeront" automatiquement le 1=1de WHERE, donc cela n'a pas d'impact sur les performances.
Fonder le procès de Monica le
7
Cela est acceptable dans une bibliothèque qui génère automatiquement des requêtes SQL qui ne sont probablement jamais vues, même par l'équipe DevOps. Ce n'est pas «acceptable» dans un code de haut niveau qui doit être écrit et lu plusieurs fois.
phagio
C'est une approche vraiment pratique lors de la génération d'une sorte de SQL dynamique avec un nombre inconnu de conditions finales.
Skipper
1
@freakish en effet j'ai écrit le contraire: une syntaxe mal lisible est acceptable dans le code généré car elle ne sera probablement jamais lue, pas dans un code fonctionnel de haut niveau maintenu par les développeurs.
phagio
44

Telle qu'elle est écrite, la if (0) {}clause ne se compile à rien.

Je soupçonne que la fonction de la clause en haut de cette échelle est de fournir un endroit facile pour désactiver temporairement toutes les autres fonctionnalités à la fois (à des fins de débogage ou de comparaison) en remplaçant le 0par un 1ou true.

Russell Borogove
la source
2
J'y suis arrivé. Je ne voyais aucune autre raison à part le débogage.
tfont
16

Je ne suis pas sûr des optimisations, mais mes deux cents:

Cela s'est produit à cause d'une modification du code, où une condition principale a été supprimée (l'appel de fonction dans le ifbloc initial , disons), mais les développeurs / mainteneurs

donc au lieu de supprimer le ifbloc associé , ils ont simplement changé la condition if(0)et sont passés à autre chose.

Sourav Ghosh
la source
3
La if(0)diminution de la couverture des succursales n'est-elle pas aussi?
David Szalai
1
@DavidSzalai Pas complètement - au plus il diminuera de 1 (par rapport aux 2 précédents) - mais un coup sera toujours nécessaire pour la couverture, au meilleur de ma connaissance.
Sourav Ghosh
15

C'est la pourriture du code.

À un moment donné, «si» faisait quelque chose d'utile, la situation a changé, peut-être que la variable évaluée a été supprimée.

La personne qui réparait / changeait le système a fait le moins possible pour affecter la logique du système donc il s'est simplement assuré que le code serait recompilé. Alors il laisse un "si (0)" parce que c'est rapide et facile et il n'est pas totalement sûr que ce soit ce qu'il veut faire. Il fait fonctionner le système et il ne revient pas en arrière pour le réparer complètement.

Ensuite, le développeur suivant arrive et pense que cela a été fait délibérément et ne commente que cette partie du code (car elle n'est de toute façon pas évaluée), puis la prochaine fois que le code est touché, ces commentaires sont supprimés.

Matière noire
la source
2
Ouaip. Pour le code ancien, effectuez une modification de suppression de code mort à la fois. Je ne peux pas compter le nombre de fois où je suis allé sur un saccage de slash-and-burn contre le code "mort" seulement pour découvrir qu'il y avait un certain effet secondaire bizarre que le slashing-and-burn manquait.
Julie à Austin le
15

Une possibilité non encore mentionnée: la if (0) {ligne pourrait fournir un emplacement pratique pour un point d'arrêt.

Le débogage est souvent effectué sur du code non optimisé, de sorte que le test toujours faux sera présent et pourra avoir un point d'arrêt défini. Une fois compilée pour la production, la ligne de code serait optimisée. La ligne apparemment inutile donne des fonctionnalités pour le développement et le test des versions sans impact sur les versions de version.

Il y a aussi d'autres bonnes suggestions ci-dessus; la seule façon de vraiment savoir quel est le but est de retrouver l'auteur et de demander. Votre système de contrôle de code source pourrait vous aider. (Recherchez la blamefonctionnalité -type.)

studog
la source
9

J'ai vu des blocs de code non accessibles dans JavaScript pré-développé qui ont été générés à l'aide d'un langage de modélisation.

Par exemple, le code que vous lisez pourrait avoir été collé à partir d'un serveur qui a pré-évalué la première condition qui, à ce moment-là, reposait sur une variable disponible uniquement côté serveur.

if ( ${requestIsNotHttps} ){ ... }else if( ...

qui une fois pré-compilé hences:

if ( 0 ){ ... }else if ( ...

J'espère que cela vous aidera à relativiser la faible activité potentielle du clavier de l'ère des codeurs pro-recyclage pour laquelle je manifeste de l'enthousiasme!

simonarame
la source
1
Je suis d'accord, à l'ère de l'automatisation omniprésente, nous devrions davantage nous fier au code généré automatiquement, car cela nous permet de passer plus de temps sur des choses réelles. Mais pour l'instant, mon point d'intérêt exact est de savoir comment tout cela est architecturé sous le capot.
Zzaponka
8

Cette construction peut également être utilisée en C pour implémenter la programmation générique avec la sécurité de type, en s'appuyant sur le fait que le code inaccessible est toujours vérifié par le compilateur:

// this is a generic unsafe function, that will call fun(arg) at a later time
void defer(void *fun, void *arg);

// this is a macro that makes it safer, by checking the argument
// matches the function signature
#define DEFER(f, arg) \
   if(0) f(arg); \              // never actually called, but compile-time checked
   else defer(f, (void *)arg);  // do the unsafe call after safety check

void myfunction(int *p);

DEFER(myfunction, 42);     // compile error
int *b;
DEFER(myfunction, b);      // compiles OK
philfr
la source
6

Je pense que c'est juste un mauvais code. En écrivant un exemple rapide dans Compiler Explorer, nous voyons que dans gcc et clang aucun code n'est généré pour le if (0)bloc, même avec les optimisations complètement désactivées:

https://godbolt.org/z/PETIks

Jouer avec la suppression des if (0)causes ne modifie pas le code généré, je conclus donc que ce n'est pas une optimisation.

Il est possible qu'il y ait eu quelque chose dans le ifbloc supérieur qui a ensuite été supprimé. En bref, il semble que sa suppression entraînerait la génération du même code, alors n'hésitez pas à le faire.

cha0site
la source
6

Comme cela a été dit, le zéro est évalué à faux et la branche sera probablement optimisée par le compilateur.

J'ai également vu cela auparavant dans le code où une nouvelle fonctionnalité a été ajoutée et un kill-switch était nécessaire (si quelque chose ne va pas avec la fonctionnalité, vous pouvez simplement la désactiver), et quelque temps plus tard, lorsque le kill-switch a été supprimé le le programmeur n'a pas également supprimé la branche, par exemple

if (feature_a_active()) {
    use_feature_a();
} else if (some_fn()) {
   ...

est devenu

if (0) {
   // empty
} else if (some_fn()) {
   ...
sergiopme
la source
1

Cela aide à déboguer ce bloc en mettant simplement le bloc if 1. Cela désactive la fonctionnalité de blocage de tous les if else. Et nous pouvons également étendre le bloc if else.

Abdul Ahad Sheikh
la source
1
    Actually according to my opinion, if we put any variable for checking inside
    e.g:-
public static void main(string args[])
{
        var status;
        var empList=_unitofWork.EmpRepository.Get(con=>con.isRetired==true);
        //some code logic 
        if(empList.count>0)
        {
          status=true;
        }
        if(status)
        {
         //do something
        }
        else
        {
        //do something else
        }
}
     if then its dynamically get the value in run time and invoke the logic inside it, else its simply extra line of code i guess.

    Anybody have any depth knowledge why this thing is used....or agree with me.
    kindly respond. 
Sagar Kumar Choudhury
la source
1

La réponse de @ PSkocik est très bien, mais j'ajoute mes deux cents. Je ne sais pas si je devrais le faire en commentaire ou en réponse; choisir ce dernier, car IMHO vaut la peine d'être vu, alors que les commentaires sont souvent invisibles.

Non seulement j'utilise occasionnellement

if(0) {
   //deliberately left empty
} else if( cond1 ) {
   //deliberately left empty
} else if( cond2 ) {
   //deliberately left empty
...
} else {
   // no conditions matched
}

Mais je fais aussi parfois

if( 1 
    && cond1 
    && cond2
    ...
    && condN
) {

ou

if( 0 
    || cond1 
    || cond2
    ...
    || condN
) {

pour des conditions compliquées. Pour les mêmes raisons - plus facile à modifier, #ifdef, etc.

D'ailleurs, en Perl, je ferai

@array = (  
    elem1,
    elem2,
    ...
    elem1,
) {
  • notez la virgule à la fin de la liste. J'oublie si les virgules sont des séparateurs ou des délimiteurs dans les listes C et C ++. À mon humble avis, c'est une chose que nous avons apprise: [ Les virgules en Perl sont-elles une mauvaise pratique? virgules] sont une bonne chose. Comme toute nouvelle notation, il faut un certain temps pour s'y habituer.

Je compare le if(0)code à lisp

(cond   (test1    action1)
   (test2    action2)
   ...
   (testn   actionn))

qui, vous l'avez deviné, je peux indenter comme

(cond   
   (test1    action1)
   (test2    action2)
   ...
   (testn   actionn)
)

J'ai parfois essayé d'imaginer à quoi pourrait ressembler une syntaxe plus lisible par l'homme.

Peut-être

IF
:: cond1 THEN code1
:: cond2 THEN code2
...
:: condN THEN codeN
FI

inspiré du [ https://en.wikipedia.org/wiki/Guarded_Command_Language#Selection:_if[Guarded Command Language] de Dikstra ).

Mais cette syntaxe implique que les conditions sont évaluées en parallèle, alors qu'elle if...else-ifimplique une évaluation séquentielle et hiérarchisée des conditions.

J'ai commencé à faire ce genre de choses en écrivant des programmes qui généraient d'autres programmes, là où c'est particulièrement pratique.

Pendant que nous y sommes, lors de l'écriture de RTL en utilisant l'ancien iHDL d'Intel, j'ai codé des trucs comme

   IF 0 THEN /*nothing*/
   **FORC i FROM 1 TO 10 DOC** 
   ELSE IF signal%i% THEN    
      // stuff to do if signal%i% is active
   **ENDC** 
   ELSE   
      // nothing matched 
   ENDIF

FORC..DOC..ENDCest une construction de boucle de préprocesseur de macro, qui se développe en

   IF 0 THEN /*nothing*/
   ELSE IF signal1 THEN    
      // stuff to do if signal1 is active
   ELSE IF signal2 THEN    
      // stuff to do if signal2 is active
   ...
   ELSE IF signal100 THEN    
      // stuff to do if signal100 is active
   ELSE   
      // nothing matched 
   ENDIF

Il s'agissait d'un code à affectation unique, non impératif, donc la définition d'une variable d'état n'était pas autorisée si vous deviez faire des choses comme trouver le premier bit défini.

   IF 0 THEN /*nothing*/
   ELSE IF signal1 THEN    
      found := 1
   ELSE IF signal2 THEN    
      found := 2
   ...
   ELSE IF signal100 THEN    
      found := 100
   ELSE   
      // nothing matched 
   ENDIF

À bien y penser, c'est peut-être le premier endroit où j'ai rencontré de telles constructions.

BTW, les objections que certains avaient au style if (0) - que les conditions else-if sont séquentiellement dépendantes et ne peuvent pas être arbitrairement réordonnées - ne s'appliquent pas aux logiques AND et OR et XOR dans RTL - mais s'appliquent à short- circuit && et ||.

Krazy Glew
la source
-1

J'ai vu cela utilisé pour gérer les erreurs, par exemple

if(0){
lable1:
   //do something
}
if(0){
lable2:
   //do something
}
.
.
and so on.

if(condition_fails)
   goto lable1;

Cela peut être utile lorsque goto est utilisé pour gérer les erreurs, les instructions ne sont exécutées que lorsqu'une erreur se produit. J'ai vu cela dans un très vieux code C (où les arguments de fonction sont écrits en dehors du '()'), ne pensez pas que quiconque le suive maintenant.

Jayachandra M
la source
-2

J'ai vu cela plusieurs fois, je pense que la raison la plus probable est qu'il évaluait quelque chose dans une version / branche ancienne / différente du code, ou peut-être pour le débogage, et le changer en if(0)est un moyen un peu paresseux de supprimer tout ce qui était là .

John U
la source