Qual è lo scopo di un blocco 'if (0)' nel blocco if-else?


141

La mia domanda riguarda la linea che ho citato nell'argomento e che posso vedere in molti punti all'interno del codice di produzione.

Il codice complessivo è simile al seguente:

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

Gli altri rami sono irrilevanti per la mia domanda. Mi chiedo quale sia il significato di mettere if (0)qui. Le parentesi graffe sono vuote, quindi non credo che dovrebbe commentare un blocco di codice. Forza il compilatore a fare qualche ottimizzazione o le sue intenzioni sono diverse?

Ho cercato di cercare questo caso esplicito qui su SO e su Internet, ma senza successo. Ci sono domande simili su JavaScript, ma non su C. C'è un'altra domanda, cosa succede quando uno zero viene assegnato in una condizione `if`? , ma tratta l'assegnazione zero a una variabile, non l'utilizzo 'if (0)' stesso.


2
Questa affermazione sembra irrilevante. Genera codice assembly con e senza quella dichiarazione e vedrai cosa sta succedendo sotto il cofano.
Accade il

2
È possibile che questo sia un codice generato automaticamente.
strano

Risposte:


91

A volte lo uso per simmetria in modo da poter spostare l'altro else if{liberamente con il mio editor senza preoccuparmi del primo if.

Semanticamente il

if (0) {
    // Empty braces
} else 

parte non fa nulla e puoi fare affidamento su ottimizzatori per eliminarlo.


239
Opinione personale: sebbene questo possa essere il codice motivo per cui è scritto così com'è, penso che sia una cattiva giustificazione. Il codice viene letto più spesso di quanto sia scritto, e questo codice non necessario aumenta solo il sovraccarico di analisi per il lettore.
user694733

13
@ user694733: Si potrebbe sostenere che il if elseprefisso comune a tutti i percorsi di codice significativi allinea le condizioni in modo corretto e semplifica la scansione. (Questo è soggettivo, però, e dipenderebbe molto da ciò che è veramente dentro le condizioni e i blocchi di codice.)
M Oehm

72
Non credo che if (0) {..}introduce alcun problema di leggibilità / leggibilità. Dovrebbe essere ovvio a chiunque conosca un po 'di C. Non è un problema. Il problema è la domanda di follow-up dopo averlo letto: "Che diavolo è allora?" A meno che non sia per scopi di debug / temporanei (cioè l'intenzione è quella di "abilitare" quel ifblocco in un secondo momento), raccomanderei di rimuovere del tutto. Fondamentalmente "leggere" un tale codice probabilmente causerebbe una "pausa" inutile per il lettore senza una buona ragione. E questo è un motivo abbastanza buono per rimuoverlo.
PP

77
Sembra che toglie definitivamente la leggibilità. È stato così male che ha inviato quel programmatore a SO per chiedere a cosa servisse. Non è un buon segno.
Vectorjohn,

26
Anche usando questo schema, non so se puoi "muoverti else ifnell'editor senza preoccupazioni" perché le condizioni potrebbero non escludersi a vicenda, nel qual caso l' ordine conta. Personalmente userei solo if, ed eseguivo il ritorno anticipato , estraendo la catena logica in una funzione separata se necessario.
John Wu,

105

Questo può essere utile se ci sono #ifdichiarazioni, ala

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

eccetera.

In questo caso, tutti (e tutti) i test possono essere #ifeliminati e il codice verrà compilato correttamente. Quasi tutti i compilatori rimuoveranno la if (0) {}parte. Un semplice autogeneratore potrebbe generare codice come questo, poiché è leggermente più semplice da codificare - non deve considerare separatamente il primo blocco abilitato.


5
In molti casi, una if/ else ifchain non viene utilizzata tanto come un albero decisionale, ma piuttosto come un costrutto "agisci sulla prima condizione corrispondente", in cui la condizione che ha la priorità più alta non è particolarmente "speciale". Anche se non l'ho visto if(0)usato come un modo per consentire a tutti i rami reali di avere una sintassi coerente, mi piace la sintassi coerente che facilita.
supercat

1
Non è nemmeno utile in questo caso perché puoi ottenere lo stesso effetto senza: basta dividere la else iflinea in due e mettere in mezzo la protezione del preprocessore.
Konrad Rudolph,

1
@KonradRudolph non sto seguendo; come lo scriveresti?
JiK,

1
@JiK Vorrei rimuovere il if (0)ramo e riformattare il resto in modo che elsesia sulla sua stessa linea, circondato da una guardia lungo le linee di #if TEST1_ENABLED && TEST2_ENABLED.
Konrad Rudolph,

5
@KonradRudolph va bene se vuoi raddoppiare il numero di guardie e triplicare il numero di condizioni di guardia menzionate, suppongo.
Hobbs

44

Ho visto un modello simile utilizzato nel codice generato. Ad esempio, in SQL, ho visto le librerie emettere la seguente whereclausola.

