5 Sentencias

Espero que hayas tenido la paciencia suficiente para llegar hasta aquí, y que no te hayas asustado demasiado. Ahora empezaremos a entrar en la parte interesante y estaremos en condiciones de añadir algún ejemplo.

El elemento que nos falta para empezar a escribir programas que funcionen son las sentencias.

Existen sentencias de varios tipos, que nos permitirán enfrentarnos a todas las situaciones posibles en programación. Estos tipos son:

  • Bloques
  • Expresiones
    • Llamada a función
    • Asignación
    • Nula
  • Bucles
    • while
    • do...while
    • for
  • Etiquetas
    • Etiquetas de identificación
    • case
    • default
  • Saltos
    • break
    • continue
    • goto
    • return
  • Selección
    • if...else
    • switch
  • Comentarios

Bloques

Una sentencia compuesta o un bloque es un conjunto de sentencias, que puede estar vacía, encerrada entre llaves "{}". Sintácticamente, un bloque se considera como una única sentencia.

También se usa en variables compuestas, como veremos en el capítulo de variables II, y en la definición de cuerpo de funciones. Los bloques pueden estar anidados hasta cualquier profundidad.

Expresiones

Una expresión seguida de un punto y coma (;), forma una sentencia de expresión. La forma en que el compilador ejecuta una sentencia de este tipo evaluando la expresión. Cualquier efecto derivado de esta evaluación se completará antes de ejecutar la siguiente sentencia.

<expresión>;

Llamadas a función

Esta es la manera de ejecutar las funciones que se definen en otras partes del programa o en el exterior de éste, ya sea una biblioteca estándar o particular. Consiste en el nombre de la función, una lista de argumentos entre paréntesis y un ";".

Por ejemplo, para ejecutar la función que declarábamos en el capítulo 3 usaríamos una sentencia como ésta:

Mayor(124, 1234);

Pero vamos a complicar un poco la situación para ilustrar la diferencia entre una sentencia de expresión y una expresión, reflexionemos sobre el siguiente ejemplo:

Mayor(124, Mayor(12, 1234));

Aquí se llama dos veces a la función "Mayor", la primera vez como una sentencia; la segunda como una expresión, que nos proporciona el segundo parámetro de la sentencia.

Pero en realidad, el compilador evalúa primero la expresión, de modo que se obtenga el segundo parámetro de la función, y después llama a la función. ¿Parece complicado?. Puede ser, pero también puede resultar interesante...

En el futuro diremos mucho más sobre este tipo de sentencias, pero por el momento es suficiente.

Asignación

Las sentencias de asignación responden al siguiente esquema:

<variable> <operador de asignación> <expresión>;

La expresión de la derecha es evaluada y el valor obtenido es asignado a la variable de la izquierda. El tipo de asignación dependerá del operador utilizado, estos operadores ya los vimos en el capítulo anterior.

La expresión puede ser, por supuesto, una llamada a función. De este modo podemos escribir un ejemplo con la función "Mayor" que tendrá más sentido que el anterior:

m = Mayor(124, 1234);

Nula

La sentencia nula consiste en un único ";". Sirve para usarla en los casos en los que el compilador espera que aparezca una sentencia, pero en realidad no pretendemos hacer nada. Veremos ejemplo de esto cuando lleguemos a los bucles.

Bucles

Estos tipos de sentencias son el núcleo de cualquier lenguaje de programación, y están presentes en la mayor parte de ellos. Nos permiten realizar tareas repetitivas, y se usan en la resolución de la mayor parte de los problemas.

Ada Byron
Nota:

El descubrimiento de los bucles se lo debemos a Ada Byron, así como el de las subrutina (que no es otra cosa que una función o procedimiento). Está considerada como la primera programadora, y ella misma se autodenominaba analista, lo que no deja de ser sorprendente, ya que el primer ordenador no se construyó hasta un siglo después.

