El texto llega de los usuarios, de los archivos y de la línea de comandos como caracteres. "42" son tres bytes —'4', '2', '\0'— y nada en él es el número 42 hasta que lo conviertes. C ofrece dos generaciones de funciones de conversión: las viejas y cortas que no pueden reportar errores, y las más largas que sí. Esta página muestra ambas y explica por qué los programas reales usan el segundo grupo.
La familia antigua: atoi, atol, atof
Se leen como sus nombres: de ASCII a int, a long, a double. Un argumento, un resultado.
Los espacios iniciales se saltan, se lee un signo opcional, se consumen los dígitos y todo lo que viene después se ignora. Eso es cómodo hasta que la entrada está mal:
Los tres imprimen 0. No hay código de retorno, ni bandera, ni forma de preguntar "¿eso funcionó?", y un valor que desborde un int es comportamiento indefinido en lugar de un error reportado. Para un literal escrito a mano en un programa de juguete eso es tolerable. Para cualquier cosa que pueda suministrar una persona o un archivo, es un generador silencioso de errores.
La herramienta correcta: strtol
strtol (string to long) es la misma conversión con dos canales para reportar qué pasó.
long strtol(const char *text, char **endptr, int base);
endptr queda apuntando al primer carácter que la función no consumió. base es la base numérica: usa 10, o 0 para dejar que el prefijo del propio literal decida (0x para hexadecimal, un 0 inicial para octal).
Leer endptr responde las preguntas que atoi no puede:
end == inputsignifica que no se convirtió nada: el texto no empezaba con un número.*end != '\0'significa que había contenido al final. Si eso es un error o no lo decides tú: está bien al analizar"42 manzanas"y es un problema cuando la línea entera debía ser un número.
El desbordamiento se reporta aparte mediante errno. El modismo completo y correcto vale la pena escribirlo una vez y reutilizarlo:
Tres detalles hacen esto correcto. El errno = 0 antes de la llamada importa porque la biblioteca solo pone errno; nunca lo limpia, así que un valor rancio de una llamada anterior se leería como el fallo de esta. strtol devuelve long, que puede ser más ancho que int, así que la comprobación de rango contra INT_MIN/INT_MAX es aparte de ERANGE. Y devolver el éxito como estado mientras el valor se entrega por un puntero es como las funciones de C reportan "o un valor o un fallo": el valor de retorno no puede hacer los dos trabajos.
strtoul es la contraparte sin signo, y strtoll/strtoull manejan long long.
Otras bases
La base 16 lee hexadecimal; la base 0 inspecciona el prefijo y decide por sí misma.
La base 0 es una trampa en un caso concreto: un decimal rellenado con ceros como "012" se lee como octal y sale como 10. Si tu entrada es decimal, di 10 explícitamente.
Punto flotante: strtod
strtod tiene la misma forma para double, y atof tiene el mismo defecto que atoi.
strtod también acepta notación científica ("1.5e3"), decimales hexadecimales y las palabras "inf" y "nan". La configuración regional afecta al separador decimal, lo que sorprende a quienes analizan archivos escritos en otro lugar; en la configuración regional "C" por defecto el separador siempre es un punto.
De números a texto: snprintf
En el otro sentido, snprintf es la respuesta estándar. Escribe como mucho el tamaño que le des, siempre termina la cadena y devuelve la longitud que el resultado completo habría necesitado.
El valor de retorno es la comprobación de truncamiento. Si es mayor o igual que el tamaño del búfer, el texto no cupo:
Dos cosas a las que no recurrir: itoa no forma parte del C estándar, así que el código que la use no compilará en todas partes; y sprintf es snprintf sin el límite de tamaño, lo que significa que escribirá más allá del final de tu búfer sin quejarse.
Caracteres sueltos y el '0'
Los caracteres de dígito '0' a '9' tienen garantizado ser consecutivos, así que una simple resta convierte entre un carácter de dígito y su valor.
Solo los dígitos se comportan así: no se exige que las letras sean consecutivas, así que c - 'a' no es portable para aritmética con el alfabeto. Protege la resta con isdigit antes de confiar en ella:
La conversión a unsigned char es obligatoria por la misma razón que en cadenas: las funciones de <ctype.h> están indefinidas para argumentos negativos, y un char a secas puede tener signo.
Qué función usar
- Texto de un usuario, de un archivo o de
argv→strtol/strtodcon la comprobación completa. - Un literal que escribiste tú y controlas →
atoies aceptable, aunque poco notable. - Un número dentro de un búfer →
snprintf, y comprueba el valor de retorno. - Un solo carácter de dígito → resta
'0', después deisdigit. - Nunca →
gets,sprintf,itoa.
Preguntas frecuentes
¿Cómo se convierte una cadena a int en C?
strtol(text, &end, 10) es la respuesta correcta: devuelve el valor, deja end en el primer carácter que no consumió y reporta el desbordamiento mediante errno. atoi(text) es más corto pero devuelve 0 tanto para "0" como para "hola", sin forma de distinguir ambos casos.
¿Qué tiene de malo atoi en C?
No puede reportar fallos. atoi("abc") devuelve 0, indistinguible de un cero real, y el desbordamiento es comportamiento indefinido en lugar de un error reportado. Usa strtol siempre que el texto venga de un usuario, un archivo o un argumento de la línea de comandos.
¿Cómo convierto un int a una cadena en C?
snprintf(buf, sizeof buf, "%d", n). Es estándar, está limitado por el tamaño del búfer y devuelve la cantidad de caracteres que el resultado completo necesitaba, así que un valor de retorno igual o mayor que sizeof buf te dice que el texto se truncó. itoa no es C estándar.
¿Cómo convierto el carácter '7' en el número 7 en C?
Resta '0': int digit = c - '0';. Los caracteres de dígito son consecutivos en todo conjunto de caracteres de C, así que la resta da de 0 a 9. Protégelo antes con isdigit((unsigned char)c), ya que la aritmética no significa nada para cualquier otra cosa.