que - ¿Cuál es el beneficio de terminar si... más si se construye con una cláusula else?
sentencia if else en c++ (12)
Nuestra organización tiene una regla de codificación requerida (sin ninguna explicación) que:
if ... else if construcciones deben terminarse con una cláusula else
Ejemplo 1:
if ( x < 0 )
{
x = 0;
} /* else not needed */
Ejemplo 2
if ( x < 0 )
{
x = 0;
}
else if ( y < 0 )
{
x = 3;
}
else /* this else clause is required, even if the */
{ /* programmer expects this will never be reached */
/* no change in value of x */
}
¿Qué caja de borde está diseñada para manejar?
Lo que también me preocupa de la razón es que el
Ejemplo 1
no necesita otra
else
pero el
Ejemplo 2
sí.
Si la razón es la reutilización y la extensibilidad, creo
else
debería usarse en ambos casos.
Esto es equivalente a requerir un caso predeterminado en cada switch.
Este extra disminuirá la cobertura del código de su programa.
En mi experiencia con portar el kernel de Linux o el código de Android a una plataforma diferente, muchas veces hacemos algo mal y en logcat vemos algunos errores como
if ( x < 0 )
{
x = 0;
}
else if ( y < 0 )
{
x = 3;
}
else /* this else clause is required, even if the */
{ /* programmer expects this will never be reached */
/* no change in value of x */
printk(" /n [function or module name]: this should never happen /n");
/* It is always good to mention function/module name with the
logs. If you end up with "this should never happen" message
and the same message is used in many places in the software
it will be hard to track/debug.
*/
}
Actualmente estoy trabajando con PHP. Crear un formulario de registro y un formulario de inicio de sesión. Solo estoy usando if y else. No más si o algo que sea innecesario.
Si el usuario hace clic en el botón Enviar -> pasa a la siguiente declaración if ... si el nombre de usuario es menor que ''X'', entonces alerta. Si tiene éxito, verifique la longitud de la contraseña, etc.
No es necesario un código adicional, como otro, si eso podría descartar la confiabilidad del tiempo de carga del servidor para verificar todo el código adicional.
Bueno, mi ejemplo implica un comportamiento indefinido, pero a veces algunas personas intentan ser elegantes y fallan mucho, eche un vistazo:
int a = 0;
bool b = true;
uint8_t* bPtr = (uint8_t*)&b;
*bPtr = 0xCC;
if(b == true)
{
a += 3;
}
else if(b == false)
{
a += 5;
}
else
{
exit(3);
}
Probablemente nunca esperaría tener
bool
que no es
true
ni
false
, sin embargo, puede suceder.
Personalmente, creo que este es un problema causado por una persona que decide hacer algo elegante, pero una declaración adicional puede evitar más problemas.
Como se mencionó en otra respuesta, esto es de las pautas de codificación MISRA-C. El propósito es la programación defensiva, un concepto que a menudo se usa en la programación de misión crítica.
Es decir, cada
if - else if
debe terminar con un
else
, y cada
switch
debe terminar con un
default
.
Hay dos razones para esto:
-
Código autodocumentado. Si escribe otra
else
pero la deja vacía, significa: "Definitivamente he considerado el escenario cuando niif
nielse if
es cierto".No escribir otra
else
allí significa: "o consideré el escenario donde niif
nielse if
es cierto, o olvidé por completo considerarlo y hay potencialmente un error gordo aquí mismo en mi código". -
Detener el código fuera de control. En el software de misión crítica, debe escribir programas robustos que tengan en cuenta incluso lo poco probable. Entonces puedes ver código como
if (mybool == TRUE) { } else if (mybool == FALSE) { } else { // handle error }
Este código será completamente ajeno a los programadores de PC y los científicos informáticos, pero tiene mucho sentido en el software de misión crítica, ya que detecta el caso en el que "mybool" se ha corrompido, por cualquier razón.
Históricamente, temerías la corrupción de la memoria RAM debido a EMI / ruido. Esto no es un gran problema hoy. Es mucho más probable que la corrupción de la memoria ocurra debido a errores en otras partes del código: punteros a ubicaciones incorrectas, errores fuera de los límites de la matriz, desbordamiento de pila, código desbocado, etc.
Entonces, la mayoría de las veces, un código como este regresa para abofetearse cuando ha escrito errores durante la etapa de implementación. Lo que significa que también podría usarse como una técnica de depuración: el programa que está escribiendo le dice cuándo ha escrito errores.
EDITAR
En cuanto a por qué no se necesita más después de cada uno
if
:
Un
if-else
o
if-else if-else
cubre completamente todos los valores posibles que puede tener una variable.
Pero una declaración simple
if
no está necesariamente allí para cubrir todos los valores posibles, tiene un uso mucho más amplio.
En la mayoría de los casos, solo desea verificar una determinada condición y, si no se cumple, no haga nada.
Entonces simplemente no tiene sentido escribir programación defensiva para cubrir el caso
else
.
Además, desordenaría el código por completo si escribiera un espacio vacío después de todos y cada uno.
MISRA-C: 2012 15.7 no da razones por las cuales no se necesita más, solo dice:
Nota: no se requiere una declaración final de
else
para una declaraciónif
simple.
Esto se hace para que el código sea más legible, para referencias posteriores y para dejar en claro, a un revisor posterior, que los casos restantes manejados por el último
else
son casos de
no hacer nada
, para que no se pasen por alto a primera vista .
Esta es una buena práctica de programación, que hace que el código sea reutilizable y ampliable .
Lógicamente, cualquier prueba implica dos ramas. ¿Qué haces si es verdad y qué haces si es falso?
Para aquellos casos en que cualquiera de las ramas no tiene funcionalidad, es razonable agregar un comentario sobre por qué no necesita tener funcionalidad.
Esto puede ser beneficioso para el próximo programador de mantenimiento. No deberían tener que buscar demasiado para decidir si el código es correcto. Puedes prehunt el elefante .
Personalmente, me ayuda, ya que me obliga a mirar el caso más y evaluarlo. Puede ser una condición imposible, en cuyo caso puedo lanzar una excepción ya que se viola el contrato. Puede ser benigno, en cuyo caso un comentario puede ser suficiente.
Su experiencia puede ser diferente.
La mayoría de las veces cuando solo tiene una sola declaración
if
, probablemente sea una de las razones, tales como:
- Verificaciones de guardia de función
- Opción de inicialización
- Rama de procesamiento opcional
Ejemplo
void print (char * text)
{
if (text == null) return; // guard check
printf(text);
}
Pero cuando lo hace
if .. else if
no, probablemente sea una de las razones, tales como:
- Caja de cambio dinámico
- Horquilla de procesamiento
- Manejo de un parámetro de procesamiento
Y en caso de que su
if .. else if
cubra todas las posibilidades, en ese caso su último
if (...)
no es necesario, simplemente puede eliminarlo, porque en ese punto los únicos valores posibles son los cubiertos por esa condición.
Ejemplo
int absolute_value (int n)
{
if (n == 0)
{
return 0;
}
else if (n > 0)
{
return n;
}
else /* if (n < 0) */ // redundant check
{
return (n * (-1));
}
}
Y en la mayoría de estos motivos, es posible que algo no encaje en ninguna de las categorías en su
if .. else if
, por lo tanto, la necesidad de manejarlos en una cláusula
else
final, el manejo se puede hacer a través de un procedimiento a nivel comercial, usuario notificación, mecanismo de error interno, etc.
Ejemplo
#DEFINE SQRT_TWO 1.41421356237309504880
#DEFINE SQRT_THREE 1.73205080756887729352
#DEFINE SQRT_FIVE 2.23606797749978969641
double square_root (int n)
{
if (n > 5) return sqrt((double)n);
else if (n == 5) return SQRT_FIVE;
else if (n == 4) return 2.0;
else if (n == 3) return SQRT_THREE;
else if (n == 2) return SQRT_TWO;
else if (n == 1) return 1.0;
else if (n == 0) return 0.0;
else return sqrt(-1); // error handling
}
Esta última cláusula
else
es bastante similar a algunas otras cosas en lenguajes como
Java
y
C++
, como:
-
caso
default
en una declaración de cambio -
catch(...)
que viene después de todos los bloques decatch
específicos -
finally
en una cláusula try-catch
La razón básica es probablemente la cobertura del código y la otra implícita: ¿cómo se comportará el código si la condición no es verdadera? Para una prueba genuina, necesita alguna forma de ver que ha probado con la condición falsa. Si cada caso de prueba que tiene pasa por la cláusula if, su código podría tener problemas en el mundo real debido a una condición que no probó.
Sin embargo, algunas condiciones pueden ser como el Ejemplo 1, como en una declaración de impuestos: "Si el resultado es menor que 0, ingrese 0." Aún necesita hacerse una prueba donde la condición es falsa.
Me gustaría agregar, y en parte contradecir, las respuestas anteriores. Si bien es común usar if-else if de una manera similar a un interruptor que debería cubrir el rango completo de valores pensables para una expresión, de ninguna manera se garantiza que cualquier rango de condiciones posibles esté completamente cubierto. Lo mismo puede decirse sobre la construcción del interruptor en sí, de ahí el requisito de usar una cláusula predeterminada, que capture todos los valores restantes y, si no se requiere de otra manera, puede usarse como una salvaguarda de aserción.
La pregunta en sí presenta un buen contraejemplo: la segunda condición no se relaciona en absoluto con x (que es la razón por la que a menudo prefiero la variante basada en if más flexible que la variante basada en switch). Del ejemplo, es obvio que si se cumple la condición A, x debe establecerse en un cierto valor. Si no se cumple A, entonces se prueba la condición B. Si se cumple, entonces x debería recibir otro valor. Si no se cumplen ni A ni B, entonces x debería permanecer sin cambios.
Aquí podemos ver que se debe usar una rama vacía para comentar sobre la intención del programador para el lector.
Por otro lado, no puedo ver por qué debe haber una cláusula else, especialmente para la última declaración if. En C, no existe tal cosa como ''más si''. Solo hay si y más. En cambio, según MISRA, la construcción debería tener una sangría formal de esta manera (y debería haber puesto las llaves de apertura en sus propias líneas, pero eso no me gusta):
if (A) {
// do something
}
else {
if (B) {
// do something else (no pun intended)
}
else {
// don''t do anything here
}
}
Cuando MISRA pide colocar llaves alrededor de cada rama, entonces se contradice al mencionar "si ... si no construye".
Cualquiera puede imaginar la fealdad de los árboles profundamente anidados si no, ver aquí en una nota al margen . Ahora imagine que esta construcción se puede extender arbitrariamente a cualquier lugar. Luego, pedir una cláusula else al final, pero no en ningún otro lado, se vuelve absurdo.
if (A) {
if (B) {
// do something
}
// you could to something here
}
else {
// or here
if (B) { // or C?
// do something else (no pun intended)
}
else {
// don''t do anything here, if you don''t want to
}
// what if I wanted to do something here? I need brackets for that.
}
Por lo tanto, estoy seguro de que las personas que desarrollaron las pautas de MISRA tenían en mente la intención de cambiar si no es así.
Al final, se trata de que definan con precisión lo que se entiende con un "if ... else if construct"
Nuestro software no era de misión crítica, pero también decidimos usar esta regla debido a la programación defensiva. Agregamos una excepción de lanzamiento al código teóricamente inalcanzable (switch + if-else). Y nos salvó muchas veces ya que el software falló rápidamente, por ejemplo, cuando se agregó un nuevo tipo y se nos olvidó cambiar uno o dos en caso contrario o cambiar. Como beneficio adicional, fue muy fácil encontrar el problema.
Solo una breve explicación, ya que hice esto hace unos 5 años.
No hay (con la mayoría de los lenguajes) ningún requisito sintáctico para incluir una declaración
else
"nula" (e innecesaria
{..}
), y en "pequeños programas simples" no hay necesidad.
Pero los programadores reales no escriben "pequeños programas simples" y, lo que es más importante, no escriben programas que se usarán una vez y luego se descartarán.
Cuando uno escribe un if / else:
if(something)
doSomething;
else
doSomethingElse;
todo parece simple y apenas se ve el punto de agregar
{..}
.
Pero algún día, dentro de unos meses, algún otro programador (¡nunca cometerá tal error!) Necesitará "mejorar" el programa y agregará una declaración.
if(something)
doSomething;
else
doSomethingIForgot;
doSomethingElse;
De repente,
doSomethingElse
algo más, olvida un poco que se supone que está en la
else
pierna.
Así que eres un buen programador y siempre usas
{..}
.
Pero tu escribes:
if(something) {
if(anotherThing) {
doSomething;
}
}
Todo está bien hasta que ese nuevo niño haga una modificación a medianoche:
if(something) {
if(!notMyThing) {
if(anotherThing) {
doSomething;
}
else {
dontDoAnything; // Because it''s not my thing.
}}
}
Sí, está formateado incorrectamente, pero también lo es la mitad del código en el proyecto, y el "formateador automático" se enloquece con todas las declaraciones
#ifdef
.
Y, por supuesto, el código real es mucho más complicado que este ejemplo de juguete.
Desafortunadamente (o no), he estado fuera de este tipo de cosas durante algunos años, así que no tengo un nuevo ejemplo "real" en mente: lo anterior es (obviamente) artificial y un poco tonto.
Su empresa siguió la guía de codificación MISRA. Hay algunas versiones de estas pautas que contienen esta regla, pero de MISRA-C:2004 † :
Regla 14.10 (requerido): Todos si… si no si las construcciones se terminarán con una cláusula else.
Esta regla se aplica siempre que una declaración if es seguida por una o más declaraciones if; El último
if
debe ir seguido de una declaraciónelse
. En el caso de una declaraciónif
simple, la declaraciónelse
no necesita ser incluida. El requisito para una declaración final es la programación defensiva. La declaraciónelse
tomará las medidas apropiadas o contendrá un comentario adecuado sobre por qué no se toman medidas. Esto es consistente con el requisito de tener una cláusuladefault
final en una declaración deswitch
. Por ejemplo, este código es una declaración if simple:
if ( x < 0 ) { log_error(3); x = 0; } /* else not needed */
mientras que el siguiente código muestra una construcción
if
,else if
if ( x < 0 ) { log_error(3); x = 0; } else if ( y < 0 ) { x = 3; } else /* this else clause is required, even if the */ { /* programmer expects this will never be reached */ /* no change in value of x */ }
En MISRA-C:2012 , que reemplaza a la versión 2004 y es la recomendación actual para nuevos proyectos, existe la misma regla pero está numerada 15.7 .
Ejemplo 1: en un único programador de sentencias if, es posible que necesite verificar n número de condiciones y realizar una sola operación.
if(condition_1 || condition_2 || ... condition_n)
{
//operation_1
}
En un uso regular, no es necesario realizar una operación todo el tiempo cuando se usa
if
.
Ejemplo 2:
Aquí el programador verifica n número de condiciones y realiza múltiples operaciones.
En el uso regular
if..else if
es como un
switch
es posible que deba realizar una operación como la predeterminada.
Por
else
se necesita
else
uso según el estándar misra
if(condition_1 || condition_2 || ... condition_n)
{
//operation_1
}
else if(condition_1 || condition_2 || ... condition_n)
{
//operation_2
}
....
else
{
//default cause
}
† Las versiones actuales y pasadas de estas publicaciones están disponibles para su compra a través de la tienda web MISRA ( via ).