Menu

Especificadores de formato en C: la referencia completa de printf y scanf

Todos los especificadores de formato de C en un solo lugar: qué significan %d, %s, %c, %f, %p y el resto, los modificadores de longitud l, ll, h y z, ancho, precisión y banderas, y cuáles difieren entre printf y scanf.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un especificador de formato le dice a printf cómo convertir un valor en texto, y a scanf cómo convertir texto en un valor. Es la única parte de C donde el compilador no puede ayudarte por defecto —los tipos de los argumentos son invisibles para la función— así que esta página es la tabla con la que contrastar.

La forma general de una especificación es:

%[banderas][ancho][.precision][longitud]conversion

Solo el % y la letra de conversión son obligatorios. %-8.2lf usa las cinco partes.

La tabla de conversiones

EspecificadorSignificadoArgumento de printfArgumento de scanf
%dentero decimal con signointint *
%ientero con signoint (igual que %d)int * (también acepta 0x, octal con 0)
%udecimal sin signounsigned intunsigned int *
%fpunto flotante decimaldoublefloat *
%Fcomo %f, con INF/NAN en mayúsculasdouble-
%e / %Enotación científicadoublefloat *
%g / %Gel más corto entre %f y %edoublefloat *
%a / %Apunto flotante hexadecimaldoublefloat *
%cun solo carácterint (char promocionado)char *
%scadenachar * (terminada en '\0')char * (búfer)
%pdirección de un punterovoid *void **
%x / %Xhexadecimal sin signounsigned intunsigned int *
%ooctal sin signounsigned intunsigned int *
%nguarda los caracteres escritos hasta ahíint *int *
%%un % literalningunocoincide con un % literal
%[...]conjunto de caracteres (solo scanf)-char *

Dos entradas merecen una advertencia. %n escribe en memoria en lugar de imprimir, y es el mecanismo detrás de la vulnerabilidad de cadena de formato: nunca dejes que texto del usuario llegue a una cadena de formato. Y %p exige un void *; pasar un int * directamente es indefinido aunque todos los compiladores reales impriman la dirección igual.

Modificadores de longitud

El modificador de longitud va entre la precisión y la letra de conversión, y dice qué tan grande es realmente el argumento.

Las conversiones con signo (d i) y las sin signo (u x o) toman tipos distintos: %lu quiere un unsigned long, no un long, y equivocarse ahí es comportamiento indefinido, no un desliz cosmético.

ModificadorCon d i (printf / scanf)Con u x o (printf / scanf)
hhint (desde signed char) / signed char *unsigned int (desde unsigned char) / unsigned char *
hint (desde short) / short *unsigned int (desde unsigned short) / unsigned short *
(ninguno)int / int *unsigned int / unsigned int *
llong / long *unsigned long / unsigned long *
lllong long / long long *unsigned long long / unsigned long long *
ztipo de tamaño con signo / puntero a élsize_t / size_t *
jintmax_t / intmax_t *uintmax_t / uintmax_t *
tptrdiff_t / ptrdiff_t *contraparte sin signo de ptrdiff_t

Y para las familias de punto flotante y de caracteres:

ModificadorConTipo en printfTipo en scanf
(ninguno)f e g adoublefloat *
lf e g adouble (aceptado, sin efecto)double *
Lf e g along doublelong double *
lc swint_t / wchar_t *wchar_t *

Las combinaciones que de verdad escribes:

%zu es el que hay que memorizar. sizeof, strlen y cualquier tamaño de la biblioteca estándar devuelven size_t, que ocupa 8 bytes en un sistema de 64 bits mientras que int ocupa 4. printf("%d", strlen(s)) es un error real que parece funcionar en algunas plataformas: usa %zu.

Los modificadores hh y h existen sobre todo para scanf, donde importa el tamaño del destino. En printf un short se promociona a int antes de la llamada, así que %d lo imprimiría bien de todos modos; %hd solo documenta la intención.

Tipos de ancho fijo

Para los tipos de <stdint.h> como int32_t, los especificadores portables son macros de <inttypes.h>:

PRId32 se expande a las letras correctas para tu plataforma, y los literales de cadena adyacentes se concatenan, que es por lo que el formato queda partido en tres piezas. Es feo; la alternativa —convertir a long long y usar %lld— suele ser más legible y siempre es correcta.

Banderas

Las banderas van inmediatamente después del % y cambian la presentación. Se aplican solo a printf.

BanderaEfectoEjemploSalida
-rellena después del valor en vez de antes%-6d| con 4242 |
+imprime siempre el signo en valores con signo%+d con 42+42
espacioimprime un espacio donde iría un +% d con 42 42
0rellena con ceros en lugar de espacios%06d con 42000042
#forma alternativa: 0x para %x, 0 para %o, conserva el punto en %g%#x con 2550xff

