scanf (parte 1)

Si printf sirve para imprimir por pantalla, scanf sirve para leer de la entrada estandar caracteres y volcarlos en variables. La particularidad de scanf está en que puede hacer conversiones automáticamente a otros tipos que no sea el char. En este primer vídeo dedicado a scanf, hablo de su funcionamiento más rudimentario.

En el mundo de la programación en C, la función scanf es una herramienta fundamental para leer datos formateados desde la entrada estándar y almacenarlos directamente en variables de distintos tipos. A diferencia de funciones como getchar o fgets, que solo leen caracteres o cadenas de caracteres, scanf nos permite interpretar esos caracteres y convertirlos automáticamente en enteros, flotantes o incluso caracteres individuales, facilitando así la interacción con el usuario.

La clave para usar scanf correctamente es entender que su primer parámetro es una cadena de formato que indica qué tipo de dato queremos leer. Por ejemplo, %d para enteros, %f para números flotantes o %c para caracteres. A continuación, debemos pasar punteros a las variables donde queremos almacenar los valores leídos. Esto es crucial porque scanf necesita modificar directamente el contenido de esas variables, y para ello requiere sus direcciones de memoria. Un error común es olvidar el operador ampersand (&), lo que provoca fallos en la ejecución.

Cuando usamos scanf para leer un entero, por ejemplo, la función interpreta los caracteres que el usuario introduce, los convierte a un número entero y lo almacena en la variable indicada. Además, scanf devuelve un entero que indica cuántos elementos ha leído correctamente, lo que nos permite detectar errores de entrada. Si el usuario introduce un carácter que no corresponde con el formato esperado, como una letra cuando se espera un número, scanf no lee nada y devuelve cero, dejando la variable sin modificar.

En cuanto a los números, scanf es capaz de interpretar tanto enteros positivos como negativos, y también números en notación científica cuando usamos el formato adecuado. Para los números flotantes, podemos usar %f, %g o %e, y la función los procesa correctamente, aunque hay que tener en cuenta que el redondeo puede afectar la precisión.

La lectura de caracteres y cadenas con scanf tiene sus particularidades. Para leer un solo carácter, usamos %c, pero para leer cadenas usamos %s. Sin embargo, %s solo lee hasta el primer espacio en blanco, tabulador o salto de línea, por lo que si intentamos leer un nombre compuesto como Luis Enrique, solo capturará Luis. Además, scanf no verifica si la cadena que estamos leyendo cabe en el espacio reservado en memoria, lo que puede provocar desbordamientos de buffer y vulnerabilidades de seguridad como el stack smashing.

Para evitar estos problemas, podemos limitar el número de caracteres que scanf lee usando un modificador en el formato, por ejemplo %9s para leer hasta 9 caracteres y dejar espacio para el carácter nulo de terminación. Pero si necesitamos leer cadenas con espacios, una opción más segura y sencilla es usar fgets, que permite leer toda una línea hasta un límite especificado.

Si insistimos en usar scanf para leer múltiples palabras o cadenas con espacios, podemos aprovechar un placeholder especial que solo existe en scanf: los corchetes [ ]. Dentro de estos corchetes podemos especificar un conjunto de caracteres permitidos o excluidos para la lectura. Por ejemplo, %[A-Za-z0-9] leerá una cadena compuesta solo por letras mayúsculas, minúsculas y números. También podemos usar el símbolo ^ para indicar qué caracteres no queremos que se lean, por ejemplo %[^\\n] para leer hasta el salto de línea.

Este mecanismo nos permite leer cadenas completas con espacios, siempre que definamos correctamente el conjunto de caracteres que queremos aceptar. Sin embargo, es importante recordar que scanf dejará de leer en cuanto encuentre un carácter que no esté en el conjunto especificado, y si el primer carácter no es válido, devolverá cero y no modificará la variable.

En definitiva, scanf es una función poderosa pero que requiere cuidado y comprensión para evitar errores y problemas de seguridad. Saber cómo usar sus formatos, pasar correctamente los punteros y manejar las limitaciones en la lectura de cadenas es esencial para escribir programas robustos en C. Más adelante podemos profundizar en técnicas avanzadas para manejar errores y formatos específicos, pero con estas bases ya podemos empezar a interactuar con el usuario de forma efectiva.

