Estructuras de Control

Capítulo 5: Estructuras de Control

Las estructuras de control determinan el flujo de ejecución de tu programa. En este capítulo aprenderás a tomar decisiones con if/else y a repetir operaciones con loops (for, while). Son las piezas fundamentales para escribir lógica compleja.

Objetivos

Condicionales: if/else

Las estructuras condicionales permiten que tu programa tome decisiones basándose en condiciones que se evalúan en tiempo de ejecución. Son fundamentales para crear lógica adaptativa: el programa puede responder diferente según el input del usuario, el estado del sistema, o cálculos previos.

¿Cómo funciona internamente? Cuando el procesador encuentra un if, evalúa la condición (que debe resultar en true o false). Si es verdadera, ejecuta el bloque de código; si es falsa, salta directamente a la siguiente instrucción después del bloque. A nivel de assembly, esto se traduce en instrucciones de comparación (CMP) seguidas de saltos condicionales (JMP).

Sintaxis básica

#include <iostream>

int main() {
    int age = 25;

    if (age >= 18) {
        std::cout << "Eres mayor de edad
";
    } else {
        std::cout << "Eres menor de edad
";
    }

    return 0;
}

Múltiples condiciones

int score = 85;

if (score >= 90) {
    std::cout << "A
";
} else if (score >= 80) {
    std::cout << "B
";
} else if (score >= 70) {
    std::cout << "C
";
} else {
    std::cout << "F
";
}
¡Cuidado con el orden!

Las condiciones se evalúan en orden. Una vez que una es verdadera, se ejecuta ese bloque y se ignoran las demás, incluso si también son verdaderas.

// [X] Mal: la segunda condición nunca se alcanza
if (score >= 70) {      /*...*/
}                       // Se ejecuta si score=85
else if (score >= 80) { /*...*/
}                       // Nunca se alcanza!

// [OK] Bien: del más restrictivo al menos restrictivo
if (score >= 90) {        /*...*/
} else if (score >= 80) { /*...*/
} else if (score >= 70) { /*...*/
}

Operador ternario

int a = 10;
int b = 20;

// Forma larga
int max;
if (a > b) {
    max = a;
} else {
    max = b;
}

// Forma corta (operador ternario)
int max = (a > b) ? a : b;

El operador ternario es útil para asignaciones condicionales simples, pero evita abusar de él.

Loops: for

Los loops (bucles) permiten ejecutar un bloque de código repetidamente. Son esenciales para procesar colecciones de datos, realizar cálculos iterativos, o repetir acciones hasta que se cumpla una condición. El loop for es ideal cuando conoces de antemano cuántas iteraciones necesitas.

¿Por qué necesitamos loops? Imagina que necesitas procesar 1000 elementos de un array. Sin loops, tendrías que escribir 1000 líneas de código casi idénticas. Los loops te permiten escribir la lógica una vez y repetirla automáticamente, haciendo el código más corto, legible y mantenible.

Loop For Clásico

#include <iostream>

int main() {
    // Imprimir números del 0 al 9
    for (int i = 0; i < 10; i++) {
        std::cout << i << " ";
    }
    // Output: 0 1 2 3 4 5 6 7 8 9

    return 0;
}

Anatomía del Loop For

flowchart TB
    A["for (int i = 0; i < 10; i++)"] --> B["int i = 0"]
    A --> C["i < 10"]
    A --> D["i++"]

    B --> B1["Inicialización<br/>(se ejecuta UNA vez al inicio)"]
    C --> C1["Condición<br/>(se evalúa antes de cada iteración)"]
    D --> D1["Incremento<br/>(después de cada iteración)"]

    E["Flujo de ejecución:"] --> E1["1. Inicialización: int i = 0"]
    E1 --> E2["2. ¿Condición verdadera? (i < 10)"]
    E2 --> E3["3. Ejecuta el cuerpo del loop"]
    E3 --> E4["4. Incremento: i++"]
    E4 --> E5["5. Vuelve al paso 2"]

    style A fill:#e1f5fe,stroke:#0277bd,stroke-width:3px
    style B fill:#fff3e0,stroke:#e65100,stroke-width:2px
    style C fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px
    style D fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px

En loops, i++ y ++i tienen el mismo efecto para tipos primitivos, pero ++i es mejor práctica.

Loops: while y do-while

While loop

int count = 0;

while (count < 5) {
    std::cout << "Count: " << count << "
";
    count++;
}
// Imprime Count: 0, 1, 2, 3, 4

Do-while loop

int num;
do {
    std::cout << "Ingresa un número positivo: ";
    std::cin >> num;
} while (num <= 0);

// Se ejecuta al menos UNA vez, incluso si num > 0
Diferencia clave: while vs do-while
Tipo Evalúa condición Ejecuciones mínimas
while Antes del cuerpo 0 (puede no ejecutarse)
do-while Después del cuerpo 1 (siempre se ejecuta al menos una vez)

Break y continue

// break: Sale del loop
for (int i = 0; i < 10; i++) {
    if (i == 5) break;  // Sale cuando i=5
    std::cout << i << " ";
}
// Output: 0 1 2 3 4

// continue: Salta a la siguiente iteración
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue;  // Salta números pares
    std::cout << i << " ";
}
// Output: 1 3 5 7 9

Break vs Continue

flowchart TB
    subgraph "break: Sale del loop"
    B1["Loop cuerpo"] --> B2["if (x)"]
    B2 --> B3["break"]
    B3 --> B4["Sale del loop<br/>completamente"]
    end

    subgraph "continue: Salta a siguiente iteración"
    C1["Loop cuerpo"] --> C2["if (x)"]
    C2 --> C3["continue"]
    C3 --> C4["Salta a la<br/>siguiente iteración"]
    C1 --> C5["código..."]
    C5 -.->|"este código no se<br/>ejecuta si continue"| C4
    end

    style B3 fill:#ffebee,stroke:#c62828,stroke-width:2px
    style C3 fill:#fff3e0,stroke:#f57c00,stroke-width:2px

Loops anidados

// Tabla de multiplicar
for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 5; j++) {
        std::cout << i * j << "	";
    }
    std::cout << "
";
}

/* Output:
1  2  3  4  5
2  4  6  8  10
3  6  9  12 15
4  8  12 16 20
5  10 15 20 25
*/

Cada nivel de anidación multiplica el número de iteraciones exponencialmente.

Range-based for loop (C++11)

#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {10, 20, 30, 40, 50};

    // Range-based for loop (C++11)
    for (int num : nums) {
        std::cout << num << " ";
    }
    // Output: 10 20 30 40 50

    return 0;
}
Ventajas del Range-Based For
  • ✅ Más legible y conciso
  • ✅ Menos propenso a errores (no índices fuera de rango)
  • ✅ Funciona con arrays, vectores, strings, etc.
  • ✅ Usa const auto& para evitar copias innecesarias
// [X] Copia cada elemento (ineficiente para objetos grandes)
for (MyObject obj : vec) {
    ...
}

// [OK] Referencia (no copia, pero puedes modificar)
for (MyObject& obj : vec) {
    ...
}

// [OK] Referencia constante (no copia, no modifica)
for (const MyObject& obj : vec) {
    ...
}

// [OK] Auto + const& (el compilador deduce el tipo)
for (const auto& obj : vec) {
    ...
}

Errores comunes

Loop Infinito
// [X] Loop infinito: i nunca cambia
int i = 0;
while (i < 10) {
    std::cout << i << "\n";
    // Falta: i++
}

// [X] Loop infinito: condición siempre verdadera
for (int i = 0; i > -1; i++) {
    // i siempre será >= 0
}
Off-by-One Error
// [X] Se pasa del límite
for (int i = 0; i <= 10; i++) {
    // Itera 11 veces (0-10), no 10
}

// [OK] Correcto
for (int i = 0; i < 10; i++) {
    // Itera 10 veces (0-9)
}

Checklist

Preguntas para reflexionar

¿Cuál es la diferencia entre while y do-while?
Respuesta:

while: Evalúa la condición antes de ejecutar el cuerpo. Puede ejecutarse 0 veces.

do-while: Evalúa la condición después de ejecutar el cuerpo. Siempre se ejecuta al menos 1 vez.

Usa do-while cuando necesites que el código se ejecute al menos una vez (ej: menús, validación de input).

¿Qué hace break dentro de un loop anidado?
Respuesta:

break solo sale del loop más interno, no de todos los loops.

for (int i = 0; i < 10; i++) {
  for (int j = 0; j < 10; j++) {
    if (j == 5) break; // Solo sale del loop j
  }
  // El loop i continúa
}

Si necesitas salir de todos los loops, usa un goto o una variable flag.

¿Por qué for (int i = 0; i < 10; i++) itera 10 veces y no 11?
Respuesta:

La condición es i < 10 (estrictamente menor), no i <= 10.

Iteraciones:

  • i=0: 0 < 10 ✓
  • i=1: 1 < 10 ✓
  • ...
  • i=9: 9 < 10 ✓
  • i=10: 10 < 10 ✗ (se detiene)

Total: 10 iteraciones (0 a 9).

Para iterar 11 veces (0 a 10), usa i <= 10.

¿Cuándo debo usar const auto& en un range-based for?
Respuesta:

Usa const auto& cuando:

  • Solo necesitas leer los valores (no modificar)
  • Los elementos son objetos grandes (evita copias)
  • Quieres que el compilador deduzca el tipo automáticamente
std::vector<std::string> names = ("Alice", "Bob");

// ❌ Copia cada string (ineficiente)
for (auto name : names) (...)

// ✅ Referencia constante (eficiente)
for (const auto& name : names) (...)
¿Puedo modificar la variable de control (i) dentro de un for loop?
Respuesta:

Técnicamente sí, pero es muy mala práctica.

for (int i = 0; i < 10; i++) {
  i += 2; // ❌ Confuso y propenso a errores
}

Problemas:

  • Difícil de predecir cuántas iteraciones habrá
  • Puede causar loops infinitos o comportamiento inesperado
  • Dificulta el mantenimiento

Si necesitas incrementos personalizados, usa while o modifica el incremento en la definición del for: for (int i = 0; i < 10; i += 3)

Próximo Capítulo

En el Capítulo 5 aprenderás sobre arrays y punteros, conceptos fundamentales para entender cómo se almacenan y acceden los datos en memoria. Los punteros son una de las características más poderosas (y peligrosas) de C++.

Siguiente capítulo: Arrays y Punteros →