Generalmente estas sentencias tienen correspondencia con estructuras de control equivalentes en pseudocódigo. El pseudocódigo es un lenguaje creado para expresar algoritmos formalmente y de manera clara. No es en si mismo un lenguaje de programación, sino más bien, un lenguaje formal (con reglas muy extrictas), pero humano, que intenta evitar ambigüedades.

A su vez, un algoritmo es un conjunto de reglas sencillas, que aplicadas en un orden determinado, permiten resolver un problema más o menos complejo.

Por ejemplo, un algoritmo para saber si un número N, es primo, puede ser:

Cualquier número es primo si sólo es divisible entre si mismo y la unidad.

Por lo tanto, para saber si un número N es primo o no, bastará con dividirlo por todos los números entre 2 y N-1, y si ninguna división es exacta, entonces el número N es primo.

Pero hay algunas mejoras que podemos aplicar:

La primera es que no es necesario probar con todos los números entre 2 y N-1, ya que podemos dar por supuesto que si N no es divisible entre 2, tampoco lo será para ningún otro número par: 4, 6, 8..., por lo tanto, después de probar con 2 pasaremos al 3, y después podemos probar sólo con los impares.

La segunda es que tampoco es necesario llegar hasta N-1, en realidad, sólo necesitamos llegar hasta el valor entero más cercano a la raíz cuadrada de N.

Esto es así porque estamos probando con todos los números menores que N uno a uno. Supongamos que vamos a probar con un número M mayor que la raíz cuadrada de N. Para que M pudiera ser un divisor de N debería existir un número X que multiplicado por M fuese igual a N.

N = M x X

El caso extremo, es aquel en el que M fuese exactamente la raíz cuadrada de N. En ese caso, el valor de X sería exactamente M, ya que ese es el valor de la raíz cuadrada de N:

N = M2 = M x M

Pero en el caso de que M fuese mayor que la raíz cuadrada de N, entonces el valor de X debería ser menor que la raíz cuadrada de N. Y el caso es que ya hemos probado con todos los números menores que la raíz cuadrada de N, y ninguno es un divisor de N.

Por lo tanto, ningún número M mayor que la raíz cuadrada de N puede ser divisor de N si no existen números menores que la raíz cuadrada de N que lo sean.

El pseudocódigo para este algoritmo sería algo parecido a esto:

¿Es N=1? -> N es primo, salir
¿Es N=2? -> N es primo, salir
Asignar a M el valor 2
mientras M <= raíz cuadrada(N) hacer:
   ¿Es N divisible entre M? -> N no es primo, salir
   Si M=2 entondes Asignar a M el valor 3
   Si M distinto de 2 entonces Asignar a M el valor M+2
Fin del mientras
N es primo ->salir

Bucles "mientras"

Es la sentencia de bucle más sencilla, y sin embargo es tremendamente potente. En C++ se usa la palabra reservada while (que significa "mientras"), y la sintaxis es la siguiente:

while (<condición>) <sentencia> 

La sentencia es ejecutada repetidamente mientras la condición sea verdadera. Si no se especifica condición se asume que es true, y el bucle se ejecutará indefinidamente. Si la primera vez que se evalúa la condición resulta falsa, la sentencia no se ejecutará ninguna vez.

Las condiciones no son otra cosa que expresiones de tipo booleano, cualquier otro tipo de expresión se convertirá a tipo booleano, si es posible. Y si no lo es, se producirá un error.

Por ejemplo:

while (x < 100) x = x + 1;

Se incrementará el valor de x mientras x sea menor que 100.

Este ejemplo puede escribirse, usando el C++ con propiedad y elegancia (es decir, con clase), de un modo más compacto:

while (x++ < 100);

Aquí vemos el uso de una sentencia nula, de la que hablábamos hace un rato. Observa que el bucle simplemente se repite, y la sentencia ejecutada es ";", es decir, nada.

Nota:

En realidad estos dos bucles no son equivalentes, ya que el valor de x al finalizar el segundo bucle es 101, y al finalizar el primero es 100.

Bucle "hacer...mientras"

Esta sentencia va un paso más allá que el while. La sintaxis es la siguiente:

do <sentencia> while(<condicion>);

La sentencia es ejecutada repetidamente mientras la condición resulte verdadera. Si no se especifica condición se asume que es true, y el bucle se ejecutará indefinidamente.

En otros lenguajes, como PASCAL, se usa para el mismo tipo de bucle la estructura "repetir...hasta", es decir la sentencia se repite hasta que se cumpla una determinada condición. La diferencia está en que la lógica es la inversa: la sentencia se repite mientras la condición sea falsa. El resultado es el mismo, en cualquier caso.

A diferencia del bucle while, la evaluación de la condición se realiza después de ejecutar la sentencia, de modo que ésta se ejecutará al menos una vez. Por ejemplo:

do
   x = x + 1;
while (x < 100);

En este bucle se incrementará el valor de x hasta que valga 100.

Pero aunque la condición sea falsa, por ejemplo, si x vale inicialmente 200, la sentencia x = x + 1;, se ejecuta primero, y después se verifica la condición.

Se pueden construir bucles do...while usando bucles while, pero a costa de repetir la sentencia dos veces:

<sentencia>
while(<condición>) <sentencia>

Esto puede hacernos pensar que estos bucles no son necesarios, y efectivamente, así es. Pero nos facilitan las cosas a la hora de codificar algoritmos basados en bucles do...while, ya que, por una parte, nos ahorran el trabajo de escribir dos veces el mismo código, y por otra, disminuyen las posibilidades de errores en una de las repeticiones, sobre todo al corregir un error en el código, cuando es más fácil olvidar que estamos corrigiendo un bucle do...while, por lo que tendríamos que corregirlo dos veces.

Además, no olvidemos que existen sentencias de bloque, que pueden constar de cientos de sentencias simples, cada una de las cuales puede ser una sentencia cualquiera de las que estamos estudiando.

Bucle "para"

Por último el bucle para, que usa la palabra reservada for. Este tipo de bucle es el más elaborado. La sintaxis es:

for ( [<inicialización>]; [<condición>] ; [<incremento>] )
   <sentencia>;

La sentencia es ejecutada repetidamente mientras la condición resulte verdadera, o expresado de otro modo, hasta que la evaluación de la condición resulte falsa.

Antes de la primera iteración se ejecutará la iniciación del bucle, que puede ser una expresión o una declaración. En este apartado se suelen iniciar las variables usadas en el bucle. Estas variables también pueden ser declaradas en este punto, pero en ese caso tendrán validez (ámbito) sólo dentro del bucle for.

Después de cada iteración se ejecutará el incremento de las variables del bucle. Este incremento también es una sentencia de asignación, y no tiene por qué ser necesariamente un incremento.

En general, podemos considerar que la parte de inicialización establece las condiciones iniciales del bucle, la parte de la condición establece la condición de salida, y la parte del incremento, modifica las condiciones iniciales para establecer las de la siguiente iteración del bucle, o para alcanzar la condición de salida.

Todas las expresiones son opcionales, y si no se especifica la condición se asume que es verdadera. Ejemplos:

for(int i = 0; i < 100; i = i + 1);
for(int i = 100; i < 0; i = i - 1);

Como las expresiones son opcionales, podemos simular bucles while:

for(;i < 100;) i = i + 1;
for(;i++ < 100;);

O bucles infinitos:

for(;;);

En realidad, podemos considerar un bucle for como una extensión de un bucle while. La equivalencia entre un bucle for y un bucle while es la siguiente:

[<inicialización>];
   while([<condición>]) {
      <sentencia>
      [<incremento>]
   }

