Curso C++

El Ciclo for y la Repetición Controlada en C++

1. El Problema de Repetir: ¿Por Qué Necesitamos un Ciclo?

Imaginá que tenés que registrar la temperatura de 5 días seguidos. Sin conocimientos de bucles, probablemente escribirías esto:

#include <iostream>

int main() {
    int tempDia1, tempDia2, tempDia3, tempDia4, tempDia5;

    std::cout << "Ingresá la temperatura del día 1: ";
    std::cin >> tempDia1;
    std::cout << "Ingresá la temperatura del día 2: ";
    std::cin >> tempDia2;
    std::cout << "Ingresá la temperatura del día 3: ";
    std::cin >> tempDia3;
    std::cout << "Ingresá la temperatura del día 4: ";
    std::cin >> tempDia4;
    std::cout << "Ingresá la temperatura del día 5: ";
    std::cin >> tempDia5;

    return 0;
}

¿Qué pasa si en vez de 5 días fueran 500? ¿O 5000? ¿Vas a crear 500 variables distintas? Ese enfoque no escala. Copiar y pegar el mismo código con cambios mínimos es propenso a errores: podés olvidarte de renombrar una variable, saltarte un día, o introducir bugs difíciles de rastrear. En programación, esto se conoce como violar el principio DRY (Don’t Repeat Yourself).

La solución es el ciclo for, una estructura de control que ejecuta un bloque de código un número determinado de veces de forma controlada y predecible. A nivel de máquina, la CPU ejecuta un salto condicional hacia atrás en el flujo de instrucciones, evitando tener que duplicar el código físicamente.

El contador del bucle se almacena típicamente en un registro de la CPU o en la pila (stack) como una variable local de tipo entero. El compilador traduce todo el bloque a instrucciones de máquina eficientes con un solo salto condicional.

2. La Anatomía del for: Los Tres Pilares

El for en C++ tiene una sintaxis que separa tres responsabilidades distintas con punto y coma (;):

for ( inicialización ; condición ; actualización )
    instrucción;

Cada uno de estos tres pilares tiene un momento exacto de ejecución y un rol específico. Vamos a desglosarlos.

2.1. Inicialización: El Punto de Partida

La inicialización se ejecuta una sola vez antes que cualquier otra cosa dentro del for. Es el punto de partida del ciclo.

for (int dia = 1; dia <= 5; dia++) {
    std::cout << "Día " << dia << std::endl;
}

En este ejemplo, int dia = 1 es la inicialización. Se ejecuta apenas el programa llega al for, se crea la variable dia con valor 1, y después el control pasa a la condición.

La variable contador puede llamarse de cualquier forma (i, j, contador, dia, vuelta), pero lo importante es que su declaración ocurre en este primer espacio. C++ permite tanto declarar la variable directamente aquí como usar una ya declarada antes.

La inicialización es como dar el primer paso antes de arrancar una carrera: se hace una sola vez y define desde dónde empezás.

2.2. Condición: La Regla para Seguir

La condición se evalúa antes de cada iteración, incluyendo la primera. Si el resultado es true (distinto de cero), el cuerpo del bucle se ejecuta. Si es false (cero), el bucle termina y el programa continúa con la instrucción siguiente al for.

for (int dia = 1; dia <= 5; dia++) {
    std::cout << "Procesando día " << dia << std::endl;
}

Acá dia <= 5 es la condición. Mientras dia sea menor o igual a 5, el bucle sigue ejecutándose. Cuando dia llegue a 6, la condición se evalúa como false y el ciclo termina.

Si la condición es false desde el principio, el cuerpo del for nunca se ejecuta. Por ejemplo, for (int i = 10; i < 5; i++) daría 0 vueltas. El bucle puede dar cero iteraciones sin ser un error.

La condición usa los operadores relacionales que ya conocés (<, <=, >, >=, ==, !=). Estos operadores devuelven un valor de tipo bool: true (1) si la relación se cumple, false (0) si no.

2.3. Actualización (Paso): Cómo Avanza el Contador

La actualización se ejecuta al final de cada iteración, justo después del cuerpo del bucle y antes de volver a evaluar la condición. Su propósito es avanzar el contador.

for (int dia = 1; dia <= 5; dia++) {
    std::cout << "Día " << dia << std::endl;
}

dia++ es la actualización. En cada vuelta, después de ejecutar el cuerpo, se incrementa dia en 1. Sin esta línea, el contador nunca avanzaría y el bucle sería infinito (si la condición nunca se vuelve false).

El operador ++ suma 1 a la variable, y -- resta 1. Ambos tienen dos variantes:

  • Prefijo (++i): incrementa y devuelve el valor ya incrementado.
  • Postfijo (i++): devuelve el valor original y luego incrementa.

En un for tradicional, el efecto es el mismo porque el valor retornado no se usa, pero es importante saber que existen.

// Avance de 2 en 2
for (int i = 0; i < 10; i = i + 2) {
    std::cout << i << " ";
}
// Salida: 0 2 4 6 8

// Decremento (cuenta regresiva)
for (int i = 10; i > 0; i--) {
    std::cout << i << " ";
}
// Salida: 10 9 8 7 6 5 4 3 2 1

La actualización es como subir un escalón después de dar cada paso. No podés saber si llegaste al último piso hasta no haber subido todos los escalones.

2.4. El Ámbito (Scope) de la Variable Contador

Si declarás la variable contador dentro del for (en la inicialización), su ámbito (scope) se limita al bloque del ciclo. Esto significa que la variable “vive” solo dentro de las llaves del for y no puede usarse después.

for (int dia = 1; dia <= 5; dia++) {
    // acá 'dia' existe y se puede usar
}
// acá 'dia' ya no existe
// std::cout << dia; // ERROR: 'dia' was not declared in this scope

Si intentás usar la variable dia después de cerrar el for, el compilador te va a marcar un error. La variable ya no existe porque salió de su ámbito natural.

Si necesitás el valor del contador después del bucle, tenés que declarar la variable fuera del for:

int dia;
for (dia = 1; dia <= 5; dia++) {
    std::cout << "Procesando día " << dia << std::endl;
}
std::cout << "Valor final del contador: " << dia << std::endl;

Este comportamiento sigue el estándar C++. En MSVC, la opción /Zc:forScope (activada por defecto) fuerza esta conducta estándar.

3. El Flujo de Ejecución: Cómo Piensa la Máquina

Entender el orden exacto en que se ejecuta cada parte del for es fundamental. Acá no hay magia: hay una secuencia estricta que la CPU sigue al pie de la letra.

Tomemos este código:

for (int vuelta = 1; vuelta <= 3; vuelta++) {
    std::cout << "Vuelta número " << vuelta << std::endl;
}
std::cout << "Ciclo terminado." << std::endl;

La secuencia de ejecución es:

  1. Inicialización: int vuelta = 1; → se ejecuta una sola vez. vuelta vale 1.
  2. Condición: vuelta <= 3 → 1 <= 3 es true. Se ejecuta el cuerpo.
  3. Cuerpo: Imprime "Vuelta número 1".
  4. Actualización: vuelta++vuelta ahora vale 2.
  5. Condición: vuelta <= 3 → 2 <= 3 es true. Se ejecuta el cuerpo.
  6. Cuerpo: Imprime "Vuelta número 2".
  7. Actualización: vuelta++vuelta ahora vale 3.
  8. Condición: vuelta <= 3 → 3 <= 3 es true. Se ejecuta el cuerpo.
  9. Cuerpo: Imprime "Vuelta número 3".
  10. Actualización: vuelta++vuelta ahora vale 4.
  11. Condición: vuelta <= 3 → 4 <= 3 es false. El bucle termina.
  12. El programa continúa con la línea siguiente: imprime "Ciclo terminado.".

Corte de la Condicón: cuando la condición se evalúa como false, el control salta inmediatamente a la primera instrucción después del bloque for. No se ejecuta el cuerpo, no se ejecuta la actualización. El ciclo muere ahí.

Si la condición inicial ya es false, el cuerpo no se ejecuta ni una sola vez. Por ejemplo, for (int i = 5; i < 3; i++) da 0 vueltas. Esto no es un error: a veces necesitás exactamente ese comportamiento.

Pensá en el for como una montaña rusa: la inicialización es cuando te subís y te ajustan el cinturón (una vez). La condición es la verificación de seguridad antes de cada loop. La actualización es el impulso al final del recorrido. Y el quiebre es cuando la máquina se detiene porque el viaje terminó.

