Cuando ejecutas un programa, el sistema operativo no salta directamente a la primera línea de tu código. Primero, el runtime de C (un conjunto de código que se ejecuta antes que el tuyo) prepara el terreno: configura la pila (stack), limpia la memoria y prepara los argumentos. Este proceso llama a una función invisible llamada _start, la cual, finalmente, te entrega el control a ti mediante la función main.
La función main es el punto de entrada oficial de cualquier programa ejecutable en C. Es una función especial que el cargador del sistema operativo busca para saber por dónde empezar a ejecutar instrucciones. El estándar define principalmente dos formas para declararla: int main(void), si el programa no necesita recibir datos externos, o int main(int argc, char *argv[]), si el programa debe interactuar con la terminal. Aunque podrías ver la segunda forma escrita como char **argv, para el compilador es exactamente lo mismo: un array de punteros a caracteres.
Esta estructura funciona así para permitir la comunicación entre el usuario (a través de la línea de comandos) y tu lógica de programación. El parámetro argc (argument count) te dice cuántos elementos hay en el array argv. Es vital recordar que argc siempre incluye el nombre del programa, por lo que argv[0] siempre contiene la ruta o el nombre con el que llamaste al ejecutable. Si intentas acceder a un índice que no existe, como argv[argc], el estándar garantiza que encontrarás un puntero NULL, una señal de que has llegado al final de la lista de argumentos.
Debes usar main siempre como el punto de partida de tu lógica y utilizar su valor de retorno para informar al sistema operativo si todo salió bien o si ocurrió un error. Si devuelves un valor distinto de cero, el sistema (o el script que llamó al programa) sabrá que algo falló. Si no devuelves nada, en versiones modernas de C [C99], el compilador insertará automáticamente un return 0 al final de la función, pero es una buena práctica ser explícito. Si te equivocas en la firma de la función o si el programa termina con un código de retorno inesperado, podrías causar errores en scripts de automatización o procesos que dependen de la salida de tu programa para continuar.
#include <stdio.h>
#include <stdlib.h>
/*
La firma estándar permite recibir argumentos de la terminal.
argc: número de argumentos (incluyendo el nombre del programa).
argv: array de strings que contienen los argumentos.
*/
int main(int argc, char *argv[]) {
// El primer argumento (argv[0]) siempre es el nombre del programa.
printf("Ejecutando: %s\n", argv[0]);
// Verificamos si el usuario pasó exactamente un argumento extra.
// Si argc no es 2, significa que no se pasó el parámetro esperado.
if (argc != 2) {
fprintf(stderr, "Error: Se requiere exactamente un nombre como argumento.\n");
fprintf(stderr, "Uso: %s <nombre>\n", argv[0]);
// EXIT_FAILURE es una macro de <stdlib.h> que garantiza un
// valor de error estándar (normalmente 1).
return EXIT_FAILURE;
}
// Accedemos al primer argumento proporcionado por el usuario.
printf("Hola, %s!\n", argv[1]);
// EXIT_SUCCESS indica al sistema que el programa terminó correctamente.
return EXIT_SUCCESS;
}
Desglose del código
En el ejemplo anterior, hemos utilizado la firma int main(int argc, char *argv[]). Fíjate en la validación if (argc != 2). Esto es crucial: si ejecutas el programa sin argumentos, argc será 1 (solo el nombre del programa). Si ejecutas ./ejemplo hola, argc será 2. Si el usuario escribe ./ejemplo hola mundo, argc será 3.
Al usar fprintf(stderr, ...) en lugar de printf, estamos enviando el mensaje de error al flujo de salida de errores estándar (standard error), lo cual es la forma correcta de informar fallos sin mezclar los errores con la salida normal del programa. Finalmente, el uso de EXIT_SUCCESS y EXIT_FAILURE de <stdlib.h> asegura que el programa devuelva los códigos de estado que el sistema operativo espera para distinguir entre un éxito total y un error de ejecución.
El error frecuente
Un error clásico para quienes empiezan es asumir que argc cuenta solo los parámetros que el usuario escribe. Si escribes un código que intenta acceder a argv[argc] esperando que sea el último argumento, estarás accediendo a un puntero NULL.
// ERROR: Intentar usar el argumento "fantasma" al final
for (int i = 0; i <= argc; i++) {
printf("Argumento %d: %s\n", i, argv[i]); // Crash si i == argc
}
Si intentas imprimir argv[argc], el programa intentará leer una dirección de memoria 0x0 (NULL), lo que provocará un error de segmentación (segmentation fault) y cerrará tu programa inmediatamente. El bucle siempre debe ir de 0 hasta argc - 1. Herramientas como Valgrind detectarán este acceso inválido a la memoria de inmediato.
N° 9