where 1 = 1

Questo presumibilmente rende più semplice aggiungere solo altri criteri, poiché è possibile anteporre a tutti i criteri aggiuntivi andanziché un controllo aggiuntivo per vedere se si tratta dei primi criteri o meno.


4
Il 1=1è anche "utile", perché si può sempre aggiungere il wheredavanti, incondizionatamente. Altrimenti dovresti controllare se è vuoto e in tal caso evita di generare la whereclausola.
Bakuriu,

2
Inoltre, la maggior parte dei database "rimuoverà automaticamente" 1=1da WHERE, quindi non ha alcun impatto sulle prestazioni.
Finanzi la causa di Monica il

7
Ciò è accettabile in una libreria che genera automaticamente query SQL che molto probabilmente non verranno mai viste nemmeno dal team DevOps. Non è "accettabile" nel codice di alto livello che deve essere scritto e letto più volte.
phagio,

Questo è un approccio molto utile quando si genera un tipo di SQL dinamico con un numero sconosciuto di condizioni finali.
Skipper

1
@freakish in effetti ho scritto il contrario: la sintassi scarsamente leggibile è accettabile nel codice generato poiché molto probabilmente non verrà mai letta, non nel codice funzionale di alto livello gestito dagli sviluppatori.
phagio

44

Come scritto, la if (0) {}clausola non si compila nel nulla.

Sospetto che la funzione della clausola nella parte superiore di questa scala sia quella di fornire un posto facile per disabilitare temporaneamente tutte le altre funzionalità contemporaneamente (per scopi di debug o confronto) cambiando 0in a 1o true.


2
Azzeccato. Non ho potuto vedere nessun altro motivo oltre al debug.
fino al

16

Non sono sicuro di alcuna ottimizzazione, ma i miei due centesimi:

Ciò è accaduto a causa di alcune modifiche al codice, in cui è stata rimossa una condizione primaria, ( ifdiciamo la funzione nel blocco iniziale ), ma gli sviluppatori / manutentori

  • erano pigri per ristrutturare il if-elseblocco
  • non volevo scendere nel conteggio delle filiali

così invece di rimuovere il ifblocco associato , hanno semplicemente cambiato la condizione if(0)e sono passati.


3
Non si if(0)riduce anche la copertura delle filiali?
David Szalai,

1
@DavidSzalai Non del tutto - al massimo diminuirà di 1 (rispetto ai precedenti 2) - ma per la copertura delle informazioni sarà comunque richiesto un colpo per la copertura.
Sourav Ghosh

15

È il marciume del codice.

Ad un certo punto che "se" ha fatto qualcosa di utile, la situazione è cambiata, forse la variabile da valutare è stata rimossa.

La persona che stava riparando / modificando il sistema ha fatto il meno possibile per influenzare la logica del sistema, quindi si è assicurato che il codice si ricompilasse. Quindi lascia un "if (0)" perché è facile e veloce e non è del tutto sicuro di quello che vuole fare. Fa funzionare il sistema e non torna a ripararlo completamente.

Quindi arriva lo sviluppatore successivo e pensa che sia stato fatto deliberatamente e commenta solo quella parte del codice (dal momento che non viene valutato comunque), quindi la volta successiva che il codice viene toccato quei commenti vengono rimossi.


2
Sì. Per il codice antico, apporta una modifica per rimuovere il codice morto alla volta. Non riesco a contare il numero di volte in cui ho scatenato una furia a squarciagola contro il codice "morto" solo per scoprire che c'era qualche bizzarro effetto collaterale a cui mancavano tagli e bruciature.
Julie ad Austin, il

15