Evidentemente, los bucles for no son extrictamente necesarios, (como tampoco lo son los bucles do...while), pero en muchas ocasiones simplifican el código de los bucles, haciendo más fácil la comprensión de un programa, ya sea para su análisis, su modificación o para su depuración.

En un estilo de programación claro, los bucles for se suelen utilizar para recorrer listas de elementos (como veremos el tema de los arrays). Usados con esta función, la inicialización se limita a asignar el valor inicial de un índice que sirve para recorrer la lista, la condición comprueba si hemos llegado al final de la lista, y el incremento modifica el índice para que apunte al siguiente elemento de la lista.

Otra cosa, por muy tentador que resulte a veces, debemos intentar resistirnos a la tentación de usar los bucles for para emular otro tipo de bucles, ya que generalmente, esto puede inducir a malas interpretaciones sobre la finalidad del código, dificultando la depuración o el análisis.

Veamos un ejemplo sencillo:

int i=0;
for(bool salir = false; !salir; salir = (i > -5)) i++;

Aunque este bucle funcione, y haga lo que el programador tiene intención que haga, averiguar cómo lo hace requerirá cierto esfuerzo por nuestra parte. Supongo que estarás de acuerdo conmigo en que esto se puede expresar más claramente de otro modo:

i = 0;
do {
   i++;
} while(i <= -5);

Etiquetas

Los programas C++ se ejecutan secuencialmente, es decir, las sentencias se ejecutan una a continuación de otra, en el mismo orden en que están escritas en el programa.

Sin embargo, esta secuencia puede ser interrumpida de varias maneras, mediante el uso de sentencias de salto.

Las etiquetas son la forma en que se indica al compilador en qué puntos será reanudada la ejecución de un programa cuando se produzca una ruptura del orden secuencial de ejecución.

Etiquetas de identificación

Corresponden con la siguiente sintaxis:

<identificador>: <sentencia>

Estas etiquetas sirven como puntos de entrada para la sentencia de salto goto, que veremos más abajo, y tienen el ámbito restringido a la función dentro de la cual están definidas. Veremos su uso con más detalle al analizar la sentencia goto.

Etiquetas case y default

Esta etiqueta se circunscribe al ámbito de la sentencia switch, y se verá su uso en el siguiente apartado. Sintaxis:

switch(<variable>)
{
   case <expresión_constante>: [<sentencias>][break;]
   . . .
   [default: [<sentencias>]]
}

Selección

Las sentencias de selección permiten controlar el flujo del programa, seleccionando distintas sentencias en función de diferentes circunstancias.

Sentencia if...else

Permite la ejecución condicional de una sentencia. Sintaxis:

if (<condición>) <sentencia1>
  [else <sentencia2>]

En C++ se pueden declarar variables dentro de la expresión de condición, que será, como en el caso de los bucles, una expresión booleana. Por ejemplo:

if(int val = func(arg))...

En este caso, la variable val sólo será accesible dentro del ámbito de la sentencia if y, si existe, del else.

Si la condición es verdadera se ejecutará la sentencia1, si es falsa, (y si existe la parte del else), se ejecutará la sentencia2.

La cláusula else es opcional, y si se usa, no pueden insertarse sentencias entre la sentencia1 y el else.

Sentencia switch

Esta sentencia es una generalización de las sentencias if...else. En el caso de las sentencias if, la expresión que se evalúa como condición es booleana, lo que quiere decir que sólo hay dos valores posibles, y por lo tanto, sólo se puede elegir entre dos sentencias a ejecutar.

En el caso de la sentencia switch, la expresión a evaluar será entera, por lo tanto, el número de opciones es mucho mayor, y en consecuencia, también es mayor el número de diferentes sentencias que se pueden ejecutar.

Sintaxis:

switch (<expresión entera>)
{
   [case <expresión_constante1>: [<sentencias1>]]
   [case <expresión_constante2>: [<sentencias2>]]
   ...
   [case <expresión_constanten>: [<sentenciasn>]]
   [default : [<sentencia>]]
}

