printf y scanf son las funciones estándar para la entrada y salida de datos en C. Imagina que printf es un molde que le dice a la computadora cómo “dibujar” un valor en la pantalla, mientras que scanf es un filtro que le dice a la computadora cómo interpretar los bytes que el usuario teclea para guardarlos en la memoria. Para que esto funcione, utilizamos especificadores de formato (como %d para enteros o %f para decimales), que actúan como marcadores de posición dentro de una cadena de texto.
Estas funciones se usan siempre que necesitemos interactuar con el usuario o mostrar información relevante del programa. Sin embargo, requieren precisión absoluta: si le dices a scanf que guarde un número decimal en una variable entera, o si intentas meter un nombre de 50 caracteres en un espacio de 10, provocarás un comportamiento indefinido (undefined behavior). Esto significa que el programa puede cerrarse de golpe, corromper otras variables o dar resultados erróneos que son extremadamente difíciles de rastrear.
#include <stdio.h>
int main(void) {
int edad;
float precio;
double precision;
char nombre[20];
char categoria[10];
// printf usa especificadores para imprimir valores
// %d: entero, %f: flotante, %lf: double, %s: cadena, %zu: size_t
printf("--- Registro de Producto ---\n");
printf("Introduce edad (entero), precio (float), precision (double) y nombre (texto):\n");
/*
* scanf lee de la entrada estándar.
* IMPORTANTE: Debemos pasar la dirección de las variables con '&'.
* Usamos %9s para limitar la lectura de 'nombre' a 9 caracteres
* (dejando espacio para el nulo '\0'), evitando desbordamientos.
*/
int leidos = scanf("%d %f %lf %9s", &edad, &precio, &precision, nombre);
if (leidos < 4) {
fprintf(stderr, "Error: Datos de entrada insuficientes o tipo incorrecto.\n");
return 1;
}
// Mostramos datos usando diferentes especificadores
printf("\n--- Resumen de Datos ---\n");
printf("Nombre: %s\n", nombre);
printf("Edad: %d años\n", edad);
// En printf, %f sirve tanto para float como para double debido a la
// promoción de tipos (el float se convierte en double al pasarse a la función).
printf("Precio: %.2f (con 2 decimales)\n", precio);
printf("Prec: %.10f (notación decimal)\n", precision);
printf("Prec: %e (notación científica)\n", precision);
// %zu es el especificador correcto para el tipo size_t (resultado de sizeof)
size_t tamano_nombre = sizeof(nombre);
printf("Espacio ocupado por el buffer de nombre: %zu bytes\n", tamano_nombre);
return 0;
}
Análisis del código
En el bloque de scanf, verás que para edad, precio y precision hemos usado el operador & (dirección de). Esto es vital: scanf no necesita el valor de la variable, necesita saber en qué dirección de memoria debe escribir el dato que el usuario escriba. Si olvidas el &, el programa intentará escribir en una dirección aleatoria y colapsará.
Para el nombre, hemos aplicado una medida de seguridad crucial: %9s. Como el array nombre tiene capacidad para 20 caracteres, limitar la lectura a 9 asegura que, incluso si el usuario escribe un texto infinito, no sobrepasaremos los límites de la memoria reservada para esa variable.
En cuanto a los decimales, nota la diferencia entre printf y scanf. En printf, usar %f funciona correctamente para float y double. Sin embargo, en scanf, la distinción es obligatoria: para un float usamos %f, pero para un double debemos usar %lf. Esto ocurre porque scanf necesita saber exactamente cuántos bytes debe leer de la entrada para llenar la variable correctamente.
Finalmente, para imprimir el tamaño de un tipo usando sizeof, el estándar C99 [disponible desde C99] introduce %zu, que es el especificador específico para el tipo size_t, asegurando la portabilidad entre diferentes arquitecturas de CPU.
El error frecuente
Uno de los errores más peligrosos es usar %s sin especificar un ancho máximo en scanf.
char buffer[5];
// PELIGRO: Si el usuario escribe "hola_mundo",
// se escribirá más allá de los 5 bytes de 'buffer'.
scanf("%s", buffer);
Esto es un desbordamiento de búfer (buffer overflow). El programa escribirá en la memoria de otras variables o en la pila de llamadas, permitiendo que un atacante tome el control del flujo del programa o simplemente causando un Segmentation Fault. Herramientas como AddressSanitizer (compilando con -fsanitize=address) detectarán este error inmediatamente al ejecutar el programa, señalando la zona de la memoria corrompida.
N° 80