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

Tengo un pequeño progtwig 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 mensaje de entrada debe regresar nuevamente. En la entrada de “0”, el ciclo debe finalizar y la cantidad de valores positivos / negativos dados debe imprimirse en la consola. Aquí está el progtwig:

#include  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 un número no (como “a”), esto da como resultado un ciclo de escritura infinito “-> Err …” una y otra vez. Supongo que es un problema de escaneo (scanf) y sé que esta función podría reemplazarse por una más segura, pero este ejemplo es para principiantes, ya que saben de printf / scanf, if-else y loops.

Ya leí las respuestas a esta pregunta y hojeé otras preguntas, pero nada realmente responde a este problema específico.

scanf solo consume la entrada que coincide con la cadena de formato, devolviendo la cantidad de caracteres consumidos. Cualquier carácter que no coincida con la cadena de formato hace que deje de escanear y deja el carácter no válido aún en el búfer. Como dijeron otros, aún necesita eliminar el carácter inválido del búfer antes de continuar. Esta es una solución bastante sucia, pero eliminará los caracteres ofensivos de la salida.

 char c = '0'; if (scanf("%d", &number) == 0) { printf("Err. . .\n"); do { c = getchar(); } while (!isdigit(c)); ungetc(c, stdin); //consume non-numeric chars from buffer } 

editar: corrigió el código para eliminar todos los caracteres no numéricos de una sola vez. Ya no imprimirá múltiples “errores” para cada carácter no numérico.

Aquí hay una muy buena descripción de scanf.

scanf() deja la ” 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. ¿Y qué? 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 quieras hacerlo todo a mano.)

En algunas plataformas (especialmente Windows y Linux) puede usar fflush(stdin); :

 #include  int main(void) { int number, p = 0, n = 0; while (1) { printf("-> "); if (scanf("%d", &number) == 0) { fflush(stdin); printf("Err...\n"); continue; } fflush(stdin); 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; } 

Creo que solo tienes que lavar el búfer antes de continuar con el bucle. 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); 

En lugar de usar scanf() y tener que lidiar con el buffer que tiene un carácter no válido, use fgets() y sscanf() .

 /* ... */ printf("0 to quit -> "); fflush(stdout); while (fgets(buf, sizeof buf, stdin)) { if (sscanf(buf, "%d", &number) != 1) { fprintf(stderr, "Err...\n"); } else { work(number); } printf("0 to quit -> "); fflush(stdout); } /* ... */ 

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

Tuve un problema similar. Lo resolví usando solo scanf.

Input "abc123" para ver cómo funciona.

 #include  int n, num_ok; char c; main() { while (1) { printf("Input Number: "); num_ok = scanf("%d", &n); if (num_ok != 1) { scanf("%c", &c); printf("That wasn't a number: %c\n", c); } else { printf("The number is: %d\n", n); } } } 

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

Aquí está el código OP ligeramente reescrito; probablemente no sea útil para él, pero quizás ayude a alguien más allá.

 #include  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; } 

Intenta usar esto:

 if (scanf("%d", &number) == 0) { printf("Err...\n"); break; } 

esto funcionó bien para mí … prueba esto … la statement de continuar no es apropiada ya que el Err .. solo debe ejecutar una vez. así que prueba el descanso que probé … esto funcionó bien para ti … probé …

Buenas tardes. Recientemente he tenido el mismo problema y encontré una solución que podría ayudar a muchos hombres. Bueno, en realidad la función “scanf” deja un búfer en la memoria … y es por eso que el bucle infinito es causado. Entonces, realmente tiene que “almacenar” este buffer en otra variable SI su scanf inicial contiene el valor “nulo”. Esto es lo que quiero decir:

 #include  int n; char c[5]; main() { while (1) { printf("Input Number: "); if (scanf("%d", &n)==0) { //if you type char scanf gets null value scanf("%s", &c); //the abovementioned char stored in 'c' printf("That wasn't a number: %s\n", c); } else printf("The number is: %d\n", n); } } 

Cuando se ingresa un no número, se produce un error y el no número se mantiene en el búfer de entrada. Deberías omitirlo. También incluso esta combinación de símbolos como por ejemplo 1a se leerá al principio como número 1, creo que también debería omitir dicha entrada.

El progtwig puede verse de la siguiente manera.

 #include  #include  int main(void) { int p = 0, n = 0; while (1) { char c; int number; int success; printf("-> "); success = scanf("%d%c", &number, &c); if ( success != EOF ) { success = success == 2 && isspace( ( unsigned char )c ); } if ( ( success == EOF ) || ( success && number == 0 ) ) break; if ( !success ) { scanf("%*[^ \t\n]"); clearerr(stdin); } else if ( number > 0 ) { ++p; } else if ( number < n ) { ++n; } } printf( "\nRead %d positive and %d negative numbers\n", p, n ); return 0; } 

El resultado del progtwig podría verse como

 -> 1 -> -1 -> 2 -> -2 -> 0a -> -0a -> a0 -> -a0 -> 3 -> -3 -> 0 Read 3 positive and 3 negative numbers 

Tuve el mismo problema y encontré una solución algo hacky. Yo uso fgets() para leer la entrada y luego alimentar a sscanf() . Esta no es una mala solución para el problema de bucle infinito, y con un bucle for simple, le digo a C que busque cualquier carácter no numérico. El código siguiente no permitirá entradas como 123abc .

 #include  #include  #include  int main(int argc, const char * argv[]) { char line[10]; int loop, arrayLength, number, nan; arrayLength = sizeof(line) / sizeof(char); do { nan = 0; printf("Please enter a number:\n"); fgets(line, arrayLength, stdin); for(loop = 0; loop < arrayLength; loop++) { // search for any none numeric charcter inisde the line array if(line[loop] == '\n') { // stop the search if there is a carrage return break; } if((line[0] == '-' || line[0] == '+') && loop == 0) { // Exculude the sign charcters infront of numbers so the program can accept both negative and positive numbers continue; } if(!isdigit(line[loop])) { // if there is a none numeric character then add one to nan and break the loop nan++; break; } } } while(nan || strlen(line) == 1); // check if there is any NaN or the user has just hit enter sscanf(line, "%d", &number); printf("You enterd number %d\n", number); return 0; } 

Vacíe el búfer de entrada antes de escanear:

 while(getchar() != EOF) continue; if (scanf("%d", &number) == 0) { ... 

Iba a sugerir fflush(stdin) , pero aparentemente eso da como resultado un comportamiento indefinido .

En respuesta a su comentario, si desea que se muestre el aviso, debe vaciar el búfer de salida. Por defecto, eso solo ocurre cuando imprime una nueva línea. Me gusta:

 while (1) { printf("-> "); fflush(stdout); while(getchar() != EOF) continue; if (scanf("%d", &number) == 0) { ...