Qual é o objetivo de um bloco 'if (0)' no bloco if-else?


141

Minha pergunta é sobre a linha que mencionei no assunto e que posso ver em muitos lugares dentro do código de produção.

O código geral fica assim:

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

Os outros ramos são irrelevantes para a minha pergunta. Gostaria de saber qual é o significado de colocar if (0)aqui. As chaves estão vazias, então não acho que deva comentar algum bloco de código. Isso força o compilador a fazer alguma otimização ou suas intenções são diferentes?

Eu tentei procurar esse caso explícito aqui no SO e na internet, mas sem sucesso. Há perguntas semelhantes sobre JavaScript, mas não C. Há outra pergunta: o que acontece quando um zero é atribuído em uma condição `if`? , mas discute a atribuição zero a uma variável, não o uso 'if (0)' em si.


2
Essa afirmação parece irrelevante. Gere código de montagem com e sem essa instrução e você verá o que está acontecendo sob o capô.
haccks

2
É possível que este seja um código gerado automaticamente.
freakish

Respostas:


91

Às vezes, uso isso como simetria para poder mover o outro else if{livremente com o meu editor sem ter que me preocupar com o primeiro if.

Semanticamente o

if (0) {
    // Empty braces
} else 

parte não faz nada e você pode contar com otimizadores para excluí-lo.


239
Opinião pessoal: Embora esse possa ser o código do motivo de sua escrita, acho que é uma justificativa ruim. O código é lido com mais frequência do que está escrito, e esse código desnecessário apenas aumenta a sobrecarga de análise para o leitor.
user694733

13
@ user694733: Você poderia argumentar que o if elseprefixo comum a todos os caminhos de código significativos alinha bem as condições e facilita a verificação. (Isso é subjetivo, embora, e vai depender muito do que é realmente dentro das condições e blocos de código.)
M Oehm

72
Acho que não if (0) {..}apresenta nenhum problema de parsabilidade / legibilidade. Deveria ser óbvio para quem conhece um pouco de C. Isso não é problema. O problema é a pergunta seguinte após a leitura: "Para que diabos é então?" A menos que seja para fins de depuração / temporários (ou seja, a intenção é "ativar" esse ifbloco posteriormente)), eu recomendaria a remoção por completo. Basicamente, "ler" esse código provavelmente causaria uma "pausa" desnecessária para o leitor, sem uma boa razão. E esse é um motivo suficientemente bom para removê-lo.
PP

77
Parece que definitivamente prejudica a legibilidade. Foi tão ruim que enviou o programador à SO para perguntar para que era. Não é um bom sinal.
Vectorjohn

26
Mesmo usando esse padrão, não sei se você pode "se movimentar else ifpelo editor sem se preocupar" porque as condições podem não ser mutuamente exclusivas; nesse caso, a ordem é importante. Pessoalmente, eu usaria apenas ife executaria o retorno antecipado , extraindo a cadeia lógica para uma função separada, se necessário.
John Wu

105

Isso pode ser útil se houver #ifinstruções, ala

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

etc.

Nesse caso, todos (e todos) os testes podem ser #ifexecutados e o código será compilado corretamente. Quase todos os compiladores removerão a if (0) {}peça. Um gerador automático simples pode gerar código como este, pois é um pouco mais fácil de codificar - ele não precisa considerar o primeiro bloco ativado separadamente.


5
Em muitos casos, uma cadeia if/ else ifnão é usada tanto como uma árvore de decisão, mas como uma construção "agir na primeira condição de correspondência", onde a condição que tem a maior prioridade não é particularmente "especial". Embora eu não tenha visto isso if(0)como uma maneira de permitir que todas as ramificações reais tenham sintaxe consistente, eu gosto da sintaxe consistente que ela facilita.
Supercat

1
Nem é útil neste caso, porque você pode obter o mesmo efeito sem: basta dividir a else iflinha em duas e colocar a proteção do pré-processador no meio.
Konrad Rudolph

1
@KonradRudolph Não estou seguindo; como você escreveria?
21718 JiK em

1
@JiK Eu removia o if (0)galho e reformatava o restante, de modo que estivesse elseem sua própria linha, cercado por um guarda ao longo das linhas de #if TEST1_ENABLED && TEST2_ENABLED.
Konrad Rudolph

5
@KonradRudolph, tudo bem se você quiser dobrar o número de guardas e triplicar o número de condições de guarda mencionadas, suponho.
Hbbs #

44

Eu vi um padrão semelhante usado no código gerado. Por exemplo, no SQL, vi bibliotecas emitirem a seguinte wherecláusula.