Cuando se usa la sentencia switch el control se transfiere al punto etiquetado con el case cuya expresión constante coincida con el valor de la expresión entera evaluada dentro del switch. A partir de ese punto todas las sentencias serán ejecutadas hasta el final del switch, es decir hasta llegar al "}". Esto es así porque las etiquetas sólo marcan los puntos de entrada después de una ruptura de la secuencia de ejecución, pero no marcan los puntos de salida.

Esta estructura está diseñada para ejecutar cierta secuencia de instrucciones, empezando a partir de un punto diferente, en función de un valor entero y dejando sin ejecutar las anteriores a ese punto.

Veamos un ejemplo. Para hacer un pan hay que seguir (en una versión resumida), la siguiente secuencia:

  1. Conseguir las semillas de trigo
  2. Arar el campo
  3. Sembrar el trigo
  4. Esperar a que madure (No soy agricultor, pero sospecho que esta etapa es algo más complicada).
  5. Cosechar el trigo
  6. Separar el grano de la paja
  7. Moler el grano, para coseguir harina
  8. Amasar la harina junto con agua, sal y levadura. (Dejaremos de momento de lado el método para conseguir estos ingredientes).
  9. Dejar reposar la masa
  10. Encender el horno. (También dejaremos de momento los métodos para conseguir leña, construir un horno, o encenderlo).
  11. Meter el pan crudo en el horno.
  12. Esperar a que se haga el pan.
  13. Sacar el pan del horno.

A cada paso le hemos asignado un número entero. Si por ejemplo, tenemos que hacer un pan, pero ya hemos hecho los primeros siete pasos, es decir, disponemos de harina, la ejecución empezará en el paso ocho, y continuará hasta el trece. De forma análoga, si ya tenemos masa dejada a reposar, podremos entrar directamente en el nivel diez.

Esta característica también nos permite ejecutar las mismas sentencias para varias etiquetas distintas, y en el apartado siguiente veremos (aunque vamos a adelantarlo ahora) que se puede eludir la ejecución secuencial normal usando la sentencia de ruptura break para ejecutar sólo parte de las sentencias.

Si el valor de la expresión entera no satisface ningún case, es decir, si no existe un case con una expresión constante igual al valor de la expresión entera, el control parará a la sentencia etiquetada con la etiqueta default.

Todas las etiquetas son opcionales, tanto default como todos los case. Si no se satisface ningún case, ni aparece la etiqueta default, se abandonará la sentencia switch sin ejecutar ninguna sentencia.

Por ejemplo:

bool EsVocal;
char letra;
...
switch(letra)
{
   case 'a':
   case 'e':
   case 'i':
   case 'o':
   case 'u':
      EsVocal = true;
      break;
   default:
      EsVocal = false;
}

En este ejemplo letra es una variable de tipo char y EsVocal de tipo bool. Si el valor de entrada en el switch corresponde a una vocal, EsVocal saldrá con un valor verdadero, en caso contrario, saldrá con un valor falso. El ejemplo ilustra el uso del break. Si por ejemplo, letra contiene el valor 'a', se cumple el primer case, y la ejecución continúa en la siguiente sentencia: EsVocal = true, ignorando el resto de los case hasta el break, que nos hace abandonar la sentencia switch.

Otro ejemplo:

int Menor1, Menor2, Menor3, Mayor3;

Menor1 = Menor2 = Menor3 = Mayor3 = false;
switch(numero)
{
   case 0:
      Menor1 = true;
   case 1:
      Menor2 = true;
   case 2:
      Menor3 = true;
      break;
   default:
      Mayor3 = true;
}

Veamos qué pasa en este ejemplo si numero vale 1. Directamente se reanuda la ejecución en case 1:, con lo cual Menor2 tomará el valor true, lo mismo pasará con Menor3. Después aparece el break y se abandona la sentencia switch.

