Was ist der Zweck eines 'if (0)' - Blocks im if-else-Block?

141

Meine Frage bezieht sich auf die Zeile, die ich im Betreff erwähnt habe und die ich an vielen Stellen im Produktionscode sehen kann.

Der Gesamtcode sieht folgendermaßen aus:

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

Die anderen Zweige sind für meine Frage irrelevant. Ich frage mich, was es bedeutet, if (0)hier zu setzen . Die geschweiften Klammern sind leer, daher denke ich nicht, dass sie einen Codeblock kommentieren sollen. Erzwingt es den Compiler, eine Optimierung vorzunehmen, oder unterscheiden sich seine Absichten?

Ich habe versucht, diesen expliziten Fall hier auf SO und im Internet zu suchen, aber ohne Erfolg. Es gibt ähnliche Fragen zu JavaScript, aber nicht zu C. Es gibt eine andere Frage: Was passiert, wenn eine Null in einer Wenn-Bedingung zugewiesen wird? Es wird jedoch die Zuweisung von Null zu einer Variablen erörtert, nicht die Verwendung von 'if (0)' selbst.

Zzaponka
quelle
2
Diese Aussage scheint irrelevant. Wenn Sie mit und ohne diese Anweisung Assembly-Code generieren, werden Sie sehen, was unter der Haube vor sich geht.
Haccks
2
Möglicherweise ist dies ein automatisch generierter Code.
freakish

Antworten:

91

Ich benutze dies manchmal aus Symmetriegründen, damit ich den anderen else if{mit meinem Editor frei bewegen kann, ohne auf den ersten achten zu müssen if.

Semantisch die

if (0) {
    // Empty braces
} else 

Teil macht nichts und Sie können sich darauf verlassen, dass Optimierer es löschen.

PSkocik
quelle
239
Persönliche Meinung: Obwohl dies der Grund dafür sein mag, warum es so geschrieben ist, denke ich, dass es eine schlechte Rechtfertigung ist. Code wird häufiger gelesen als geschrieben, und dieser unnötige Code erhöht nur den Analyseaufwand für den Leser.
user694733
13
@ user694733: Sie könnten argumentieren, dass das gemeinsame if elsePräfix für alle wichtigen Codepfade die Bedingungen gut ausrichtet und das Scannen erleichtert. (Das ist jedoch subjektiv und würde viel davon abhängen, was wirklich in den Bedingungen und Codeblöcken enthalten ist.)
M Oehm
72
Ich glaube nicht, if (0) {..}dass es Probleme mit der Parsierbarkeit / Lesbarkeit gibt. Es sollte jedem klar sein, der ein bisschen von C weiß. Das ist kein Problem. Das Problem ist die Folgefrage nach dem Lesen: "Wofür zum Teufel ist es dann?" Sofern es sich nicht um Debugging- / temporäre Zwecke handelt (dh die Absicht besteht darin, diesen ifBlock später zu "aktivieren" ), würde ich empfehlen , ihn insgesamt zu entfernen . Grundsätzlich würde das "Lesen" eines solchen Codes ohne guten Grund wahrscheinlich eine unnötige "Pause" für den Leser verursachen. Und das ist Grund genug, es zu entfernen.
PP
77
Scheint, als würde dies definitiv die Lesbarkeit beeinträchtigen. Es war so schlimm, dass dieser Programmierer zu SO geschickt wurde, um zu fragen, wofür es war. Kein gutes Zeichen.
Vectorjohn
26
Selbst wenn ich dieses Muster verwende, weiß ich nicht, ob Sie sich " else ifohne Sorgen im Editor bewegen können", da sich die Bedingungen möglicherweise nicht gegenseitig ausschließen. In diesem Fall ist die Reihenfolge wichtig. Persönlich würde ich nur verwenden ifund eine frühzeitige Rückkehr durchführen und die Logikkette bei Bedarf in eine separate Funktion extrahieren.
John Wu
105

Dies kann nützlich sein, wenn es #ifAussagen gibt, ala

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

etc.

In diesem Fall können alle (und alle) Tests #ifgelöscht werden, und der Code wird korrekt kompiliert. Fast alle Compiler entfernen das if (0) {}Teil. Ein einfacher Autogenerator könnte Code wie diesen generieren, da er etwas einfacher zu codieren ist - er muss den ersten aktivierten Block nicht separat betrachten.

CSM
quelle
5
In vielen Fällen wird eine if/ else ifKette nicht so sehr als Entscheidungsbaum verwendet, sondern als Konstrukt "Auf die erste übereinstimmende Bedingung einwirken", bei dem die Bedingung mit der höchsten Priorität nicht besonders "speziell" ist. Ich hatte zwar nicht gesehen if(0), dass alle realen Zweige eine konsistente Syntax haben, aber ich mag die konsistente Syntax, die sie ermöglicht.
Supercat
1
Dies ist in diesem Fall nicht einmal nützlich, da Sie den gleichen Effekt erzielen können, ohne: Teilen Sie einfach die else ifLinie in zwei Teile und setzen Sie den Präprozessorschutz dazwischen.
Konrad Rudolph
1
@KonradRudolph Ich folge nicht; wie würdest du es schreiben
JiK
1
@JiK Ich würde den if (0)Zweig entfernen und den Rest so neu formatieren, dass er elsesich in einer eigenen Zeile befindet, umgeben von einer Wache nach dem Vorbild von #if TEST1_ENABLED && TEST2_ENABLED.
Konrad Rudolph
5
@KonradRudolph, das ist in Ordnung, wenn Sie die Anzahl der Wachen verdoppeln und die Anzahl der genannten Wachbedingungen verdreifachen möchten, nehme ich an.
Hobbs
44

Ich habe ein ähnliches Muster gesehen, das im generierten Code verwendet wird. In SQL habe ich beispielsweise gesehen, dass Bibliotheken die folgende whereKlausel ausgeben .

where 1 = 1

Dies erleichtert vermutlich das Hinzufügen anderer Kriterien, da allen zusätzlichen Kriterien andanstelle einer zusätzlichen Prüfung vorangestellt werden kann, ob es sich um das erste Kriterium handelt oder nicht.

Seth Blumen
quelle
4
Das 1=1ist auch "nützlich", weil Sie das wherevorne immer bedingungslos hinzufügen können . Andernfalls müssten Sie überprüfen, ob es leer ist, und in diesem Fall vermeiden, die whereKlausel zu generieren .
Bakuriu
2
Darüber hinaus "entfernen" die meisten Datenbanken das automatisch 1=1aus dem WHERE, sodass es keinen Einfluss auf die Leistung hat.
Fund Monica Klage
7
Dies ist in einer Bibliothek akzeptabel, die automatisch SQL-Abfragen generiert, die selbst vom DevOps-Team höchstwahrscheinlich nie gesehen werden. Es ist nicht "akzeptabel" in High-Level-Code, der mehrmals geschrieben und gelesen werden muss.
Phagio
Dies ist ein sehr praktischer Ansatz, wenn Sie eine Art dynamisches SQL mit unbekannter Anzahl von Endbedingungen generieren .
Skipper
1
@freakish In der Tat habe ich das Gegenteil geschrieben: Eine schlecht lesbare Syntax ist im generierten Code akzeptabel, da sie höchstwahrscheinlich nie gelesen wird, nicht in Funktionscode auf hoher Ebene, der von Entwicklern verwaltet wird.
Phagio
44

Wie geschrieben, if (0) {}kompiliert die Klausel zu nichts.

Ich vermute, dass die Funktion der Klausel oben auf dieser Leiter darin besteht, einen einfachen Ort bereitzustellen, an dem alle anderen Funktionen auf einmal vorübergehend deaktiviert werden können (zu Debugging- oder Vergleichszwecken), indem 0in a 1oder geändert wird true.

Russell Borogove
quelle
2
Geschafft. Ich konnte keinen anderen Grund als das Debuggen erkennen.
tfont
16

Ich bin mir keiner Optimierung sicher, aber meine zwei Cent:

Dies geschah aufgrund einer Codeänderung, bei der eine Hauptbedingung entfernt wurde (der Funktionsaufruf im Anfangsblock if, sagen wir), aber die Entwickler / Betreuer

Anstatt den zugehörigen ifBlock zu entfernen , änderten sie einfach die Bedingung in if(0)und gingen weiter.

Sourav Ghosh
quelle
3
if(0)Verringert sich nicht auch die Filialabdeckung?
David Szalai
1
@DavidSzalai Nicht vollständig - höchstens um 1 (gegenüber den vorherigen 2) -, aber nach meinem besten Wissen ist immer noch ein Treffer für die Berichterstattung erforderlich.
Sourav Ghosh
15

Es ist Code Rot.

Irgendwann, als "wenn" etwas Nützliches tat, änderte sich die Situation, möglicherweise wurde die zu bewertende Variable entfernt.

Die Person, die das System reparierte / änderte, tat so wenig wie möglich, um die Logik des Systems zu beeinflussen , und stellte nur sicher, dass der Code neu kompiliert wurde. Also hinterlässt er ein "if (0)", weil das schnell und einfach geht und er nicht ganz sicher ist, ob er das tun möchte. Er bringt das System zum Laufen und geht nicht zurück, um es vollständig zu reparieren.

Dann kommt der nächste Entwickler und denkt, dass dies absichtlich gemacht wurde und kommentiert nur diesen Teil des Codes aus (da er sowieso nicht ausgewertet wird). Wenn der Code das nächste Mal berührt wird, werden diese Kommentare entfernt.

Dunkle Materie
quelle
2
Jep. Nehmen Sie für alten Code jeweils eine Änderung zum Entfernen von toten Codes vor. Ich kann nicht zählen, wie oft ich einen Brandrodungs-Amoklauf gegen "toten" Code unternommen habe, nur um festzustellen, dass es einen bizarren Nebeneffekt gab, den Brandrodung übersehen hat.
Julie in Austin
15

Eine noch nicht erwähnte Möglichkeit: Die if (0) {Linie könnte einen geeigneten Ort für einen Haltepunkt bieten.

Das Debuggen wird häufig für nicht optimierten Code durchgeführt, sodass der immer falsche Test vorhanden ist und ein Haltepunkt festgelegt werden kann. Bei der Kompilierung für die Produktion würde die Codezeile optimiert. Die scheinbar nutzlose Zeile bietet Funktionen zum Entwickeln und Testen von Builds, ohne die Release-Builds zu beeinträchtigen.

Es gibt auch andere gute Vorschläge oben; Der einzige Weg, um wirklich zu wissen, was der Zweck ist, besteht darin, den Autor aufzuspüren und zu fragen. Ihr Quellcode-Kontrollsystem könnte dabei helfen. (Suchen Sie nach blameFunktionen vom Typ.)

studog
quelle
9

Ich habe nicht erreichbare Codeblöcke in vorerweitertem JavaScript gesehen, die mit einer Vorlagensprache generiert wurden.

Beispielsweise könnte der Code, den Sie lesen, von einem Server eingefügt worden sein, der die erste Bedingung vorab ausgewertet hat, die zu diesem Zeitpunkt auf einer Variablen beruhte, die nur auf der Serverseite verfügbar war.

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

die einmal vorkompilierte Hences:

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

Ich hoffe, dies hilft Ihnen dabei, die potenziell niedrige Tastaturaktivität der Pro-Recycling-Codierer-Ära zu relativieren, für die ich Begeisterung zeige!

Simonarame
quelle
1
Ich stimme zu, im Zeitalter der allgegenwärtigen Automatisierung sollten wir uns mehr auf automatisch generierten Code verlassen, da wir so mehr Zeit für tatsächliche Dinge aufwenden können. Aber im Moment ist mein genaues Interesse, wie dies alles unter der Haube aufgebaut ist.
Zzaponka
8

Dieses Konstrukt kann auch in C verwendet werden, um generische Programmierung mit Typensicherheit zu implementieren, wobei davon ausgegangen wird, dass der nicht erreichbare Code noch vom Compiler überprüft wird:

// 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
quelle
6

Ich denke, es ist nur schlechter Code. Wenn wir ein kurzes Beispiel im Compiler-Explorer schreiben, sehen wir, dass sowohl in gcc als auch in clang kein Code für den if (0)Block generiert wird , selbst wenn die Optimierungen vollständig deaktiviert sind:

https://godbolt.org/z/PETIks

Das Herumspielen mit dem Entfernen der if (0)Ursachen führt zu keinen Änderungen am generierten Code, daher komme ich zu dem Schluss, dass dies keine Optimierung ist.

Es ist möglich, dass sich im oberen ifBlock etwas befand, das später entfernt wurde. Kurz gesagt, es sieht so aus, als würde durch das Entfernen genau derselbe Code generiert. Sie können dies also jederzeit tun.

cha0site
quelle
6

Wie bereits erwähnt, wird die Null als falsch ausgewertet, und der Zweig wird wahrscheinlich vom Compiler optimiert.

Ich habe dies auch schon einmal in Code gesehen, in dem eine neue Funktion hinzugefügt wurde und ein Kill-Schalter benötigt wurde (wenn etwas mit der Funktion schief geht, können Sie sie einfach ausschalten), und einige Zeit später, als der Kill-Schalter entfernt wurde Der Programmierer hat den Zweig nicht entfernt, z

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

wurden

if (0) {
   // empty
} else if (some_fn()) {
   ...
Sergiopm
quelle
1

Es ist hilfreich, diesen Block zu debuggen, indem nur if 1 gesetzt wird. Dadurch wird die Blockfunktionalität deaktiviert. Und wir können auch den if else-Block erweitern.

Abdul Ahad Sheikh
quelle
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
quelle
1

@ PSkociks Antwort ist in Ordnung, aber ich addiere meine zwei Cent. Unsicher, ob ich dies als Kommentar oder als Antwort tun soll; Wählen Sie Letzteres, weil IMHO andere sehenswert sind, während Kommentare häufig unsichtbar sind.

Ich benutze nicht nur gelegentlich

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

Aber ich mache es auch gelegentlich

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

oder

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

für komplizierte Bedingungen. Aus den gleichen Gründen - einfacher zu bearbeiten, #ifdef usw.

Im Übrigen werde ich in Perl tun

@array = (  
    elem1,
    elem2,
    ...
    elem1,
) {
  • Beachten Sie das Komma am Ende der Liste. Ich vergesse, ob Kommas Trennzeichen oder Trennzeichen in C- und C ++ - Listen sind. IMHO ist dies eine Sache, die wir gelernt haben: [ Sind nachgestellte Kommas in Perl eine schlechte Praxis? Kommas] sind eine gute Sache. Wie bei jeder neuen Notation dauert es eine Weile, bis man sich daran gewöhnt hat.

Ich vergleiche den if(0)Code mit lisp

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

was, Sie haben es erraten, ich kann einrücken als

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

Ich habe manchmal versucht mir vorzustellen, wie eine besser lesbare Syntax dafür aussehen könnte.

Vielleicht

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

inspiriert von Dikstras [ https://en.wikipedia.org/wiki/Guarded_Command_Language#Selection:_if‹[Guarded Command Language].

Diese Syntax impliziert jedoch, dass die Bedingungen parallel ausgewertet werden, während if...else-ifdie sequentielle und priorisierte Bewertung der Bedingungen impliziert wird.

Ich habe so etwas angefangen, als ich Programme geschrieben habe, die andere Programme generiert haben, wo es besonders praktisch ist.

Während wir gerade dabei sind, RTL mit Intels alter iHDL zu schreiben, habe ich Sachen wie codiert

   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

Dabei FORC..DOC..ENDChandelt es sich um ein Makro-Präprozessor-Schleifenkonstrukt, das auf erweitert wird

   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

Dies war eine nicht zwingende Einzelzuweisung, daher war das Festlegen einer Statusvariablen nicht zulässig, wenn Sie beispielsweise das erste gesetzte Bit suchen mussten.

   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

Wenn ich daran denke, war dies vielleicht der erste Ort, an dem ich auf solche Konstrukte gestoßen bin.

Übrigens gelten die Einwände, die einige gegen den if (0) -Stil hatten - dass die else-if-Bedingungen sequentiell abhängig sind und nicht willkürlich neu angeordnet werden können - nicht für die AND- und OR- und XOR-Logik in RTL - sondern für Short- Schaltung && und ||.

Krazy Glew
quelle
-1

Ich habe gesehen, dass dies zum Beispiel zur Behandlung von Fehlern verwendet wurde

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

if(condition_fails)
   goto lable1;

Dies kann hilfreich sein, wenn goto zum Verwalten von Fehlern verwendet wird. Anweisungen werden nur ausgeführt, wenn ein Fehler auftritt. Ich habe dies in sehr altem C-Code gesehen (wo Funktionsargumente außerhalb von '()' geschrieben sind), glaube nicht, dass jetzt jemand dem folgt.

Jayachandra M.
quelle
-2

Ich habe dies einige Male gesehen. Ich denke, der wahrscheinlichste Grund ist, dass etwas in einer älteren / anderen Version / einem anderen Zweig des Codes ausgewertet oder möglicherweise zum Debuggen verwendet wurde. Das Ändern in if(0)ist eine etwas träge Methode, um alles zu entfernen, was vorhanden war .

John U.
quelle