where 1 = 1

Presumivelmente, isso facilita a adição de outros critérios, porque todos os critérios adicionais podem ser anexados com anduma verificação adicional para verificar se esse é o primeiro critério ou não.


4
O 1=1também é "útil" porque você sempre pode adicionar o wherena frente, incondicionalmente. Caso contrário, você teria que verificar se está vazio e, em caso afirmativo, evite gerar a wherecláusula.
Bakuriu 14/11/2018

2
Além disso, a maioria dos bancos de dados "remove" automaticamente o arquivo 1=1do WHERE, para que não tenha impacto no desempenho.
Fund Monica's Lawsuit

7
Isso é aceitável em uma biblioteca que gera automaticamente consultas SQL que provavelmente nunca são vistas, mesmo pela equipe do DevOps. Não é "aceitável" no código de alto nível que precisa ser escrito e lido várias vezes.
Phagio

Essa é uma abordagem realmente útil ao gerar algum tipo de SQL dinâmico com número desconhecido de condições finais.
Skipper

1
@ Freakish De fato, escrevi o contrário: sintaxe pouco legível é aceitável no código gerado, pois provavelmente nunca será lido, não no código funcional de alto nível que é mantido pelos desenvolvedores.
Phagio

44

Como está escrito, a if (0) {}cláusula não compila em nada.

Suspeito que a função da cláusula no topo dessa escada seja fornecer um local fácil para desativar temporariamente todas as outras funcionalidades de uma só vez (para fins de depuração ou comparação), alterando 0para a 1ou true.


2
Acertou em cheio. Não pude ver nenhum outro motivo além da depuração.
tfont

16

Não tenho certeza de nenhuma otimização, mas meus dois centavos:

Isso aconteceu devido a alguma modificação de código, onde uma condição primária foi removida (a chamada de função no ifbloco inicial , digamos), mas os desenvolvedores / mantenedores

então, em vez de remover o ifbloco associado , eles simplesmente alteraram a condição if(0)e seguiram em frente.


3
if(0)Também não diminui a cobertura das agências?
precisa

1
@DavidSzalai Não completamente - no máximo, diminuirá em 1 (dos 2 anteriores) - mas ainda será necessário um hit para cobertura, tanto quanto eu saiba.
Sourav Ghosh

15

É código podre.

Em algum momento em que "se" fez algo útil, a situação mudou, talvez a variável que está sendo avaliada tenha sido removida.

A pessoa que estava consertando / alterando o sistema fez o mínimo possível para afetar a lógica do sistema, para garantir que o código fosse recompilado. Então ele deixa um "se (0)" porque é rápido e fácil e não tem muita certeza de que é isso que ele quer fazer. Ele faz o sistema funcionar e não volta para corrigi-lo completamente.

Em seguida, o próximo desenvolvedor aparece e pensa que foi feito deliberadamente e apenas comenta essa parte do código (já que não está sendo avaliado de qualquer maneira), e na próxima vez em que o código for tocado, esses comentários serão removidos.


2
Sim. Para código antigo, faça uma alteração de remoção de código morto por vez. Não posso contar o número de vezes que estive em uma briga de cortar e queimar contra código "morto" apenas para descobrir que havia algum efeito colateral bizarro que o corte e a queima perdiam.
Julie em Austin

15