Una possibilità non ancora menzionata: la if (0) {linea potrebbe fornire un punto conveniente per un punto di interruzione.

Il debug viene spesso eseguito su codice non ottimizzato, quindi il test sempre falso sarà presente e in grado di impostare il punto di interruzione su di esso. Una volta compilato per la produzione, la riga di codice verrebbe ottimizzata. La linea apparentemente inutile offre funzionalità per sviluppo e test di build senza influire sulle build di rilascio.

Ci sono anche altri buoni suggerimenti sopra; l'unico modo per sapere veramente quale sia lo scopo è rintracciare l'autore e chiedere. Il tuo sistema di controllo del codice sorgente potrebbe aiutarti in questo. (Cerca blamefunzionalità di tipo.)


9

Ho visto blocchi di codice non raggiungibili in JavaScript pre-espanso che sono stati generati usando un linguaggio di template.

Ad esempio, il codice che stai leggendo avrebbe potuto essere incollato da un server che ha pre-valutato la prima condizione che a quel tempo si basava su una variabile disponibile solo sul lato server.

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

che una volta pre-compilato hences:

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

spero che questo ti aiuti a relativizzare la potenziale attività di tastiera bassa dell'era dei programmatori pro-riciclaggio per la quale manifesto entusiasmo!


1
Sono d'accordo, nell'era dell'automazione onnipresente dovremmo fare maggiormente affidamento sul codice autogenerato, in quanto ci consente di dedicare più tempo alle cose reali. Ma per ora, il mio esatto punto di interesse è come tutto questo è progettato sotto il cofano.
Zzaponka,

8

Tale costrutto può anche essere usato in C per implementare la programmazione generica con sicurezza di tipo, basandosi sul fatto che il compilatore controlla ancora il codice non raggiungibile:

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

6

Penso che sia solo un brutto codice. Scrivendo un rapido esempio in Compiler Explorer, vediamo che sia in gcc che in clang non viene generato alcun codice per il if (0)blocco, anche con ottimizzazioni completamente disabilitate:

https://godbolt.org/z/PETIks

Giocare con la rimozione delle if (0)cause non modifica il codice generato, quindi concludo che questa non è un'ottimizzazione.

È possibile che ci fosse qualcosa nel ifblocco superiore che è stato successivamente rimosso. In breve, sembra che rimuoverlo provocherebbe la generazione dello stesso codice esatto, quindi sentiti libero di farlo.


6

Come è stato detto, lo zero viene valutato come falso e il ramo sarà probabilmente ottimizzato dal compilatore.

L'ho già visto prima nel codice in cui è stata aggiunta una nuova funzionalità ed era necessario un kill-switch (se qualcosa non funziona con la funzione, è possibile disattivarla) e qualche tempo dopo, quando è stato rimosso il kill switch il programmatore non ha rimosso anche il ramo, ad es

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

divenne

if (0) {
   // empty
} else if (some_fn()) {
   ...

1

Aiuta a eseguire il debug di questo blocco semplicemente inserendo il blocco 1. Questo disabilita tutte le funzionalità del blocco if else. E inoltre possiamo espandere il blocco if else.


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. 

1

La risposta di @ PSkocik va bene, ma aggiungo i miei due centesimi. Non sono sicuro se dovrei farlo come commento o come risposta; scegliendo quest'ultimo, perché IMHO merita di essere visto dagli altri, mentre i commenti sono spesso invisibili.

Non solo lo uso occasionalmente

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

Ma lo faccio anche occasionalmente

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

o

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

per condizioni complicate. Per gli stessi motivi: più facile da modificare, #ifdef, ecc.

Del resto, in Perl lo farò

@array = (  
    elem1,
    elem2,
    ...
    elem1,
) {
  • notare la virgola alla fine dell'elenco. Dimentico se le virgole sono separatori o delimitatori negli elenchi C e C ++. IMHO questa è una cosa che abbiamo imparato: [ Le virgole finali in Perl sono una cattiva pratica? virgole] sono una buona cosa. Come ogni nuova notazione, ci vuole un po 'per abituarsi.

Confronto il if(0)codice con lisp

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

che, avete indovinato, potrei indentare come

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

A volte ho provato a immaginare quale potrebbe essere una sintassi più umana leggibile per questo.

Forse

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

ispirato da Dikstra [ https://en.wikipedia.org/wiki/Guarded_Command_Language#Selection:_if lasting[Guarded Command Language].

Ma questa sintassi implica che le condizioni siano valutate in parallelo, mentre if...else-ifimplica una valutazione sequenziale e prioritaria delle condizioni.

Ho iniziato a fare questo genere di cose durante la scrittura di programmi che hanno generato altri programmi, dove è particolarmente conveniente.

Mentre ci siamo, quando scrivo RTL usando il vecchio iHDL di Intel, ho codificato cose come

   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

dove FORC..DOC..ENDCè un costrutto per il ciclo del preprocessore macro, che si espande in

   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

Si trattava di un codice a assegnazione singola, non imperativo, quindi l'impostazione di una variabile di stato non era consentita, se era necessario eseguire operazioni come trovare il primo bit impostato.

   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

Vieni a pensarci bene, questo potrebbe essere stato il primo posto in cui ho incontrato tali costrutti.

A proposito, le obiezioni che alcuni avevano sullo stile if (0) - che le condizioni else-if sono sequenzialmente dipendenti e non possono essere arbitrariamente riordinate - non si applicano alla logica AND e OR e XOR in RTL - ma si applicano a short- circuito && e ||.


-1

Ho visto questo usato per gestire gli errori, per esempio

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

if(condition_fails)
   goto lable1;

Questo può essere utile quando goto viene utilizzato per gestire gli errori, le istruzioni vengono eseguite solo quando si verifica un errore. L'ho visto in un codice C molto vecchio (dove gli argomenti della funzione sono scritti al di fuori del '()'), non credo che nessuno lo segua ora.


-2

L'ho visto un paio di volte, penso che la ragione più probabile sia che stia valutando qualcosa in una versione / ramo del codice precedente / diverso, o forse per il debug, e cambiarlo in if(0)è un modo un po 'pigro di rimuovere qualsiasi cosa fosse lì .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.