Las banderas se combinan: %-+8.2f es ancho mínimo 8, dos decimales, siempre con signo, rellenado después del número. - gana sobre 0 si escribes ambas.

Ancho y precisión

El ancho es un tamaño mínimo de campo: la salida se rellena hasta alcanzarlo, y nunca se trunca para caber en él. La precisión (después de un punto) significa algo distinto para cada familia:

ConversiónLa precisión significa
%f %edígitos después del punto decimal (6 por defecto)
%gdígitos significativos totales (6 por defecto)
%smáximo de caracteres a imprimir
%d %i %u %x %omínimo de dígitos, rellenado con ceros

Un * en lugar de cualquiera de los dos números lo toma de un argumento int, leído antes del valor en sí. Así se construyen tablas cuyos anchos de columna se calculan en tiempo de ejecución.

En scanf el ancho tiene un trabajo distinto y mucho más importante: limita cuánta entrada consume una conversión, que es lo único que se interpone entre %s y un desbordamiento de búfer.

Dónde difieren printf y scanf

Se parecen y no coinciden en cuatro puntos. Cada uno de ellos es una fuente viva de errores:

Situaciónprintfscanf
Valor double%f (float promocionado a double)%lf: %f significa float *
Anchotamaño mínimo de campo, rellenamáximo de entrada consumida, trunca
Argumentosvalorespunteros: &x, o el nombre de un array
Un espacio en el formatoimprime un espaciosalta cualquier racha de espacios en blanco

Prueba con 2.5 hola. El formato que lo lee y el que lo imprime usan letras distintas para el mismo double, y el 15 de %15s limita la entrada a 15 caracteres más el terminador, exactamente el tamaño de word.

El conjunto de caracteres de scanf

%[...] lee un conjunto de caracteres en vez de una palabra delimitada por espacios. Es la forma de leer una línea completa con scanf, aunque fgets sigue siendo la mejor herramienta:

[^\n] significa "cualquier carácter excepto un salto de línea". %[0-9] leería solo dígitos, %[abc] solo esas tres letras. A diferencia de %s, un conjunto de caracteres no salta los espacios iniciales, así que un salto de línea suelto en el búfer hace que no coincida con nada.

Equivocarse es comportamiento indefinido

printf no puede ver el tipo real de tu argumento. Lee la cantidad de bytes que implica el especificador, interpretados como el especificador diga. Así que un desajuste no es un fallo de formato: es leer memoria como lo que no es.

printf("%d\n", 3.5);        /* imprime basura: lee los bits de un double como int */
printf("%s\n", 42);         /* trata 42 como una direccion: revienta */
printf("%d\n", strlen(s));  /* size_t leido como int: mal en 64 bits */
printf("%f\n", 3);          /* int leido como double: basura */

La defensa cuesta una bandera. Compila con -Wall y GCC contrasta cada cadena de formato literal con sus argumentos:

gcc -Wall -Wextra program.c -o program

Reporta cada una de las cuatro líneas de arriba en tiempo de compilación. Es el aviso de mayor valor de todo C, y está activado por defecto en el editor de arriba.

Para el lado práctico de usarlos —tablas alineadas, imprimir decimales de forma legible, leer entrada sin el clásico error del salto de línea— mira printf y scanf.

Preguntas frecuentes

¿Qué significa %d en C?

%d es el especificador de conversión para un entero decimal con signo, es decir un int. En printf imprime el argumento como dígitos en base 10; en scanf lee dígitos en base 10 hacia un int *. %i significa lo mismo en printf, pero en scanf %i además acepta hexadecimal con 0x y octal con cero inicial.

¿Cuál es la diferencia entre %f y %lf en C?

En printf no hay diferencia práctica: los argumentos float se promocionan a double, así que %f imprime ambos, y C99 acepta %lf como sinónimo. En scanf la diferencia es crítica: %f escribe en un float * (4 bytes) y %lf en un double * (8 bytes). Usar %f para un double corrompe memoria.

¿Cómo imprimo un size_t o un long long en C?

%zu para size_t (lo que devuelven sizeof y strlen) y %lld / %llu para long long / unsigned long long. Usar %d para estos es comportamiento indefinido: da la casualidad de que funciona en sistemas de 32 bits y falla en los de 64, que es por lo que el error sobrevive tanto tiempo.

¿Qué pasa si usas el especificador de formato equivocado en C?

Es comportamiento indefinido, no un error de redondeo. printf lee el argumento como lo que el especificador afirme, así que printf("%d", 3.5) imprime basura y printf("%s", 42) sigue el número 42 como si fuera una dirección y normalmente revienta. Compila con -Wall, que hace que GCC contraste las cadenas de formato con los argumentos.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR