¿Cuál es el propósito de un bloque 'if (0)' en el bloque if-else?

141

Mi pregunta es sobre la línea que he mencionado en el tema y que puedo ver en muchos lugares dentro del código de producción.

El código general se ve así:

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

Las otras ramas son irrelevantes para mi pregunta. Me pregunto cuál es el significado de poner if (0)aquí. Las llaves están vacías, así que no creo que deba comentar algún bloque de código. ¿Obliga al compilador a hacer alguna optimización o sus intenciones son diferentes?

He intentado buscar este caso explícito aquí en SO y en Internet, pero sin éxito. Hay preguntas similares sobre JavaScript, pero no C. Hay otra pregunta, ¿qué sucede cuando se asigna un cero en una condición `if`? , pero analiza la asignación cero a una variable, no el uso 'if (0)' en sí mismo.

Zzaponka
fuente
2
Esa afirmación parece irrelevante. Genere código de ensamblaje con y sin esa declaración y verá lo que sucede debajo del capó.
piratea el
2
Es posible que este sea un código generado automáticamente.
extraño

Respuestas:

91

A veces uso esto para la simetría para poder mover el otro else if{libremente con mi editor sin tener que preocuparme por el primero if.

Semánticamente el

if (0) {
    // Empty braces
} else 

parte no hace nada y puede contar con optimizadores para eliminarlo.

PSkocik
fuente
239
Opinión personal: Si bien este puede ser el código de razón por el que está escrito tal como está, creo que es una mala justificación. El código se lee con más frecuencia de lo que se escribe, y este código innecesario solo aumenta la sobrecarga de análisis para el lector.
user694733
13
@ user694733: Se podría argumentar que el if elseprefijo común a todas las rutas de código significativas alinea las condiciones muy bien y facilita el escaneo. (Sin embargo, eso es subjetivo y dependería mucho de lo que realmente está dentro de las condiciones y los bloques de código).
M Oehm,
72
No creo que if (0) {..}presente ningún problema de legibilidad / legibilidad. Debería ser obvio para cualquiera que sepa un poco de C. Eso no es un problema. El problema es la pregunta de seguimiento después de leerlo: "¿Para qué demonios es entonces?" A menos que sea para fines de depuración / temporales (es decir, la intención es "habilitar" ese ifbloqueo más adelante), recomendaría eliminarlo por completo. Básicamente, "leer" dicho código probablemente causaría una "pausa" innecesaria para el lector sin una buena razón. Y esa es una buena razón para eliminarlo.
PP
77
Parece que definitivamente resta valor a la legibilidad. Fue tan malo que envió a ese programador a SO para preguntar para qué era. No es una buena señal.
Vectorjohn
26
Incluso utilizando este patrón, no sé si puede "moverse else ifpor el editor sin preocuparse" porque las condiciones pueden no ser mutuamente excluyentes, en cuyo caso el orden es importante. Personalmente, solo usaría ify realizaría un retorno temprano , extrayendo la cadena lógica a una función separada si fuera necesario.
John Wu
105

Esto puede ser útil si hay #ifdeclaraciones, ala

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

etc.

En este caso, cualquiera (y todas) de las pruebas se pueden #ifeliminar y el código se compilará correctamente. Casi todos los compiladores eliminarán la if (0) {}pieza. Un generador automático simple podría generar un código como este, ya que es un poco más fácil de codificar: no tiene que considerar el primer bloque habilitado por separado.

CSM
fuente
55
En muchos casos, una cadena if/ else ifno se usa tanto como un árbol de decisión, sino más bien como una construcción de "actuar según la primera condición coincidente", donde la condición que tiene la más alta prioridad no es particularmente "especial". Si bien no lo había visto if(0)como una forma de permitir que todas las ramas reales tengan una sintaxis consistente, me gusta la sintaxis consistente que facilita.
supercat
1
Ni siquiera es útil en este caso porque puede lograr el mismo efecto sin: simplemente divida la else iflínea en dos y coloque el protector del preprocesador en el medio.
Konrad Rudolph el
1
@KonradRudolph no estoy siguiendo; como lo escribirias
JiK
1
@JiK Quitaría la if (0)rama y volvería a formatear el resto de modo que elseesté en su propia línea, rodeada por un guardia a lo largo de las líneas de #if TEST1_ENABLED && TEST2_ENABLED.
Konrad Rudolph el
55
@KonradRudolph está bien si quieres duplicar el número de guardias y triplicar el número de condiciones de guardia mencionadas, supongo.
hobbs
44

He visto un patrón similar usado en el código generado. Por ejemplo, en SQL, he visto que las bibliotecas emiten la siguiente wherecláusula.

where 1 = 1

Presumiblemente, esto hace que sea más fácil agregar otros criterios, ya que todos los criterios adicionales pueden anteponerse en andlugar de una verificación adicional para ver si es el primer criterio o no.

Seth Flowers
fuente
44
El 1=1también es "útil" porque siempre puede agregar el wherefrente, incondicionalmente. De lo contrario, deberá verificar si está vacío y, de ser así, evitar generar la wherecláusula.
Bakuriu
2
Además, la mayoría de las bases de datos "eliminarán" automáticamente 1=1de WHERE, por lo que no tiene un impacto en el rendimiento.
Financia la demanda de Mónica el
77
Esto es aceptable en una biblioteca que genera automáticamente consultas SQL que probablemente nunca sean vistas por el equipo de DevOps. No es "aceptable" en el código de alto nivel que debe escribirse y leerse varias veces.
phagio
Este es un enfoque realmente útil cuando se genera algún tipo de SQL dinámico con un número desconocido de condiciones finales.
Patrón
1
@freakish, de hecho, escribí lo contrario: la sintaxis poco legible es aceptable en el código generado, ya que probablemente nunca se leerá, no en el código funcional de alto nivel que mantienen los desarrolladores.
Phago
44

Tal como está escrito, el if (0) {} cláusula se compila en nada.

Sospecho que la función de la cláusula en la parte superior de esta escalera es proporcionar un lugar fácil para deshabilitar temporalmente todas las demás funciones a la vez (para fines de depuración o comparación) cambiando el 0a a 1o true.

Russell Borogove
fuente
2
Justo en el clavo. No pude ver ninguna otra razón además de la depuración.
tfont
16

No estoy seguro de ninguna optimización, pero mis dos centavos:

Esto sucedió debido a alguna modificación del código, donde se eliminó una condición primaria (la llamada a la función en el ifbloque inicial , digamos), pero los desarrolladores / mantenedores

así que en lugar de eliminar el ifbloque asociado , simplemente cambiaron la condición if(0)y continuaron.

Sourav Ghosh
fuente
3
¿No if(0)disminuye también la cobertura de sucursales?
David Szalai
1
@DavidSzalai No completamente, a lo sumo disminuirá en 1 (de los 2 anteriores), pero aún así se requerirá un hit para la cobertura, que yo sepa.
Sourav Ghosh
15

Es la putrefacción del código.

En algún momento que "si" hizo algo útil, la situación cambió, tal vez se eliminó la variable que se está evaluando.

La persona que estaba arreglando / cambiando el sistema hizo lo menos posible para afectar la lógica del sistema por lo que se aseguró de que el código se volviera a compilar. Entonces deja un "si (0)" porque es rápido y fácil y no está totalmente seguro de que eso es lo que quiere hacer. Hace que el sistema funcione y no regresa para arreglarlo por completo.

Luego aparece el siguiente desarrollador y piensa que se hizo deliberadamente y solo comenta esa parte del código (ya que de todos modos no se está evaluando), luego la próxima vez que se toque el código, esos comentarios se eliminarán.

Materia oscura
fuente
2
Sip. Para el código antiguo, realice un cambio de eliminación de código muerto a la vez. No puedo contar la cantidad de veces que he estado en un ataque violento contra el código "muerto" solo para descubrir que hubo algún efecto secundario extraño que se perdió en cortar y quemar.
Julie en Austin
15

Una posibilidad aún no mencionada: la if (0) {línea podría ser un lugar conveniente para un punto de interrupción.

La depuración a menudo se realiza en código no optimizado, por lo que la prueba siempre falsa estará presente y podrá tener un punto de interrupción establecido. Cuando se compila para la producción, la línea de código se optimizará. La línea aparentemente inútil brinda funcionalidad para el desarrollo y las versiones de prueba sin afectar las versiones de lanzamiento.

Hay otras buenas sugerencias anteriores también; La única forma de saber realmente cuál es el propósito es localizar al autor y preguntar. Su sistema de control de código fuente podría ayudar con eso. (Busque la blamefuncionalidad de tipo).

Studog
fuente
9

He visto bloques de código no accesibles en JavaScript previamente expandido que se han generado utilizando un lenguaje de plantillas.

Por ejemplo, el código que está leyendo podría haberse pegado desde un servidor que evaluó previamente la primera condición que en ese momento dependía de una variable solo disponible en el lado del servidor.

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

que una vez compilaron previamente hences:

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

¡Espero que esto te ayude a relativizar la potencial actividad de teclado bajo de la era de los codificadores de reciclaje profesional por la cual manifesté entusiasmo!

simonarame
fuente
1
Estoy de acuerdo, en la era de la automatización ubicua, debemos confiar más en el código autogenerado, ya que nos permite pasar más tiempo en cosas reales. Pero por ahora, mi punto de interés exacto es cómo todo esto está diseñado bajo el capó.
Zzaponka
8

Esa construcción también se puede usar en C para implementar programación genérica con seguridad de tipo, basándose en el hecho de que el compilador sigue comprobando el código inalcanzable:

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

Creo que es solo un mal código. Al escribir un ejemplo rápido en Compiler Explorer, vemos que tanto en gcc como en clang no se genera código para elif (0) bloque, incluso con optimizaciones completamente deshabilitadas:

https://godbolt.org/z/PETIks

Jugar con la eliminación de las if (0)causas no causa cambios en el código generado, por lo que concluyo que esto no es una optimización.

Es posible que haya algo en el ifbloque superior que luego se eliminó. En resumen, parece que eliminarlo podría generar exactamente el mismo código, así que siéntase libre de hacerlo.

cha0site
fuente
6

Como se ha dicho, el cero se evalúa como falso y es probable que el compilador optimice la rama.

También he visto esto antes en el código donde se agregó una nueva función y se necesitaba un interruptor de apagado (si algo sale mal con la función, simplemente puede desactivarlo), y algún tiempo después, cuando se quitó el interruptor de apagado el programador tampoco eliminó la rama, por ejemplo

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

convirtió

if (0) {
   // empty
} else if (some_fn()) {
   ...
sergiopm
fuente
1

Ayuda a depurar este bloque simplemente colocando el bloque 1. Esto deshabilita toda la funcionalidad del bloque if else. Y también podemos expandir el bloque if else.

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

La respuesta de @ PSkocik está bien, pero agrego mis dos centavos. No estoy seguro si debo hacer esto como un comentario, o como una respuesta; elegir este último, porque en mi humilde opinión vale la pena ver a otros, mientras que los comentarios son con frecuencia invisibles.

No solo uso ocasionalmente

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

Pero también ocasionalmente

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

o

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

para condiciones complicadas Por las mismas razones: más fácil de editar, #ifdef, etc.

Para el caso, en Perl haré

@array = (  
    elem1,
    elem2,
    ...
    elem1,
) {
  • tenga en cuenta la coma al final de la lista. Olvidé si las comas son separadores o delimitadores en las listas C y C ++. En mi humilde opinión, esto es algo que hemos aprendido: [ ¿Las comas finales en Perl son una mala práctica? las comas] son ​​algo bueno. Como cualquier nueva notación, lleva un tiempo acostumbrarse.

Comparo el if(0)código con lisp

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

que, lo has adivinado, puedo sangrar como

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

A veces he tratado de imaginar cómo podría ser una sintaxis más legible para esto.

Quizás

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

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

Pero esta sintaxis implica que las condiciones se evalúan en paralelo, mientras que if...else-ifimplica una evaluación secuencial y priorizada de las condiciones.

Comencé a hacer este tipo de cosas cuando escribía programas que generaban otros programas, donde es especialmente conveniente.

Mientras estamos en eso, cuando escribo RTL usando el viejo iHDL de Intel, he codificado cosas 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

donde FORC..DOC..ENDCes una construcción de bucle de preprocesador de macro, que se expande a

   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

Este era un código de asignación única, no imperativo, por lo que no se permitía establecer una variable de estado, si necesitaba hacer cosas como encontrar el primer bit establecido.

   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

Ahora que lo pienso, este puede haber sido el primer lugar en el que me encontré con tales construcciones.

Por cierto, las objeciones que algunos tenían al estilo if (0), que las condiciones else-if dependen secuencialmente y no se pueden reordenar arbitrariamente, no se aplican a la lógica AND y OR y XOR en RTL, pero se aplican a short- circuito && y ||.

Krazy Glew
fuente
-1

He visto esto usado para manejar errores, por ejemplo

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

if(condition_fails)
   goto lable1;

Esto puede ser útil cuando goto se usa para administrar errores, las declaraciones se ejecutan solo cuando ocurre un error. Vi esto en un código C muy antiguo (donde los argumentos de la función están escritos fuera de '()'), no creo que nadie siga esto ahora.

Jayachandra M
fuente
-2

Lo he visto varias veces, creo que la razón más probable es que estaba evaluando algo en una versión / rama más antigua / diferente del código, o posiblemente para la depuración, y cambiarlo a if(0)es una forma algo perezosa de eliminar lo que estaba allí .

John U
fuente