Recordemos que los tipos enumerados se consideran también como enteros, de modo que también es posible usarlos en sentencias switch, de hecho, su uso en estas sentencias es bastante frecuente.

enum estado {principio, primera, segunda, tercera, final};

estado x;

x = primera;

switch(x) {
   case principio: IniciarProceso();
   case primera:   PrimeraFase();
   case segunda:   SegundaFase();
   case tercera:   TerceraFase();
   case final:     Terminar()
}

Sentencias de salto

Como vimos al hablar sobre las etiquetas, los programas C++ se ejecutan secuencialmente, pero existen formas de romper este orden secuencial, mediante el uso de sentencias de salto, que veremos a continuación.

Sentencia de ruptura

El uso de esta sentencia dentro de un bucle, una sentencia de selección o de un bloque, transfiere la ejecución del programa a la primera sentencia que haya a continuación. Esto es válido para sentencias switch, como vimos hace un rato, pero también lo es para sentencias while, do...while, for e if.

En general, una sentencia break transfiere la ejecución secuencial a la siguiente sentencia, abandonando aquella en que se ejecuta.

Sintaxis:

break

Ejemplo:

int c = 0;
{
   for(int x=0; x < 100; x++) c+=x;
   break;
   c += 100;
}
c /= 100;

En este ejemplo, la sentecia c += 100; no se ejecutará, ya que la sentencia break transfiere la ejecución secuencial fuera del bloque.

Otro ejemplo:

y = 0;
x = 0;
while(x < 1000)
{
   if(y == 1000) break;
   y++;
}
x = 1;

En este otro ejemplo el bucle no terminaría nunca si no fuera por la línea del break, ya que x no cambia. Después del break el programa continuaría en la línea x = 1.

Sentencia continue

El uso de esta sentencia dentro de un bucle ignora el resto del código de la iteración actual, y comienza con la siguiente, es decir, se transfiere la ejecución a la evaluación de la condición del bucle. Sintaxis:

continue

Ejemplo:

y = 0;
x = 0;
while(x < 1000)
{
   x++;
   if(y >= 100) continue;
   y++;
}

En este ejemplo la línea y++ sólo se ejecutaría mientras y sea menor que 100, en cualquier otro caso el control pasa a la siguiente iteración, con lo que la condición del bucle volvería a evaluarse.

Sentencia de salto

Con el uso de esta sentencia el control se transfiere directamente al punto etiquetado con el identificador especificado.

Nota:

El goto es un mecanismo que está en guerra permanente, y sin cuartel, con la programación estructurada. Las sentencias goto no se deben usar cuando se resuelven problemas mediante programación estructurada, se incluye aquí porque existe, pero siempre puede y debe ser eludido. Existen mecanismos suficientes para hacer de otro modo todo aquello que pueda realizarse con mediante goto.

En cualquier caso, nosotros somos los programadores, y podemos decidir que para cierto programa o fragmento de programa, las ventajas de abandonar la programación estructurada pueden compensar a los inconvenientes. A veces es imperativo sacrificar claridad en favor de velocidad de ejecución. Pero de todos modos, serán situaciones excepcionales.

Sintaxis:

goto <identificador>

Ejemplo:

x = 0;
Bucle:
x++;
if(x < 1000) goto Bucle;

Este ejemplo emula el funcionamiento de un bucle for como el siguiente:

for(x = 0; x < 1000; x++);

Sentencia de retorno

Esta es la sentencia de salida de una función, cuando se ejecuta, se devuelve el control a la rutina que llamó a la función.

Además, se usa para especificar el valor de retorno de la función. Sintaxis:

return [<expresión>]

Ejemplo:

int Paridad(int x)
{
   if(x % 2) return 1;
   return 0;
}

Este ejemplo ilustra la implementación de una función que calcula la paridad de un valor pasado como parámetro. Si el resto de dividir el parámetro entre 2 es distinto de cero, implica que el parámetro es impar, y la función retorna con valor 1. El resto de la función no se ejecuta. Si por el contrario el resto de dividir el parámetro entre 2 es cero, el parámetro será un número par y la función retornará con valor cero.

Es importante dejar siempre una sentencia return sin condiciones en todas las funciones. Esto es algo que a veces no se tiene en cuenta, y aunque puede no ser extrictamente necesario, siempre es conveniente. El ejemplo anterior se podría haber escrito de otro modo, sin tener en cuenta esto:

int Paridad(int x)
{
   if(x % 2) return 1;
   else return 0;
}

En este caso, para nosotros está claro que siempre se ejecutará una de las dos sentencias return, ya que cada una está en una de las alternativas de una sentencia if...else. Sin embargo, el compilador puede considerar que todas las sentencias return están en sentencias de selección, sin molestarse en analizar si están previstas todas las salidas posibles de la función, con lo que puede mostrar un mensaje de error.

El primer ejemplo es mejor, ya que existe una salida incondicional. Esto no sólo evitará errores del compilador, sino que nos ayudará a nosotros mismos, ya que vemos que existe un comportamiento incondicional.

El único caso en que una sentencia return no requiere una expresión es cuando el valor de retorno de la función es void.

Existe un mal uso de las sentencias return, en lo que respecta a la programación estructurada. Por ejemplo, cuando se usa una sentencia return para abandonar una función si se detecta un caso especial:

int Dividir(int numerador, int denominador)
{
   int cociente;

   if(0 == denominador) return 1;
   cociente = numerador/denominador;
   return cociente;
}

Esta función calcula el cociente de una división entera. Pero hemos querido detectar un posible problema, ya que no es posible dividir por cero, hemos detectado ese caso particular, y decidido (erróneamente) que cualquier número dividido por cero es uno.

Sin embargo, en este caso, el primer return se comporta como una ruptura de secuencia, ya que se abandona la función sin procesarla secuencialmente. Siendo muy puristas (o pedantes), podemos considerar que esta estructura no corresponde con las normas de la programación estructurada. Un ejemplo más conforme con las normas sería:

int Dividir(int numerador, int denominador)
{
   int cociente;

   if(0 == denominador) cociente = 1;
   else cociente = numerador/denominador;
   return cociente;
}

Sin embargo, a menudo usaremos estos atajos para abandorar funciones en caso de error, sacrificando el método en favor de la claridad en el código.

Uso de las sentencias de salto y la programación estructurada

Lo dicho para la sentencia goto es válido en general para todas las sentencias de salto. En el caso de la sentencia break podemos ser un poco más tolerantes, sobre todo cuando se usa en sentencias switch, donde resulta imprescindible. En general, es una buena norma huir de las sentencias de salto.

Comentarios

Los comentarios no son sentencias, pero me parece que es el lugar adecuado para hablar de ellos. En C++ pueden introducirse comentarios en cualquier parte del programa. Su función es ayudar a seguir el funcionamiento del programa durante la depuración o en la actualización del programa, además de documentarlo. Los comentarios en C, que también se pueden usar en C++, se delimitan entre /* y */, cualquier cosa que escribamos en su interior será ignorada por el compilador.

Sólo está prohibido su uso en el interior de palabras reservadas, de identificadores o de cadenas literales. Por ejemplo:

int main(/*Sin argumentos*/void)

está permitido, pero sin embargo:

int ma/*función*/in(void)

es ilegal.

Esto no es una limitación seria, a fin de cuentas, se trata de aclarar y documentar, no de entorpecer la lectura del código.

En C++ existe otro tipo de comentarios, que empiezan con //. Estos comentarios no tienen marca de final, sino que terminan cuando termina la línea. Por ejemplo:

int main(void) // Esto es un comentario
{
   return 0;
}

El cuerpo de la función no forma parte del comentario.

Palabras reservadas usadas en este capítulo

break, case, continue, default, do, else, for, goto, if, return, switch y while.