Entrada y salida estándar en C con validación básica

Capítulo 4

Tiempo estimado de lectura: 7 minutos

+ Ejercicio

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

EspecificadorUso típicoTipo esperado en printfEjemplo
%dEntero con signointprintf("%d", edad);
%uEntero sin signounsigned intprintf("%u", contador);
%ldEntero largo con signolong intprintf("%ld", poblacion);
%fReal en punto fijodouble (sí, double)printf("%f", precio);
%cCarácterint (promoción de char)printf("%c", inicial);
%sCadenachar* (puntero a char)printf("%s", nombre);

Notas importantes:

  • En printf, un float se pasa como double por promociones de argumentos variables; por eso %f espera un double.
  • %s requiere 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 long con %d: puede truncar o mostrar un número erróneo. Debe ser %ld.
  • Imprimir un unsigned con %d: si el valor supera el rango de int, se verá negativo o incorrecto. Usa %u.
  • Imprimir un char* con %c o un char con %s: %c imprime un solo carácter; %s espera una dirección de memoria a una cadena.
  • Olvidar el argumento: printf("%d %d", x); deja un %d sin 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: %.2f imprime 2 decimales.
  • Ancho mínimo: %8d reserva 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).

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

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

  1. Declara la variable.
  2. En un bucle, muestra el mensaje.
  3. Intenta leer con scanf.
  4. Si el retorno no es 1, informa el error y limpia la línea.
  5. 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).

Ahora responde el ejercicio sobre el contenido:

Al leer un carácter con scanf después de haber leído un número, ¿qué patrón evita que se capture el salto de línea pendiente en lugar del carácter deseado?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

En scanf, %c no ignora espacios en blanco y puede leer un \n pendiente. Anteponer un espacio en el formato (" %c") hace que se consuman blancos previos antes de leer el carácter.

Siguiente capítulo

Control de flujo condicional en C

Arrow Right Icon
Portada de libro electrónico gratuitaFundamentos de programación en C desde cero: variables, control de flujo y funciones
36%

Fundamentos de programación en C desde cero: variables, control de flujo y funciones

Nuevo curso

11 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.