Fundamentos

Capítulo 1: Hello World

Tu primer programa en C++ y una inmersión profunda en qué sucede realmente desde que escribes el código hasta que ves el resultado en pantalla.

Objetivos

El código

// Main.cc
#include <iostream>

int main() {
    std::cout << "Hello world!" << std::endl;
    return 0;
}

Explicación línea por línea

#include <iostream>

Esta es una directiva del preprocesador. El preprocesador es la primera fase del proceso de compilación, ejecutándose antes de que el compilador propiamente dicho vea tu código. Su función principal es procesar directivas que comienzan con #.

Esta directiva #include le indica al preprocesador que copie textualmente todo el contenido del archivo especificado (iostream en este caso) en la ubicación donde aparece la directiva. Es literalmente como hacer "copiar y pegar" del archivo completo.

Los corchetes angulares <> le dicen al compilador que busque en los directorios estándar del sistema (donde se instala la biblioteca estándar de C++). Si usaras comillas dobles (#include "iostream"), buscaría primero en tu directorio de proyecto, luego en los directorios del sistema.

int main()

La función main() es el punto de entrada de todo programa en C++. Cuando ejecutas tu programa desde la terminal o haciendo doble clic, el sistema operativo realiza varias operaciones: carga el ejecutable en memoria, configura el entorno de ejecución, y finalmente transfiere el control a la función main(). Es una convención heredada del lenguaje C (creado en 1972 por Dennis Ritchie en Bell Labs) - el sistema operativo busca específicamente un símbolo llamado main en el binario compilado. El int antes de main especifica el tipo de retorno: un entero que representa el código de salida del programa (0 = éxito, cualquier otro valor = error). Este código es crucial para scripts y herramientas de automatización: ./mi_programa && echo "Éxito" || echo "Falló"

Hay otras formas de escribir main Intermedio

Sí, hay dos formas estándar:

// Forma 1: Sin argumentos
int main() { return 0; }

// Forma 2: Con argumentos de línea de comandos
int main(int argc, char* argv[]) { return 0; }

argc = número de argumentos
argv = array de argumentos (como strings)

Usarás la forma 2 cuando necesites pasar parámetros al programa desde la terminal, por ejemplo: ./programa archivo.js --verbose

std::cout << "Hello world!"

Esta línea imprime texto en la consola. Vamos a desglosarla:

Puedo evitar escribir std cada vez Intermedio

Sí, puedes usar using namespace std; al inicio de tu archivo:

#include <iostream>
using namespace std;  // Ahora puedes omitir std::

int main() {
    cout << "Hello!" << endl;  // Sin std::
    return 0;
}
Pero cuidado

Usar using namespace std; en archivos header (.h) es considerado mala práctica porque contamina el namespace de todos los archivos que incluyan ese header. Puede causar conflictos de nombres difíciles de depurar.

Regla: Está bien en archivos .cc pequeños para aprender, pero en código profesional es mejor ser explícito con std::.

std::endl

std::endl hace dos cosas:

  1. Inserta un salto de línea (\n)
  2. Flush del buffer de salida (fuerza la escritura inmediata)
Consejo de rendimiento

Si solo necesitas un salto de línea, usa \n en lugar de endl. El flush tiene un costo de rendimiento. En loops que imprimen miles de líneas, la diferencia puede ser significativa.

return 0;

Retorna 0 al sistema operativo, indicando que el programa terminó exitosamente sin errores.

El proceso de compilación

Cuando ejecutas g++ Main.cc -o programa, suceden 4 etapas:

Etapas de Compilación

flowchart TB
    A["1. PREPROCESADOR<br/>Main.cc"] --> B["Procesa #include, #define<br/>Genera archivo .i"]
    B --> C["2. COMPILADOR<br/>Main.i"]
    C --> D["Traduce C++ a assembly<br/>Genera archivo .s"]
    D --> E["3. ENSAMBLADOR<br/>Main.s"]
    E --> F["Traduce assembly a código máquina<br/>Genera archivo .o"]
    F --> G["4. LINKER<br/>Main.o + libstdc++.a"]
    G --> H["Combina .o con bibliotecas<br/>Genera ejecutable final"]
    H --> I["📦 programa (ejecutable)"]

    style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
    style C fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
    style E fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
    style G fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
    style I fill:#c8e6c9,stroke:#388e3c,stroke-width:3px

Dónde vive tu programa en memoria

Cuando ejecutas ./programa, el sistema operativo:

  1. Lee el archivo ejecutable del disco
  2. Crea un nuevo proceso
  3. Asigna memoria dividida en secciones
  4. Salta a la función main()

Estructura de Memoria de un Proceso

flowchart TB
    subgraph "Direcciones Altas"
    A["Stack<br/>(crece hacia abajo)<br/>Variables locales, parámetros<br/>Memoria automática"]
    end
    A --> C["Espacio libre"]
    C --> E["Heap<br/>(crece hacia arriba)<br/>Memoria dinámica (new/delete)<br/>Tú controlas la vida útil"]
    E --> F["BSS<br/>Variables globales no inicializadas"]
    F --> G["Data<br/>Variables globales inicializadas"]
    G --> H["Text (Code)<br/>READ-ONLY<br/>main(), funciones, constantes"]
    subgraph "Direcciones Bajas"
    H
    end

    style A fill:#fff3e0,stroke:#f57c00,stroke-width:2px
    style E fill:#fce4ec,stroke:#c2185b,stroke-width:2px
    style F fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style G fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style H fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

Checklist

Preguntas para reflexionar

¿Qué pasa si cambio return 0; por return 1;?
Respuesta:

El valor de retorno de main() es el código de salida del programa.

  • return 0; → Éxito (convención estándar)
  • return 1; (o cualquier número ≠ 0) → Error

Los scripts de shell y sistemas operativos usan este código para detectar si tu programa falló. Por ejemplo:

./mi_programa
if [ $? -ne 0 ]; then
  echo 'El programa falló'
fi
¿Por qué std::cout usa << y no paréntesis como cout('texto')?
Respuesta:

<< es el operador de inserción (operator overloading). En C++ puedes redefinir operadores para que funcionen con tus propios tipos.

cout es un objeto de tipo ostream, y el operador << está sobrecargado para insertar datos en el stream.

Ventajas:

  • Puedes encadenar: cout << 'Hola' << 123 << endl;
  • Es extensible: puedes definir << para tus propios tipos
¿Si borro el #include <iostream>, en qué etapa de compilación fallaría?
Respuesta:

Fallaría en la etapa de compilación (no en el preprocesador ni en el enlazador).

Proceso completo:

  1. Preprocesador: Procesa #include (si no está, no se expande nada)
  2. Compilador: ❌ ERROR AQUÍ - No encuentra la declaración de std::cout
  3. Enlazador: No llega a esta etapa

Error típico: error: 'cout' is not a member of 'std'

¿Cuál es más rápido: endl o \n?
Respuesta:

\n es más rápido que endl.

Diferencia:

  • \n → Solo inserta una nueva línea
  • endl → Inserta nueva línea Y hace flush del buffer (fuerza escritura inmediata)

El flush es costoso. Usa \n en loops:

// ❌ Lento (1000 flushes)
for (int i = 0; i < 1000; i++)
  cout << i << endl;

// ✅ Rápido (1 flush al final)
for (int i = 0; i < 1000; i++)
  cout << i << '\n';
Próximo Capítulo

En el Capítulo 2 vamos a profundizar en los tipos de datos y cómo se representan en memoria. Aprenderás sobre bits, bytes, signed/unsigned, y por qué el tamaño importa en C++.

Siguiente capítulo: Operadores →