Uma possibilidade ainda não mencionada: a if (0) {linha poderia estar fornecendo um local conveniente para um ponto de interrupção.

A depuração geralmente é feita em código não otimizado, portanto o teste sempre falso estará presente e poderá ter um ponto de interrupção definido. Quando compilada para produção, a linha de código seria otimizada. A linha aparentemente inútil fornece funcionalidade para desenvolvimento e teste de compilações sem impactar as versões.

Existem outras boas sugestões acima também; a única maneira de realmente saber qual é o objetivo é localizar o autor e perguntar. Seu sistema de controle de código-fonte pode ajudar com isso. (Procure a blamefuncionalidade do tipo.)


9

Vi blocos de código não alcançáveis ​​no JavaScript pré-expandido que foram gerados usando uma linguagem de modelo.

Por exemplo, o código que você está lendo pode ter sido colado de um servidor que pré-avaliou a primeira condição que naquele momento contava com uma variável disponível apenas no lado do servidor.

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

que uma vez pré-compilado tem:

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

espero que isso ajude a relativizar a atividade potencial de baixo teclado da era dos codificadores pró-reciclagem, pela qual eu demonstro entusiasmo!


1
Concordo que, na era da automação onipresente, devemos confiar mais no código gerado automaticamente, pois isso nos permite dedicar mais tempo às coisas reais. Mas, por enquanto, meu ponto exato de interesse é como tudo isso é arquitetado sob o capô.
Zzaponka

8

Essa construção também pode ser usada em C para implementar programação genérica com segurança de tipo, baseando-se no fato de que o código inacessível ainda é verificado pelo compilador:

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

Eu acho que é apenas um código ruim. Escrevendo um exemplo rápido no Compiler Explorer, vemos que no gcc e no clang nenhum código é gerado para o if (0)bloco, mesmo com as otimizações completamente desabilitadas:

https://godbolt.org/z/PETIks

Brincando com a remoção das if (0)causas, não há alterações no código gerado, então concluo que isso não é uma otimização.

É possível que houvesse algo no ifbloco superior que foi removido mais tarde. Em resumo, parece que removê-lo causaria a geração exata do mesmo código, portanto, fique à vontade para fazer isso.


6

Como foi dito, o zero é avaliado como falso, e o ramo provavelmente será otimizado pelo compilador.

Eu já vi isso antes no código em que um novo recurso foi adicionado e um interruptor de interrupção era necessário (se algo der errado com o recurso, você pode simplesmente desativá-lo) e algum tempo depois, quando o interruptor de interrupção foi removido O programador também não removeu o ramo, por exemplo

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

passou a ser

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

1

Isso ajuda a depurar esse bloco apenas colocando o bloco 1. Se isso desabilitar a funcionalidade de bloco all if else. E também podemos expandir o bloco 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

A resposta de @ PSkocik é boa, mas adiciono meus dois centavos. Não tenho certeza se devo fazer isso como um comentário ou como resposta; escolhendo o último, porque IMHO vale a pena ver outros, enquanto comentários são frequentemente invisíveis.

Não só uso ocasionalmente

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

Mas eu também ocasionalmente

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

ou

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

para condições complicadas. Pelas mesmas razões - mais fácil de editar, #ifdef, etc.

Por falar nisso, no Perl eu farei

@array = (  
    elem1,
    elem2,
    ...
    elem1,
) {
  • observe a vírgula no final da lista. Eu esqueço se vírgulas são separadores ou delimitadores nas listas C e C ++. IMHO, isso é uma coisa que aprendemos: [ As vírgulas finais no Perl são uma má prática? vírgulas] são uma coisa boa. Como qualquer nova notação, leva um tempo para se acostumar.

Eu comparo o if(0) código ao lisp

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

que você adivinhou, posso recuar como

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

Às vezes, tentei imaginar como seria uma sintaxe legível por humanos.

Possivelmente

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

inspirado pelo de Dikstra [ https://en.wikipedia.org/wiki/Guarded_Command_Language#Selection:_if][Guarded Command Language].

Mas essa sintaxe implica que as condições são avaliadas em paralelo, enquanto if...else-if implica avaliação sequencial e priorizada das condições.

Comecei a fazer esse tipo de coisa ao escrever programas que geravam outros programas, onde é especialmente conveniente.

Enquanto estamos trabalhando nisso, ao escrever RTL usando o antigo iHDL da Intel, codifiquei coisas como

   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

onde FORC..DOC..ENDCé uma construção de loop de pré-processador de macro, que se expande para

   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

Esse era um código de atribuição única, não imperativa, portanto, a configuração de uma variável de estado não era permitida, se você precisasse fazer coisas como encontrar o primeiro bit definido.

   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

Venha para pensar sobre isso, este pode ter sido o primeiro lugar que eu encontrei essas construções.

BTW, as objeções que alguns tinham ao estilo if (0) - de que as condições else-if-dependem sequencialmente e não podem ser reordenadas arbitrariamente - não se aplicam à lógica AND e OR e XOR na RTL - mas se aplicam a circuito && e ||.


-1

Eu já vi isso usado para lidar com erros, por exemplo

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

if(condition_fails)
   goto lable1;

Isso pode ser útil quando goto é usado para gerenciar erros, as instruções são executadas apenas quando ocorre um erro. Eu vi isso no código C muito antigo (onde argumentos de função são escritos fora do '()'), não pense que alguém segue isso agora.


-2

Eu já vi isso algumas vezes, acho que a razão mais provável é que ele estava avaliando algo em uma versão / ramificação do código mais antiga / diferente ou possivelmente para depuração, e alterá-lo para if(0)é uma maneira um tanto preguiçosa de remover o que estava lá .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.