4.10.2 Sentencias de selección
§1 Sinopsis
Las sentencias de selección, también llamadas de control de flujo, permiten decidir entre distintos cursos de acción en función de ciertos valores. En C++ existen tres tipos de estas sentencias de selección:
Recuerde que de no ser por estas sentencias, el flujo de ejecución del programa estaría siempre constreñido a la ejecución de sus sentencias en el orden en que están colocadas en el fuente.
§2 if … else
En su forma abreviada, cuando no existe la cláusula else, esta sentencia permite escoger entre ejecutar o no una sentencia, en función del resultado de una expresión lógica. En su forma ampliada, cuando la cláusula else está presente, permite escoger entre dos opciones alternativas.
§2.1 Sintaxis
if ( <condición> ) <sentencia1>;
[ else <sentencia2>; ]
§2.2 Descripción
<condición> debe ser una expresión relacional ( 4.9.12) que devuelve un valor lógico, es decir, un bool ( 3.2.1b), y estar obligatoriamente entre paréntesis. Pueden declararse variables dentro de la <condición>. Por ejemplo, la siguiente es una sintaxis válida:
if (int val = func(arg))
val = z ;
else val = y;
El ámbito de la variable val incluye toda la sentencia if, incluyendo, en su caso, el bloque <sentencia2> de else. La cláusula else es opcional, pero no puede haber sentencias entre el if y else. Recuerde las precauciones indicadas respecto de las expresiones relacionales ( 4.9.12), ya que son motivo frecuente de error en este tipo de sentencias.
<sentencia1>. Es una sentencia o bloque de código que se ejecuta si <condicion> se evalúa como cierto (true != 0).
<sentencia2> es una sentencia o bloque de código que se ejecuta si existe un else y <condicion> resulta falso (false == 0)
Puesto que el if simplemente chequea el valor resultante de <condicion>
(igual o desigual a cero), las dos expresiones siguientes son equivalentes:
if ( expresion ) <sentencia> ;
if ( expresion !=0 ) <sentencia> ;
La forma más general es:
if (<condicion>) {
<sentencia1>;
}
else {
<sentencia2>;
}
§2.3 Ejemplos
Uno sencillo:
if (salida == 'S') break;
Otro ejemplo:
if (a > b)
z = a;
else
z = b;
Se podría haber escrito de forma más comprimida:
if (a > b) z = a;
else z = b;
También:
a > b ? z = a : z = b ;
o mejor aún:
z = (a > b ? a : b);
Otro ejemplo:
if (int val = func(count)) { /* sentencias */ }
else { // otra vía de acción
cout << "val es falso";
}
Puesto que la cláusula else es opcional, en los if... else anidados podría haber ambigüedad sobre a qué if corresponde un else; esto se evita asociando el else al if más interno sin else. Por ejemplo, en los dos trozos de código siguientes, el primero tiene una indentación que no se corresponde con la realidad lógica del programa.
// mal indentado: ------------
if ( n > 0 )
if ( a > b )
z = a;
else
z = b;
//
bien indentado:
-----------
if ( n > 0 )
if ( a > b )
z = a;
else
z = b;
§3 else if
Estas sentencias no representan en realidad nada nuevo, solo una sucesión de if else anidados, aunque de uso muy frecuente, por lo que haremos una consideración especial de este caso.
if ( <expresion1> )
<sentencia1> ;
else if ( <expresion2> )
<sentencia2> ;
else if ( <expresion3> )
<sentencia3> ;
else
<sentencia4> ;
En realidad, a la luz de lo expuesto en el apartado anterior, su indentación correcta sería:
if ( <expresion1> )
<sentencia1> ;
else
if ( <expresion2> )
<sentencia2> ;
else
if ( <expresion3> )
<sentencia3> ;
else
<sentencia4> ;
Las expresiones <expresion> son evaluadas correlativamente hasta que se encuentra la primera que devuelve un valor cierto ( != 0 ), en cuyo caso se ejecuta el bloque de código <sentencia> correspondiente y acaba la evaluación. En caso de que ninguna de las <expresion> sea cierta, se ejecuta la <sentencia> correspondiente al else (si existe).
§4 switch
Se trata de una sentencia condicional multi-salida en la que las decisiones se toman en función de un valor numérico entero de entre una serie de opciones posibles. Puede existir una cláusula por defecto o bien no adoptarse ninguna acción.
§4.1 Sintaxis
switch ( <expresion> ) {
case <const1> : <sentencia1>; [break;]
case <const2> : <sentencia2>; [break;]
.
.
.
case <constN> : <sentenciaN>; [break;]
[default : <sentenciaD>; ]
}
§4.2 Descripción
La sentencia switch comprueba cuando una expresión <expresion> entre paréntesis (que se traduce en un valor numérico) coincide con alguno de una serie de valores enteros constantes y diferentes (<constX>). En cuyo caso, se ejecuta un bloque de código específico <sentencia>. En caso de estar presente la cláusula opcional default y no existir concordancia con ninguno de los valores anteriores, se ejecuta una sentencia por defecto (<sentenciaD>).
Los valores case y default pueden aparecer en cualquier orden, aunque lo usual es colocar default al final. Los dos puntos : después de <constX> son imprescindibles.
§4.3 Las expresiones <constX>
son expresiones constantes ( 3.2.3a)
que se resuelven en un entero. Pueden ser simples o compuestas, como se
muestra en el ejemplo.
switch (c - 48) {
case 0: case 1: case 2: case 3: case 4:
case 5: case 6: case 7:
case 8: case 9:
cout << "Dígito" << endl;
break;
case 17: case 21: case 25: case 31: case 37:
case 49: case 53: case 57:
case 63: case 69:
cout << "Vocal" << endl;
break;
default:
cout << "Otro carácter" << endl;
}
§4.4
Después de ejecutado el bloque de código correspondiente a una concordancia,
siguen las comprobaciones, por
lo que puede ser conveniente incluir un break para abandonar el control
del switch (no es necesario ningún break después de la
última). Ver ejemplo .
§4.5 Es ilegal que la transferencia de control originada en
alguna cláusula case o default se salte una declaración que
incluya un inicializador implícito o explícito. A menos que la declaración
esté situada en el interior de un bloque que sea saltado completamente
durante la transferencia. Ejemplo:
switch (n) {
case 1: case 10:
int y;
// L-3: Ok. no incluye inicialización
cout << "Caso-1" << endl;
break;
case 2:
{
int z = 0;
// L-8: Ok. dentro de un bloque
cout << "Caso-2" << endl;
break;
}
case 3:
int x = 3;
// L-13: Error-1
cout << "Caso-3" << endl;
break;
case 4:
// L-16:
for (int i = 0; i<4; i++) // L-17: Error-2
cout << "Caso-4" << endl;
break;
default:
// L-20:
cout << "Resto de casos" << endl;
}
Observación: es posible que en estos casos, el error señalado por el compilador se produzca en las sentencias
responsables de que la transferencia de control se salte la declaración. En el código del ejemplo, el Error-1 podría ser señalado
en las sentencias L.16 y L.20, mientras que el Error-2 en la sentencia L.20. Como muestra se incluyen los mensajes obtenidos
en el caso anterior con los dos compiladores:
MS Visual C++ 6.0:
xxx.cpp(16) : error C2360: initialization of 'x' is skipped by 'case' label
xxx.cpp(13) : see declaration of 'x'
xxx.cpp(20) : error C2361: initialization of 'x' is skipped by 'default' label
xxx.cpp(13) : see declaration of 'x'
xxx.cpp(20) : error C2361: initialization of 'i' is skipped by 'default' label
xxx.cpp(17) : see declaration of 'i'
GNU g++ para Windows (MinGW) anuncia:
16 ...\xxx.cpp jump to case label