¿Por qué scanf() está causando un bucle infinito en este código?

5 minutos de lectura

Tengo un pequeño programa en C que solo lee números de stdin, uno en cada ciclo de ciclo. Si el usuario ingresa algo de NaN, se debe imprimir un error en la consola y el indicador de entrada debe regresar nuevamente. Al ingresar “0”, el ciclo debe terminar y el número de valores positivos/negativos dados debe imprimirse en la consola. Aquí está el programa:

#include <stdio.h>

int main()
{
    int number, p = 0, n = 0;

    while (1) {
        printf("-> ");
        if (scanf("%d", &number) == 0) {
            printf("Err...\n");
            continue;
        }
        
        if (number > 0) p++;
        else if (number < 0) n++;
        else break; /* 0 given */
    }

    printf("Read %d positive and %d negative numbers\n", p, n);
    return 0;
}

Mi problema es que al ingresar algo que no es un número (como “a”), esto da como resultado un bucle infinito que escribe “-> Err …” una y otra vez. Supongo que es un problema de scanf() y sé que esta función podría reemplazarse por una más segura, pero este ejemplo es para principiantes, que saben solo sobre printf/scanf, if-else y bucles.

Ya he leído las respuestas a la pregunta.scanf() se salta todos los demás while bucle en C y hojeó otras preguntas, pero nada realmente responde a este problema específico.

  • Muchas preguntas de SO estrechamente relacionadas, que incluyen: stackoverflow.com/questions/1669821

    –Jonathan Leffler

    11 de noviembre de 2009 a las 22:34

  • En respuesta a todas las respuestas y sugerencias: Agregar while (getchar() != ‘\n’); antes de “continuar” dentro de la declaración if funciona muy bien para mí y (con suerte) resuelve todos/la mayoría de los problemas. Además, es razonablemente explicable para los principiantes :).

    usuario208785

    13 de noviembre de 2009 a las 20:51

  • Véase también Uso fflush(stdin).

    –Jonathan Leffler

    15 de septiembre de 2016 a las 5:32

Debido a los problemas con scanf señalado por las otras respuestas, realmente debería considerar usar otro enfoque. siempre he encontrado scanf demasiado limitado para cualquier lectura y procesamiento de entrada serio. Es una mejor idea leer líneas completas con fgets y luego trabajar en ellos con funciones como strtok y strtol (que, por cierto, analizará correctamente los enteros y le dirá exactamente dónde comienzan los caracteres no válidos).

  • fgets() lee algún búfer y si no contiene formato desde el principio, toda la línea se desecha. Esto podría no ser aceptable (pero podría ser deseable, depende de los requisitos).

    – Roman Nikitchenko

    11 de noviembre de 2009 a las 16:55

avatar de usuario
Lucas

Creo que solo tienes que vaciar el búfer antes de continuar con el ciclo. Algo así probablemente haría el trabajo, aunque no puedo probar lo que estoy escribiendo desde aquí:

int c;
while((c = getchar()) != '\n' && c != EOF);

  • “la optimización prematura es la raíz de todos los males”… pero cambia las constantes: '\n' es mucho más probable que aparezca que EOF 🙂

    – pmg

    11 de noviembre de 2009 a las 15:54

  • esperarías EOF está 100% garantizado para aparecer; de lo contrario, tienes un teclado muy rápido o una CPU muy lenta

    – Andomar

    11 de noviembre de 2009 a las 16:05

  • La complicación anterior en el while la condición de la declaración es innecesaria.

    – ilgaar

    2 de diciembre de 2018 a las 5:38

  • @ilgaar ¿Qué quieres decir? Me parece bien.

    – Filippo Costa

    24 de noviembre de 2019 a las 0:09

scanf() deja el “a” todavía en el búfer de entrada para la próxima vez. Probablemente deberías usar getline() para leer una línea sin importar qué y luego analizarla con strtol() o similar en su lugar.

(Sí, getline() es específico de GNU, no POSIX. ¿Así que lo que? La pregunta está etiquetada como “gcc” y “linux”. getline() también es la única opción sensata para leer una línea de texto a menos que desee hacerlo todo a mano).

  • “la optimización prematura es la raíz de todos los males”… pero cambia las constantes: '\n' es mucho más probable que aparezca que EOF 🙂

    – pmg

    11 de noviembre de 2009 a las 15:54

  • esperarías EOF está 100% garantizado para aparecer; de lo contrario, tienes un teclado muy rápido o una CPU muy lenta

    – Andomar

    11 de noviembre de 2009 a las 16:05

  • La complicación anterior en el while la condición de la declaración es innecesaria.

    – ilgaar

    2 de diciembre de 2018 a las 5:38

  • @ilgaar ¿Qué quieres decir? Me parece bien.

    – Filippo Costa

    24 de noviembre de 2019 a las 0:09

Hola, sé que este es un hilo antiguo, pero acabo de terminar una tarea escolar donde me encontré con este mismo problema. Mi solución es que usé gets() para recoger lo que scanf() dejó atrás.

Aquí está el código OP ligeramente reescrito; probablemente no le sirva de nada, pero tal vez ayude a alguien más.

#include <stdio.h>

    int main()
    {
        int number, p = 0, n = 0;
        char unwantedCharacters[40];  //created array to catch unwanted input
        unwantedCharacters[0] = 0;    //initialzed first byte of array to zero

        while (1)
        {
            printf("-> ");
            scanf("%d", &number);
            gets(unwantedCharacters);        //collect what scanf() wouldn't from the input stream
            if (unwantedCharacters[0] == 0)  //if unwantedCharacters array is empty (the user's input is valid)
            {
                if (number > 0) p++;
                else if (number < 0) n++;
                else break; /* 0 given */
            }
            else
                printf("Err...\n");
        }
        printf("Read %d positive and %d negative numbers\n", p, n);
        return 0;
    }

  • gets es terriblemente inseguro y nunca debe usarse (se ha eliminado del estándar C por ese motivo).

    – melpomene

    2 de enero de 2019 a las 17:57

  • Estoy de acuerdo en que es un amigo peligroso y solo lo usé aquí para una pequeña aplicación (de ahí la matriz subjetiva de 40 caracteres). si el problema en cuestión fuera más objetivo en sus requisitos, entonces ya ^^.

    – codificador de medianoche

    4 de enero de 2019 a las 10:45

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad