Algoritmos y Estructuras de Datos Herramientas Lenguaje de programación
!Prog C/C++ Rust
Linux Matemáticas
Mates Discretas
Programación Orientada a Objetos Sistemas Operativos

Programación Estructurada en C

[date: 16-07-2023 14:39] [last modification: 19-07-2023 17:31]
[words: 1199] [reading time: 6min] [size: 65437 bytes]

Uso de estructuras básicas para el control del flujo del programa.

Introducción

Por defecto, un programa se ejecuta de arriba a abajo; pero gracias a diferentes estructuras de control, es posible modificar y manipular este orden para realizar tareas más complicadas.

Selectivo

if

La estructura selectiva o condicional más básica es el if. Este toma una condición entre paréntesis: si se evalua a un número distinto de 0, se ejecutará la siguiente sentencia; de lo contrario, la saltará.

1if (condición)
2    sentencia; // Se ejecuta si condición != 0

Nótese que es posible ejecutar varias sentencias usando un bloque:

1if (condición) {
2    // Se ejecuta si condición != 0
3    sentencia1;
4    sentencia2;
5    sentencia3;
6}

if else

A continuación del if, se puede añadir una parte de else, que se ejecutará en caso de que la condición sea falsa (en este caso, que se evalue a 0).

1if (condición)
2    // Se ejecuta si la condición != 0
3    sentencia_if;
4else
5    // Se ejecuta si la condición == 0
6    sentencia_else;

Y de la misma forma, se pueden usar bloques si es necesario:

1if (condición) {
2    sentencia_if1;
3    sentencia_if2;
4} else {
5    sentencia_else1;
6    sentencia_else2;
7}
8
9// Y otras variantes

else if

También es posible anidar estas estructuras, es decir, añadir unas dentro de otras:

1if (condición1) {
2    if (condición2) {
3        sentencia1;
4        sentencia2;
5    } else
6        sentencia3;
7}

En concreto, si se anida un if en un bloque de else:

1if (condición1)
2    sentencia_if;
3else {
4    if (condición2) {
5        sentencia_elseif;
6    }
7}

Aunque normalmente se formatea de la siguiente manera:

1if (condición1) {
2    sentencia_if;
3} else if (condición2) {
4    sentencia_elseif;
5}

Y nótese también, que estos bloques se pueden repetir las veces que se desee (incluso en combinación con un bloque else normal).

 1if (condición) {
 2    sentencia_if;
 3} else if (condición1) {
 4    sentencia_elseif1;
 5} else if (condición2) {
 6    sentencia_elseif2;
 7} else if (condición3) {
 8    sentencia_elseif3;
 9} else {
10    sentencia_else;
11}

switch

En algunos casos es necesario hacer comprobaciones como esta:

 1if (variable == valor1) {
 2    sentencia_valor1;
 3} else if (variable == valor2) {
 4    sentencia_valor2;
 5} else if (variable == valor3) {
 6    sentencia_valor3;
 7} else if (variable == valor4) {
 8  sentencia_valor4;
 9} else if (variable == valor5) {
10    sentencia_valor5;
11} else {
12    sentencia_else;
13}

Esto resulta un poco complicado de leer, por tanto C permite crear bloques switch con este propósito:

 1switch (variable) {
 2    case valor1:
 3        sentencia_valor1;
 4    case valor2:
 5        sentencia_valor2;
 6    case valor3:
 7        sentencia_valor3;
 8    case valor4:
 9        sentencia_valor4;
10    default:
11        sentencia_else;
12}
Importante
Solo funcionan comparaciones de igualdad (==) con números enteros (int) y caracteres (char).

Sin embargo, funciona un poco diferente de lo que se esperaría. Cuando se comprueba el primer case, continua con el siguiente; lo cual es ineficiente, dado que una variable solo puede tener un único valor. Para evitar ese problema, se escribe:

 1switch (variable) {
 2    case valor1:
 3        sentencia_valor1;
 4        break;
 5    case valor2:
 6        sentencia_valor2;
 7        break;
 8    case valor3:
 9        sentencia_valor3;
10        break;
11    case valor4:
12        sentencia_valor4;
13        break;
14    default:
15        sentencia_else;
16}

