scanf () deixa o novo caractere de linha no buffer

87

Tenho o seguinte programa:

int main(int argc, char *argv[])
{
  int a, b;
  char c1, c2;
  printf("Enter something: ");
  scanf("%d",&a); // line 1
  printf("Enter other something: ");
  scanf("%d", &b); // line 2

  printf("Enter a char: ");
  scanf("%c",&c1); // line 3
  printf("Enter another char: ");
  scanf("%c", &c2); // line 4

  printf("Done"); // line 5

  system("PAUSE");

  return 0;
}

Como li no livro C, o autor diz que scanf()deixou um caractere de nova linha no buffer, portanto, o programa não para na linha 4 para o usuário inserir os dados, em vez disso, armazena o caractere de nova linha em c2 e vai para linha 5.

Isso está certo?

No entanto, isso só acontece com chartipos de dados? Porque eu não vi esse problema com intos tipos de dados nas linhas 1, 2, 3. Está certo?

ipkiss
fonte
Às vezes, é sugerido que fflush(stdin)pode ser usado antes da chamada para scanf()para um único caractere. Leia Usandofflush(stdin) para uma discussão sobre os prós, contras e alternativas a esse método (que funciona, mais ou menos, no Windows, e não funciona na maioria dos outros lugares).
Jonathan Leffler
Você poderia nos dizer a qual livro você está se referindo?
surya kiran

Respostas:

78

A scanf()função ignora os espaços em branco iniciais automaticamente antes de tentar analisar conversões que não sejam caracteres. Os formatos de caracteres (principalmente %c; também conjuntos de varredura %[…]- e %n) são a exceção; eles não pulam os espaços em branco.

Use " %c"com um espaço em branco à esquerda para pular o espaço em branco opcional. Não use um espaço em branco à direita em uma scanf()string de formato.

Observe que isso ainda não consome nenhum espaço em branco à esquerda no fluxo de entrada, nem mesmo até o final de uma linha, então tome cuidado com isso se também estiver usando getchar()ou fgets()no mesmo fluxo de entrada. Estamos apenas fazendo o scanf pular os espaços em branco antes das conversões, como faz para %doutras conversões sem caracteres.


Observe que as "diretivas" sem espaço em branco (para usar a terminologia POSIX scanf ) além de conversões, como o texto literal em scanf("order = %d", &order);também não pula os espaços em branco. O literal orderdeve corresponder ao próximo caractere a ser lido.

Então você provavelmente vai querer " order = %d"lá se quiser pular uma nova linha da linha anterior, mas ainda exigir uma correspondência literal em uma string fixa, como esta questão .

Jeremiah Willcock
fonte
7
%c, %n, %[]São os 3 expectativas específicas que não consomem os espaços em branco.
chux - Reintegrar Monica em
@chux O mesmo acontece em outros casos, o scanf limpa todos os espaços em branco antes no buffer ou os ignora para a entrada, mas eles ainda estão lá?
Suraj Jain de
@SurajJain Sim,
chux - Reintegrar Monica em
3
Consulte Espaço em branco à direita na scanf()string de formato e scanf()perguntando duas vezes pela entrada, enquanto espero que ele peça apenas uma vez para uma discussão sobre espaços em branco à direita em strings de formato. Eles são uma má ideia - surpreendentemente ruim se você espera interação humana e ruim para a interação do programa.
Jonathan Leffler,
31

Use scanf(" %c", &c2);. Isto irá resolver o seu problema.

Shweta
fonte
7

Outra opção (que peguei aqui ) é ler e descartar a nova linha usando a opção de supressão de atribuição . Para fazer isso, apenas colocamos um formato para ler um caractere com um asterisco entre %e c:

scanf("%d%*c",&a); // line 1
scanf("%c%*c",&c1); // line 3

scanf irá então ler o próximo caractere (ou seja, a nova linha), mas não o atribuirá a nenhum ponteiro.

No final, entretanto, eu concordaria com a última opção do FAQ :

Ou, dependendo de seus requisitos, você também pode esquecer scanf () / getchar (), usar fgets () para obter uma linha de texto do usuário e analisá-la você mesmo.

Brandizzi
fonte
2
O problema com essa técnica é que se o usuário digitar aentão espaço e nova linha, a conversão de caracteres suprimida lê o espaço e ainda deixa a nova linha. Se o usuário digitar supercalifragilisticexpialidociousquando você espera a, você terá muitos caracteres extras com os quais lidar. Você nunca pode dizer se uma conversão suprimida à direita foi bem-sucedida - elas não são contadas no retorno de scanf().
Jonathan Leffler
3

Use getchar()antes de ligar em segundo lugar scanf().

scanf("%c", &c1);
getchar();  // <== remove newline
scanf("%c", &c2);
Jiwon
fonte
1
Isso funciona desde que o usuário não digite mais nada - espaços em branco à direita, por exemplo. Mas não é tão bom quanto um loop que varre para a próxima nova linha: int c; while ((c = getchar()) != EOF && c != '\n') ;(escrito em três linhas quando não está em um comentário). Muitas vezes é suficiente; não é infalível (e você deve se lembrar que os tolos são muito espertos para quebrar coisas).
Jonathan Leffler