Un ejemplo básico para leer un entero y un flotante sería:

#include <stdio.h>

int main() {
    int entero;
    float flotante;
    int leidos;

    printf("Introduce un número entero y un número flotante separados por espacio: ");
    leidos = scanf("%d %f", &entero, &flotante);

    if (leidos == 2) {
        printf("Has introducido el entero %d y el flotante %f\n", entero, flotante);
    } else {
        printf("Error en la lectura de los datos.\n");
    }

    return 0;
}

Y para leer una cadena con espacios usando el placeholder de corchetes:

#include <stdio.h>

int main() {
    char nombre[50];

    printf("Introduce tu nombre completo: ");
    scanf("%49[^\n]", nombre);  // Lee hasta el salto de línea, máximo 49 caracteres

    printf("Tu nombre es: %s\n", nombre);

    return 0;
}

Con estas técnicas podemos manejar la entrada de datos de forma más segura y flexible, evitando problemas comunes que suelen surgir con scanf.

Lista de reproducción
  1. 1
    Instalar CodeBlocks
    15 minutos
  2. 2
    Funciones y hola mundo
    17 minutos
  3. 3
    Variables y tipos de datos
    17 minutos
  4. 4
    Condicionales y operadores lógicos
    16 minutos
  5. 5
    Bucles
    11 minutos
  6. 6
    Punteros
    12 minutos
  7. 7
    Arrays
    14 minutos
  8. 8
    Estructuras
    12 minutos
  9. 9
    Otras construcciones de C
    9 minutos
  10. 10
    Memoria dinámica
    8 minutos
  11. 11
    El preprocesador (parte 1)
    10 minutos
  12. 12
    El preprocesador (parte 2)
    9 minutos
  13. 13
    Archivos de cabecera y múltiples .c (parte 1)
    8 minutos
  14. 14
    Archivos de cabecera y múltiples .c (parte 2)
    9 minutos
  15. 15
    C desde la línea de comandos (parte 1)
    8 minutos
  16. 16
    C desde la línea de comandos (parte 2)
    9 minutos
  17. 17
    Break y continue
    10 minutos
  18. 18
    Goto
    13 minutos
  19. 19
    Manipulación de bits
    15 minutos
  20. 20
    Máscaras de bit
    19 minutos
  21. 21
    Archivos (1): fopen y fclose
    13 minutos
  22. 22
    Archivos (2): leer con fgetc
    9 minutos
  23. 23
    Archivos (3): fseek y ftell
    11 minutos
  24. 24
    Archivos (4): leer con fgets
    9 minutos
  25. 25
    Archivos (5): fputc y fputs
    7 minutos
  26. 26
    Archivos (6): volcar en archivos con fwrite
    10 minutos
  27. 27
    Archivos (7): fread, fwrite y los arrays
    10 minutos
  28. 28
    Archivos (8): entrada estándar y salida estándar
    9 minutos
  29. 29
    Archivos (9): buffers
    14 minutos
  30. 30
    Archivos (y 10): otras funciones útiles con archivos
    5 minutos
  31. 31
    printf (1)
    18 minutos
  32. 32
    printf (parte 2)
    12 minutos
  33. 33
    scanf (parte 1)
    17 minutos
  34. 34
    scanf (parte 2)
    17 minutos
  35. 35
    fprintf, sprintf y snprintf
    8 minutos
  36. 36
    Tipos de datos opacos
    13 minutos
  37. 37
    Bibliotecas estáticas
    13 minutos
  38. 38
    Bibliotecas dinámicas
    15 minutos
  39. 39
    Más flags: i mayúscula (include), wall, werror, pedantic...
    12 minutos
  40. 40
    pkg-config
    12 minutos
  41. 41
    Make
    17 minutos
  42. 42
    GDB
    21 minutos
  43. 43
    Variables globales
    6 minutos
  44. 44
    extern
    9 minutos
  45. 45
    Funciones variádicas
    12 minutos
  46. 46
    El optimizador de GCC y la opción -O
    12 minutos
  47. 47
    Volatile
    6 minutos