Por que o scanf () está causando loop infinito nesse código?

Eu tenho um pequeno programa C que apenas lê números de stdin, um em cada ciclo de loop. Se o usuário insere algum NaN, um erro deve ser impresso no console e o prompt de input deve retornar novamente. Na input de “0”, o loop deve terminar e o número de valores positivos / negativos deve ser impresso no console. Aqui está o programa:

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

Meu problema é que, ao entrar em algum não-número (como “a”), isso resulta em um loop infinito escrevendo “-> Err …” repetidamente. Eu acho que é um problema scanf () e eu sei que essa function pode ser substituída por uma mais segura, mas este exemplo é para iniciantes, sabendo apenas sobre printf / scanf, if-else e loops.

Já li as respostas para essa pergunta e folheei outras perguntas, mas nada realmente responde a esse problema específico.

scanf consome apenas a input que corresponde à string de formato, retornando o número de caracteres consumidos. Qualquer caractere que não corresponda à string de formato faz com que pare de varrer e deixa o caractere inválido ainda no buffer. Como outros disseram, você ainda precisa liberar o caractere inválido do buffer antes de prosseguir. Esta é uma correção bastante suja, mas removerá os caracteres ofensivos da saída.

 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 } 

edit: corrigiu o código para remover todos os caracteres não numéricos de uma só vez. Não imprimirá mais “Errs” para cada caracter não numérico.

Aqui está uma boa visão geral do scanf.

scanf() deixa o ” a ” ainda no buffer de input para a próxima vez. Você provavelmente deve usar getline() para ler uma linha, não importa o que seja e, em seguida, analisá-la com strtol() ou similar.

(Sim, getline() é específico do GNU, não POSIX. Então, o que? A questão é marcada “gcc” e “linux”. getline() também é a única opção sensata para ler uma linha de texto a menos que você queira fazer isso tudo à mão.)

Em algumas plataformas (especialmente Windows e Linux) você pode 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; } 

Eu acho que você só precisa liberar o buffer antes de continuar com o loop. Algo assim provavelmente faria o trabalho, embora eu não possa testar o que estou escrevendo aqui:

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

Em vez de usar scanf() e ter que lidar com o buffer com caracter inválido, use fgets() e 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); } /* ... */ 

Devido aos problemas com scanf apontados pelas outras respostas, você deve realmente considerar o uso de outra abordagem. Eu sempre achei scanf muito limitado para qualquer leitura de input e processamento. É uma idéia melhor apenas ler linhas inteiras com fgets e depois trabalhar nelas com funções como strtok e strtol (que BTW irá corretamente strtol inteiros e dizer exatamente onde os caracteres inválidos começam).

Eu tive problema semelhante. Eu resolvi usando apenas scanf.

Input "abc123" para ver como 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); } } } 

Oi, eu sei que este é um tópico antigo, mas acabei de terminar uma tarefa da escola, onde me deparei com o mesmo problema. Minha solução é que usei gets () para pegar o que scanf () deixou para trás.

Aqui está o código OP, ligeiramente reescrito; provavelmente não serve para ele, mas talvez ajude alguém a sair.

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

tente usar isso:

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

isso funcionou bem para mim … tente isso .. a instrução continue não é apropriada como o Err .. deve executar apenas uma vez. Então, tente quebrar o que eu testei … isso funcionou bem para você .. eu testei ….

Boa noite. Eu recentemente passei pelo mesmo problema e encontrei uma solução que pode ajudar muitos caras. Bem, na verdade a function “scanf” deixa um buffer na memory … e é por isso que o loop infinito é causado. Então você realmente tem que “armazenar” este buffer para outra variável SE seu scanf inicial contiver o valor “nulo”. Aqui está o que eu quero dizer:

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

Quando um não-número é inserido, ocorre um erro e o não-número ainda é mantido no buffer de input. Você deveria pular isto. Além disso, até mesmo essa combinação de símbolos, como por exemplo 1a , será lida inicialmente como número 1. Acho que você também deve pular essa input.

O programa pode parecer da seguinte maneira.

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

A saída do programa pode parecer

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

Eu tive o mesmo problema e encontrei uma solução um pouco hacky. Eu uso fgets() para ler a input e, em seguida, alimentar isso para sscanf() . Isso não é uma correção ruim para o problema do loop infinito, e com um loop for simples, digo ao C para pesquisar qualquer caractere numérico nenhum. O código abaixo não permitirá inputs 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; } 

Limpe o buffer de input antes de digitalizar:

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

Eu ia sugerir fflush(stdin) , mas aparentemente isso resulta em comportamento indefinido .

Em resposta ao seu comentário, se você quiser que o prompt apareça, precisará liberar o buffer de saída. Por padrão, isso só acontece quando você imprime uma nova linha. Gostar:

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