La instrucción break fuerza la salida de la estructura, evitando que se siga comprobando tontamente el resto de valores.

Pero tiene un sentido de ser:

 1char x = 'a';
 2switch (x) {
 3    case 'a':
 4    case 'b':
 5    case 'c':
 6        sentencia1; // En caso de que x sea 'a', 'b' o 'c'
 7        break;
 8    case 'd':
 9        sentencia2; // En caso de que x sea 'd'
10        break;
11    default:
12        sentencia3;
13}

Repetitivo / bucles

while

1while (condición)
2    sentencia;

Se repetirá sentencia mientras la condición sea verdadera, es decir, mientras se evalue a un número no nulo.

Recuerda que también puedes usar un bloque para repetir varias sentencias:

1while (condición) {
2    sentencia1;
3    sentencia2;
4    sentencia3;
5}

do while

Esta estructura es exactamente igual que el while, pero evalua la condición después de ejecutar las sentencias del bucle. Es decir, que dichas sentencias se ejecutarán al menos una vez si la condición es falsa.

1do {
2    sentencia1;
3    sentencia2;
4    sentencia3;
5} while (condición);

Bucles infinitos

Teniendo en cuenta que un bucle while se ejecuta siempre que la condición es verdadera, es posible crear algo así:

1while (1)
2    sentencia;

Esto ejecutará sentencia infinitamente, lo que por definición no es un algoritmo válido.

Nótese que esto produce el mismo efecto:

1int x = 10;
2int y = 10;
3while (x > 0)
4    y--;

Para detectar estos bucles infinitos hay que analizar si la condición es constante y si alguno de las variables que se utilizan en ella cambian dentro del bucle.

for

Para muchos problemas será necesario algo similar a esto:

1int contador = 0;
2while (contador < 10) {
3    sentencia;
4    contador++;
5}

De esta forma, sentencia ejecutará exactamente 10 veces, con la variable contador almacenando en cuál se encuentra.

Por este motivo, C proporciona el bucle for, para acortar el código:

1for (int contador = 0; contador < 10; contador++) {
2    sentencia;
3}

Y en general:

 1for (<creación del contador>; <condición>; <actualización del contador>) {
 2    sentencia;
 3}
 4
 5// Equivalente a
 6<creación del contador>;
 7while (<condición>) {
 8    sentencia;
 9    <actualización del contador>;
10}
Conceptos
Cada vez que se repite un bucle se llama iteración y la variable que controla el bucle se llama iterador, por eso generalmente se usan nombres como i, j y k para los bucles. En el futuro veremos estructuras más complejas que también se utilizan para controlar los bucles.

break

La instrucción break ayuda a controlar mejor los bucles, dado que cuando se ejecuta, sale directamente del bucle.

Por ejemplo, es posible hacer lo siguiente:

1int i = 0;
2while (0) {
3    i++;
4    if (i == 10)
5        break;
6}

Aparentemente, la condición del while es constante, pero este bucle termina tras 10 iteraciones.

continue

De forma similar a break, continue ayuda a controlar los bucles, pero cuando se ejecuta, en lugar de salir, salta a la siguiente iteración.

Por ejemplo, este es un bucle infinito porque siempre se salta la instrucción que cambia la condición, por tanto esta es constante:

1int i = 0;
2while (i < 10) {
3    continue;
4    i++;      // Nunca se ejecuta
5}

Pero sin embargo eso no pasa en un bucle for, que simplemente lo repite 10 veces:

1for (int i = 0; i < 10; i++) {
2    continue;
3}

Bucles anidados

Al igual que los ifs dentro de otros ifs, es posible meter bucles dentro de otros.

Por ejemplo:

1for (int i = 0; i < 5; i++) {
2    sentencia1;
3    for (int j = 0; i < 3; j++) {
4        sentencia2;
5    }
6}

Nótese que sentencia1 está dentro del primer bucle, que solo da 5 vueltas. El segundo bucle está también dentro del primero, por tanto también se repite 5 veces. Por otro lado, sentencia2 se repite 3 veces de cada vez, y en total 3x5=15 veces.

Anterior: Preprocesador Volver a C/C++ Siguiente: CMake