
La firma de main con argc y argv
Hasta ahora, la función main se ha utilizado sin parámetros. Sin embargo, el estándar de C define una firma alternativa que permite a un programa recibir datos externos cuando se ejecuta desde la terminal. Esta firma es:
int main(int argc, char *argv[])
El parámetro argc (argument count) es un entero que indica cuántos argumentos se han pasado al programa, incluyendo el propio nombre del ejecutable. El parámetro argv (argument vector) es un array de punteros a cadenas de caracteres, donde cada posición contiene uno de los argumentos como texto.
Siempre se cumple que
argc >= 1, ya queargv[0]contiene el nombre (o ruta) del programa ejecutable. Los argumentos proporcionados por el usuario comienzan enargv[1].
Un ejemplo mínimo que imprime todos los argumentos recibidos:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Numero de argumentos: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
Si se compila este programa como args y se ejecuta con:
./args hola mundo 42
La salida será:
Numero de argumentos: 4
argv[0] = ./args
argv[1] = hola
argv[2] = mundo
argv[3] = 42
Es importante tener presente que todos los argumentos son cadenas de caracteres, independientemente de si representan números u otro tipo de datos. El valor 42 en el ejemplo anterior se almacena como la cadena "42", no como un entero.
Equivalencia entre char *argv[] y char **argv
La declaración char *argv[] es equivalente a char **argv. Ambas formas indican que argv es un puntero a un puntero a char, es decir, un puntero al primer elemento de un array de cadenas. Usar una u otra es cuestión de preferencia, aunque char *argv[] resulta más expresiva al comunicar que se trata de un array.
// Ambas firmas son válidas y equivalentes
int main(int argc, char *argv[]) { /* ... */ }
int main(int argc, char **argv) { /* ... */ }
Recorrer y procesar argumentos
El patrón más habitual para trabajar con argumentos consiste en recorrer argv con un bucle for, empezando en el índice 1 para saltar el nombre del programa:
#include <stdio.h>
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
printf("Argumento %d: %s\n", i, argv[i]);
}
return 0;
}
En muchos programas, los argumentos tienen un significado posicional. Por ejemplo, un programa que copia ficheros podría esperar exactamente dos argumentos: el fichero de origen y el de destino. Para estos casos, se accede directamente a posiciones concretas de argv:
#include <stdio.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Uso: %s <origen> <destino>\n", argv[0]);
return 1;
}
printf("Copiar de %s a %s\n", argv[1], argv[2]);
return 0;
}
Utilizar
argv[0]en los mensajes de error es una buena práctica, ya que muestra al usuario el nombre real del ejecutable sin necesidad de escribirlo manualmente en el código.
Detectar opciones con prefijo
Los programas de consola suelen aceptar opciones precedidas por un guion, como -v o -o. Se puede detectar si un argumento es una opción comprobando su primer carácter:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
printf("Opcion: %s\n", argv[i]);
} else {
printf("Parametro: %s\n", argv[i]);
}
}
return 0;
}
Para comparar opciones específicas se utiliza strcmp:
if (strcmp(argv[i], "-v") == 0) {
printf("Modo verbose activado\n");
}
Conversión de argumentos a tipos numéricos
Dado que argv contiene exclusivamente cadenas de texto, cualquier argumento que represente un número debe convertirse explícitamente al tipo numérico correspondiente. La biblioteca estándar proporciona varias funciones para esta tarea.
atoi y atof
Las funciones atoi y atof, declaradas en <stdlib.h>, convierten una cadena a int y double respectivamente:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Uso: %s <entero> <decimal>\n", argv[0]);
return 1;
}
int entero = atoi(argv[1]);
double decimal = atof(argv[2]);
printf("Entero: %d, Decimal: %.2f\n", entero, decimal);
return 0;
}
Estas funciones son sencillas de usar, pero tienen una limitación importante: si la cadena no contiene un número válido, devuelven 0 sin indicar error. No es posible distinguir entre una conversión fallida y un valor legítimo de 0.
Las funciones
atoiyatofresultan adecuadas para prototipos rápidos, pero en código de producción se recomienda usarstrtolystrtod, que ofrecen detección de errores.
strtol y strtod
La función strtol (string to long) convierte una cadena a long int y permite detectar errores de conversión. Su prototipo es:
long int strtol(const char *str, char **endptr, int base);
El parámetro endptr recibe un puntero al primer carácter que no pudo convertirse. Si tras la llamada endptr apunta al inicio de la cadena, significa que no se extrajo ningún número. El parámetro base permite indicar la base numérica (10 para decimal, 16 para hexadecimal, etc.).
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Uso: %s <numero>\n", argv[0]);
return 1;
}
char *fin;
long valor = strtol(argv[1], &fin, 10);
if (*fin != '\0') {
printf("Error: '%s' no es un numero valido\n", argv[1]);
return 1;
}
printf("Valor convertido: %ld\n", valor);
return 0;
}
De forma análoga, strtod convierte una cadena a double con detección de errores:
char *fin;
double valor = strtod(argv[1], &fin);
if (*fin != '\0') {
printf("Error: argumento no numerico\n");
return 1;
}
La comprobación *fin != '\0' verifica que toda la cadena fue consumida durante la conversión. Si queda algún carácter sin procesar, la entrada no era un número puro.
Validación de argumentos
Un programa robusto debe validar los argumentos antes de utilizarlos. Las validaciones más frecuentes incluyen comprobar la cantidad de argumentos, verificar que los valores numéricos estén en un rango aceptable y mostrar mensajes de uso claros.
Validar la cantidad de argumentos
El primer paso consiste en verificar que el usuario ha proporcionado el número correcto de argumentos:
if (argc < 2) {
printf("Uso: %s <argumento_obligatorio> [opcionales...]\n", argv[0]);
return 1;
}
Devolver un código de retorno distinto de 0 indica al sistema operativo que el programa terminó con un error, lo cual es relevante cuando se integra en scripts o automatizaciones.
Validar rangos numéricos
Tras la conversión, se debe comprobar que el valor cae dentro de un rango razonable:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Uso: %s <edad>\n", argv[0]);
return 1;
}
char *fin;
errno = 0;
long edad = strtol(argv[1], &fin, 10);
// Comprobar errores de conversion
if (*fin != '\0' || errno == ERANGE) {
printf("Error: valor no valido\n");
return 1;
}
// Comprobar rango logico
if (edad < 0 || edad > 150) {
printf("Error: la edad debe estar entre 0 y 150\n");
return 1;
}
printf("Edad registrada: %ld\n", edad);
return 0;
}
La variable global
errno, definida en<errno.h>, se establece aERANGEcuandostrtoldetecta que el resultado excede el rango representable por unlong. Es buena práctica asignarerrno = 0antes de la llamada para evitar falsos positivos.
Ejemplos prácticos
Calculadora de linea de comandos
Este programa recibe una operación aritmética como tres argumentos: operando, operador y operando.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("Uso: %s <num1> <operador> <num2>\n", argv[0]);
printf("Operadores: + - x /\n");
return 1;
}
double a = atof(argv[1]);
double b = atof(argv[3]);
char *op = argv[2];
double resultado;
if (strcmp(op, "+") == 0) {
resultado = a + b;
} else if (strcmp(op, "-") == 0) {
resultado = a - b;
} else if (strcmp(op, "x") == 0) {
resultado = a * b;
} else if (strcmp(op, "/") == 0) {
if (b == 0.0) {
printf("Error: division por cero\n");
return 1;
}
resultado = a / b;
} else {
printf("Operador '%s' no reconocido\n", op);
return 1;
}
printf("%.2f %s %.2f = %.2f\n", a, op, b, resultado);
return 0;
}
Se utiliza x en lugar de * como operador de multiplicación porque el carácter * tiene un significado especial para la shell (expansión de comodines) y requeriría escapado.
Ejemplo de ejecución:
./calc 10.5 + 3.2
10.50 + 3.20 = 13.70
Procesador de ficheros con opciones
Un ejemplo más completo que acepta un nombre de fichero obligatorio y opciones para controlar el comportamiento:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
int verbose = 0;
char *fichero = NULL;
// Recorrer argumentos buscando opciones y parametros
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
verbose = 1;
} else if (fichero == NULL) {
fichero = argv[i];
} else {
printf("Error: argumento inesperado '%s'\n", argv[i]);
return 1;
}
}
if (fichero == NULL) {
printf("Uso: %s [-v] <fichero>\n", argv[0]);
return 1;
}
if (verbose) {
printf("Modo verbose activado\n");
printf("Procesando fichero: %s\n", fichero);
}
FILE *f = fopen(fichero, "r");
if (f == NULL) {
printf("Error: no se puede abrir '%s'\n", fichero);
return 1;
}
// Contar lineas del fichero
int lineas = 0;
char buffer[1024];
while (fgets(buffer, sizeof(buffer), f) != NULL) {
lineas++;
}
fclose(f);
printf("El fichero '%s' tiene %d lineas\n", fichero, lineas);
return 0;
}
Este programa ilustra un patrón habitual: separar las opciones (argumentos que empiezan por -) de los parámetros posicionales mediante un bucle que clasifica cada argumento. Las opciones activan flags (variables booleanas) y los parámetros se almacenan en variables dedicadas.
Generador de tablas de multiplicar
Un ejemplo que combina validación numérica con strtol y lógica de negocio sencilla:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Uso: %s <numero>\n", argv[0]);
return 1;
}
char *fin;
long n = strtol(argv[1], &fin, 10);
if (*fin != '\0' || n < 1 || n > 100) {
printf("Error: introduce un numero entre 1 y 100\n");
return 1;
}
printf("Tabla de multiplicar del %ld:\n", n);
for (int i = 1; i <= 10; i++) {
printf("%ld x %d = %ld\n", n, i, n * i);
}
return 0;
}
Ejecución:
./tabla 7
Tabla de multiplicar del 7:
7 x 1 = 7
7 x 2 = 14
7 x 3 = 21
...
7 x 10 = 70
Buenas prácticas con argumentos de linea de comandos
Al diseñar programas que reciben argumentos, conviene seguir una serie de pautas que mejoran la usabilidad y la robustez del software:
-
Mostrar siempre un mensaje de uso cuando los argumentos no son correctos, indicando la sintaxis esperada.
-
Utilizar
strtolystrtoden lugar deatoiyatofcuando se necesite detectar entradas no numéricas. -
Comprobar
errnotras llamar a funciones de conversión para detectar desbordamientos de rango. -
Devolver códigos de salida distintos de
0cuando el programa termina por un error, de forma que los scripts puedan reaccionar adecuadamente. -
Documentar las opciones aceptadas directamente en el mensaje de uso del programa, ya que este es el primer recurso al que acude el usuario.
-
Evitar acceder a posiciones de
argvsin antes verificar queargces suficientemente grande, ya que hacerlo produce comportamiento indefinido.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, C es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de C
Explora más contenido relacionado con C y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Comprender la firma de main con argc y argv. Acceder y recorrer los argumentos recibidos. Convertir argumentos de texto a tipos numéricos con atoi, atof y strtol. Validar la cantidad y formato de los argumentos.
Cursos que incluyen esta lección
Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje