printf: salida formateada y correspondencia con tipos
printf imprime texto y valores en la salida estándar (normalmente la consola). Su potencia está en los especificadores de formato, que indican cómo convertir un valor a texto. La regla clave: el especificador debe corresponder con el tipo del argumento. Si no coincide, el resultado puede ser incorrecto o incluso provocar comportamiento indefinido.
Especificadores comunes y tipos esperados
| Especificador | Uso típico | Tipo esperado en printf | Ejemplo |
|---|---|---|---|
%d | Entero con signo | int | printf("%d", edad); |
%u | Entero sin signo | unsigned int | printf("%u", contador); |
%ld | Entero largo con signo | long int | printf("%ld", poblacion); |
%f | Real en punto fijo | double (sí, double) | printf("%f", precio); |
%c | Carácter | int (promoción de char) | printf("%c", inicial); |
%s | Cadena | char* (puntero a char) | printf("%s", nombre); |
Notas importantes:
- En
printf, unfloatse pasa comodoublepor promociones de argumentos variables; por eso%fespera undouble. %srequiere una cadena terminada en\0. Si pasas un puntero inválido o un arreglo sin terminación, se imprimirá basura o fallará.
Fallos típicos por especificadores incorrectos
- Imprimir un
longcon%d: puede truncar o mostrar un número erróneo. Debe ser%ld. - Imprimir un
unsignedcon%d: si el valor supera el rango deint, se verá negativo o incorrecto. Usa%u. - Imprimir un
char*con%co uncharcon%s:%cimprime un solo carácter;%sespera una dirección de memoria a una cadena. - Olvidar el argumento:
printf("%d %d", x);deja un%dsin valor y el programa leerá “lo que haya” en la pila.
Formato con precisión y ancho (muy práctico)
Además del tipo, puedes controlar presentación:
- Decimales:
%.2fimprime 2 decimales. - Ancho mínimo:
%8dreserva al menos 8 caracteres. - Relleno con ceros:
%08u(útil para IDs).
double nota = 7.5; unsigned int id = 42; printf("Nota: %.1f | ID: %08u\n", nota, id);scanf: entrada por consola con validación básica
scanf lee desde la entrada estándar. Para principiantes, lo más importante no es memorizar formatos, sino adoptar un patrón: leer, comprobar el retorno, y repetir si falla.
El valor de retorno: tu primera defensa
scanf devuelve cuántos elementos logró leer y asignar. Si esperas 1 dato y devuelve 1, todo bien. Si devuelve 0, la entrada no coincide con el formato. Si devuelve EOF, se terminó la entrada (por ejemplo, Ctrl+D/Ctrl+Z).
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
int x; if (scanf("%d", &x) != 1) { /* entrada inválida */ }Observa el &: para números y char se pasa la dirección. Para cadenas (arreglos char nombre[...];) se pasa el arreglo sin & porque ya “decay” a puntero.
Espacios, saltos de línea y por qué a veces “se salta” lecturas
En scanf, la mayoría de formatos (como %d, %u, %ld, %f, %s) ignoran espacios en blanco iniciales (espacios, tabulaciones, saltos de línea). Esto suele ayudar.
El caso especial es %c: no ignora espacios en blanco. Si antes leíste un número y quedó un salto de línea pendiente, %c podría leer ese \n.
Patrón común para leer un carácter “real” después de otras lecturas: anteponer un espacio en el formato, " %c", para que scanf consuma blancos previos.
char opcion; scanf(" %c", &opcion);Lectura de cadenas: limita el tamaño
%s lee una palabra (hasta el primer espacio). Para evitar desbordamientos, limita el número máximo de caracteres con un ancho: si el arreglo tiene tamaño 20, usa %19s para dejar espacio al \0.
char usuario[20]; if (scanf("%19s", usuario) != 1) { /* error */ }Si necesitas leer nombres con espacios (por ejemplo, “Ana María”), scanf con %s no sirve tal cual. Una alternativa simple (sin librerías avanzadas) es pedir “sin espacios” o usar un patrón con fgets (se muestra más abajo como alternativa).
Patrones simples de validación: repetir lectura si falla
La idea es encapsular una lectura en un bucle: mostrar un mensaje, intentar leer, y si falla, limpiar la entrada y volver a pedir.
Cómo “limpiar” la entrada cuando scanf falla
Cuando scanf falla (por ejemplo, el usuario escribe “abc” esperando un número), esos caracteres quedan en el búfer y el siguiente scanf fallará otra vez. Un método básico es consumir hasta el fin de línea:
int ch; while ((ch = getchar()) != '\n' && ch != EOF) { }Esto no es “avanzado”: getchar forma parte de <stdio.h> y ayuda a descartar lo que el usuario escribió en esa línea.
Guía paso a paso: leer un entero con validación
- Declara la variable.
- En un bucle, muestra el mensaje.
- Intenta leer con
scanf. - Si el retorno no es 1, informa el error y limpia la línea.
- Si es válido, sales del bucle.
#include <stdio.h> int main(void) { int edad; int ok = 0; while (!ok) { printf("Introduce tu edad (0 a 120): "); if (scanf("%d", &edad) == 1) { if (edad >= 0 && edad <= 120) { ok = 1; } else { printf("Fuera de rango.\n"); } } else { printf("Entrada inválida: escribe un número entero.\n"); } int ch; while ((ch = getchar()) != '\n' && ch != EOF) { } } printf("Edad registrada: %d\n", edad); return 0; }Ejemplos de programas pequeños (entrada + salida + cálculo)
1) Calculadora de IVA con decimales y formato
Lee un precio base, valida que sea no negativo, calcula IVA y total, e imprime con 2 decimales.
#include <stdio.h> int main(void) { double base; const double iva = 0.21; while (1) { printf("Precio base (>= 0): "); if (scanf("%lf", &base) == 1 && base >= 0.0) { int ch; while ((ch = getchar()) != '\n' && ch != EOF) { } break; } printf("Entrada inválida.\n"); int ch; while ((ch = getchar()) != '\n' && ch != EOF) { } } double impuesto = base * iva; double total = base + impuesto; printf("Base: %.2f\n", base); printf("IVA (21%%): %.2f\n", impuesto); printf("Total: %.2f\n", total); return 0; }Observa el formato %lf en scanf para leer un double. En printf se usa %f (y se imprime como double igualmente).
2) Menú simple con opción char (cuidando espacios)
Lee una opción de un menú y ejecuta una acción. Se usa " %c" para ignorar saltos de línea previos.
#include <stdio.h> int main(void) { char op; int a, b; printf("Menu:\n"); printf(" s) sumar\n"); printf(" r) restar\n"); printf("Elige una opción: "); if (scanf(" %c", &op) != 1) return 1; if (op != 's' && op != 'r') { printf("Opción no válida.\n"); return 0; } printf("Introduce dos enteros: "); if (scanf("%d %d", &a, &b) != 2) { printf("Entrada inválida.\n"); return 0; } if (op == 's') printf("%d + %d = %d\n", a, b, a + b); else printf("%d - %d = %d\n", a, b, a - b); return 0; }3) Registro de usuario (cadena limitada) y número de intentos
Lee un nombre de usuario sin espacios y un número de intentos, validando ambos.
#include <stdio.h> int main(void) { char usuario[16]; unsigned int intentos; while (1) { printf("Usuario (sin espacios, max 15): "); if (scanf("%15s", usuario) == 1) { int ch; while ((ch = getchar()) != '\n' && ch != EOF) { } break; } printf("No se pudo leer el usuario.\n"); int ch; while ((ch = getchar()) != '\n' && ch != EOF) { } } while (1) { printf("Número de intentos (0 a 10): "); if (scanf("%u", &intentos) == 1 && intentos <= 10) { int ch; while ((ch = getchar()) != '\n' && ch != EOF) { } break; } printf("Entrada inválida.\n"); int ch; while ((ch = getchar()) != '\n' && ch != EOF) { } } printf("Usuario: %s | Intentos: %u\n", usuario, intentos); return 0; }Alternativas sencillas cuando scanf se queda corto
fgets + sscanf: leer una línea completa y luego convertir
Un patrón muy usado para evitar problemas de búfer es: leer una línea con fgets y luego convertir con sscanf. Esto facilita reintentos porque siempre consumes la línea completa.
#include <stdio.h> int main(void) { char linea[64]; int n; while (1) { printf("Introduce un entero: "); if (!fgets(linea, sizeof linea, stdin)) return 1; if (sscanf(linea, "%d", &n) == 1) break; printf("Entrada inválida.\n"); } printf("Leído: %d\n", n); return 0; }Este enfoque sigue siendo básico: usa funciones estándar de <stdio.h> y mantiene la validación simple (comprobar retornos y repetir).