Documentos de Académico
Documentos de Profesional
Documentos de Cultura
§1 Sinopsis
Las sentencias de salto permiten transferir el control del programa de forma incondicional. Existen
cuatro de estas sentencias: break , continue , goto y return .
Nota: aunque en los textos no suele aparecer recogido en este epígrafe, existe una quinta
posibilidad de realizar saltos de forma incondicional utilizando el mecanismo de excepciones
throw/catch ( 1.6). Entiendo que está ahí para utilizarlo (con las debidas precauciones)
cuando se considere necesario.
§2 break
La sentencia break se usa para salir de forma incondicional de los bucles do, for y while, así como
de las sentencias switch de multi-dicisión. Hay que tener en cuenta que en el caso de bucles
anidados, break hace salir del bucle interior (ver nota a continuación).
§2.1 Sintaxis
break;
Ejemplo:
switch (c) {
case 0:
cout << "Falso" << endl;
break;
default:
cout << "Cierto" << endl;
}
Nota: al contrario que otros lenguajes, C++ no dispone de la opción break (n) para definir el
número de niveles que se saltarán hacia fuera en el caso de bucles anidados.
§3 continue
La sentencia continue se utiliza en los bucles for, while y do...while. En los primeros el control
salta al final del bucle, con lo que el contador se incrementa y comienza otra comprobación. En los
while, el control pasa inmediatamente a la condición de control.
§3.1 Sintaxis:
continue;
§3.2 Descripción
continue suele utilizarse cuando la lógica del bucle es complicada, de forma que establecer una
nueva condición y un nuevo nivel de indentación puede volver esta demasiado profunda.
§3.3 Ejemplo
void main () {
for (i = 0; i < 20; i++) {
if (array[i] == 0)
continue;
array[i] = 1/array[i];
}
}
§4 goto
goto es una sentencia de salto incondicional dentro del ámbito de una función.
§4.1 Sintaxis
goto <etiqueta> ;
§4.2 Descripción
§4.3 Comentario
Aunque en tiempos fue muy popular y los programas estaban llenos de "gotos" con saltos cuyos
destinos eran la mayoría de las veces etiquetas numéricas (el número de línea de la instrucción a
la que se quería saltar), podríamos afirmar sin rubor que actualmente se trata de la sentencia
maldita. Ningún programador medianamente "elegante" empleará jamás un goto. Incluso existen
lenguajes que simplemente no disponen de esta instrucción tan "hortera".
C++ y otros lenguajes disponen de esta sentencia aunque se considera que debe ser evitada.
Sobre todo porque la secuencia lógica del código resulta difícil de entender ( Ejemplo), aunque a
veces es de utilidad para salir de bucles anidados en los que el break no es suficiente. Por ejemplo
en condiciones de error.
Como botón de muestra de la opinión que merece esta sentencia a algunos autores, adjuntamos
una de ellas respetando el original inglés. Aunque se refiere al lenguaje Perl, podría ser
perfectamente aplicable a C++:
"If you want to really mess around with the structure of your programs, you can use goto LABEL to
jump anywhere in your program. Whatever you do, don't do this. This is not to be used. Don't go
that way.
I'm telling you about it for the simple reason that if you see it in anyone else's Perl, you can laugh
heartily at them.... But goto with a label is to be avoided like the plague... Don't use it unless you
really, really, really understand why you shouldn't. And even then, don't use it. Larry Wall has never
used goto with a label in Perl, and he worte it. Don't. (He's watching)" [1].
§4.4 Ejemplo
void foo() {
...
...
void faa() {
...
...
Final:
...
§4.5 Con los goto hay que observar las mismas precauciones señaladas para las sentencias de
selección ( 4.10.2). Es ilegal que la transferencia de control a la etiqueta 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:
void foo() {
...
goto Caso-1 // Salto a la etiqueta
int y; // Ok. no incluye inicialización
C c; // Ok. no incluye inicialización explícita [2]
{
int z = 0; // Ok. dentro de un bloque
}
int x = 3; // Error!!
for (int i=0; i<10; i++) // Error!!
...;
...
Caso-1: // Etiqueta
...
}
void foo() {
Inicio:
X x;
...
Ejemplo:
#include <iostream>
using namespace std;
int tot = 0;
struct E {
int x;
~E() { // destructor
cout << "Destruyendo objeto" << endl;
tot++;
}
};
Salida:
Destruyendo objeto
Destruyendo objeto
Destruyendo objeto
Se han destruido 3 objetos
Destruyendo objeto
Comentario
Destruyendo objeto
Se han destruido 1 objetos
Destruyendo objeto
En este caso estamos seguros que el programa no ha tenido ocasión de ejecutar las sentencias
L6/L7. Sin embargo, se han destruido (y por tanto creado previamente) dos objetos. Es fácil deducir
que la última salida corresponde a la destrucción que ocurre antes de salir de la función main,
cuando se invocan los destructores de todos los objetos automáticos definidos en dicho ámbito. Sin
embargo las dos construcciones (y la destrucción previa) no son tan evidentes.
La explicación es que este tipo de saltos transfiere el control de la ejecución, pero el compilador
supone que ha recorrido el ámbito léxico de las sentencias atravesadas, y la creación y destrucción
de objetos está relacionada con este recorrido. El esquema muestra gráficamente el proceso (las
sentencias ejecutadas se han señalado con x):
El primer recorrido (R1) crea una instancia del objeto, que es destruida en el segundo (R2), ya que
este transfiere el control a una posición anterior a la de declaración. El tercer recorrido (R3) vuelve
a crear un objeto (es la misma situación que en R1). Finalmente el objeto es destruido al salir el
programa del ámbito de main.
Puede realizarse una última comprobación encerrando la sentencia L6 en un bloque, de forma que
el recorrido léxico no incluya a su interior en el ámbito:
§5 return
La sentencia return devuelve el control de ejecución desde la función que contiene el return a la
rutina que la invocó; opcionalmente puede devolver un valor.
§5.1 Sintaxis
return [ <expresion> ] ;
§5.2 Descripción
La sentencia return devuelve el control de ejecución desde la función que contiene el return a la
rutina que la invocó. Además, opcionalmente puede devolver un valor (contenido en
<expresion>).
<expresion> es opcional, así como el paréntesis que se suele colocar, aunque no es necesario.
Si no se indica <expresion> la función no devuelve nada.
§5.3 Ejemplo
double sqr(double x) {
return (x*x);
}
Inicio.
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:
if...else .
else if .
switch .
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:
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.
if ( expresion ) <sentencia> ;
if (<condicion>) {
<sentencia1>;
else {
<sentencia2>;
§2.3 Ejemplos
Uno sencillo:
Otro ejemplo:
if (a > b)
z = a;
else
z = b;
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:
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.
if ( n > 0 )
if ( a > b )
z = a;
else
z = b;
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> ;
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> ) {
.
.
.
[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 49: case 53: case 57: case 63: case 69:
cout << "Vocal" << endl;
break;
default:
cout << "Otro carácter" << endl;
}
§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 con el compilador MS
Visual C++ 6.0 en el caso anterior: