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
- Dominar las condicionales:
if,else if,else - Entender cuándo usar cada tipo de loop
- Conocer
breakycontinue - Evitar loops infinitos y errores comunes
- Usar range-based for loops (C++11)
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
";
} 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 | 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;
} - ✅ Más legible y conciso
- ✅ Menos propenso a errores (no índices fuera de rango)
- ✅ Funciona con arrays, vectores, strings, etc.
- ✅ Usa
const auto¶ 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
// [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
} // [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
- Sé cuándo usar
ifvsswitch - Ordeno condiciones de más restrictiva a menos restrictiva
- Entiendo la diferencia entre
for,whileydo-while - Sé usar
breakycontinuecorrectamente - Evito loops infinitos verificando la condición
- Uso range-based for cuando es posible
- Entiendo la complejidad O(N²) de loops anidados
Preguntas para reflexionar
¿Cuál es la diferencia entre while y do-while?
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?
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?
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?
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?
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)
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++.