4. Patrones de Uso: Combinando for con lo que Ya Sabemos

El for es la estructura base para iterar. Pero su verdadero poder aparece cuando lo combinás con la lógica condicional (if/else) que ya dominás. Acá van los patrones fundamentales.

4.1. El Patrón Acumulador

El acumulador consiste en declarar una variable fuera del for e ir sumándole valores en cada iteración. Como la variable está fuera del ciclo, conserva su valor entre vueltas y podés leer el total después.

#include <iostream>

int main() {
    int suma = 0;
    int puntaje;

    for (int ronda = 1; ronda <= 3; ronda++) {
        std::cout << "Ingresá el puntaje de la ronda " << ronda << ": ";
        std::cin >> puntaje;
        suma += puntaje;  // acumulamos
    }

    std::cout << "Puntaje total: " << suma << std::endl;
    return 0;
}

Si declarás el acumulador dentro del for (ej. int suma = 0; adentro del bloque), en cada vuelta se reiniciaría a cero. Error clásico de principiantes: la variable se crea y destruye en cada iteración.

Podés pensarlo como una alcancía. Antes de empezar a jugar, la alcancía está vacía (suma = 0). En cada ronda, metés una moneda (suma += puntaje). Al final del día, abrís la alcancía y ves cuánto juntaste.

4.2. Filtrado dentro del Ciclo: un if dentro del for

Podés colocar una sentencia if dentro del cuerpo del for para ejecutar código condicionalmente en iteraciones específicas. Esto te permite filtrar datos o contar ocurrencias particulares.

#include <iostream>

int main() {
    int temperatura;
    int diasCalurosos = 0;

    for (int dia = 1; dia <= 10; dia++) {
        std::cout << "Ingresá la temperatura del día " << dia << ": ";
        std::cin >> temperatura;

        if (temperatura > 25) {
            std::cout << "  -> ¡Día caluroso!" << std::endl;
            diasCalurosos++;
        }
    }

    std::cout << "Días calurosos (>25°C): " << diasCalurosos << std::endl;
    return 0;
}

En este ejemplo, el for itera 10 veces (una por día). Dentro de cada vuelta, un if decide si la temperatura supera los 25°C. Si es así, imprime un mensaje y cuenta el día. Si no, simplemente avanza al siguiente.

Acá combinamos dos patrones: el acumulador (diasCalurosos) y el filtrado con if. Notá cómo el acumulador crece solo cuando se cumple la condición.

Cada vez que anidés un if dentro de un for, aumentá la indentación 2 o 4 espacios. Tu código se lee de arriba a abajo, no de costado. La indentación es la sonrisa del código: hace que la estructura sea visible de un vistazo.

Es como una cinta transportadora de paquetes (el for). En cada paquete, un inspector (el if) decide si va a la pila A o a la pila B. El for mueve todo, el if clasifica.

4.3. Ejemplo Integrador: Temperaturas y Metas

Hagamos un programa completo que resuelve el problema del inicio: registrar temperaturas, acumular el total, y contar los días que superaron una meta, todo con las herramientas que aprendiste.

#include <iostream>

int main() {
    int temperatura;
    int sumaTemperaturas = 0;
    int diasMetaSuperada = 0;
    const int META = 25;  // temperatura objetivo

    std::cout << "=== Registro de Temperaturas Semanal ===" << std::endl;

    for (int dia = 1; dia <= 5; dia++) {
        std::cout << "Día " << dia << " - Ingresá la temperatura: ";
        std::cin >> temperatura;

        sumaTemperaturas += temperatura;

        if (temperatura > META) {
            std::cout << "  >> ¡Meta superada!" << std::endl;
            diasMetaSuperada++;
        }
    }

    std::cout << "\n--- Resumen Semanal ---" << std::endl;
    std::cout << "Promedio de temperatura: " << (sumaTemperaturas / 5) << "°C" << std::endl;
    std::cout << "Días que superaron la meta: " << diasMetaSuperada << std::endl;

    return 0;
}

¿Te imaginás escribir esto sin un for? Tendrías 5 bloques de código casi idénticos, 5 variables distintas para las temperaturas, y cualquier cambio requeriría modificar los 5 bloques. El for no solo te ahorra líneas: te ahorra errores.