Buffer: o que é, como limpar e as funções fflush e __fpurge

No artigo passado foi pedido o seguinte programa:
Fazer um programa em C que peça dois caracteres ao usuário e os exiba.

Porém, há um problema ao se fazer isso, que e o que vai ser explicado nesse artigo de nossa apostila de C.

O problema de usar scanf, getchar, getc e fgetc para receber caracteres em C


"Ora, é só declarar duas variáveis char e usar a scanf duas vezes, uma para cada variável", é que você deve ter pensado para criar o tal programa.

Vamos fazer isso então, e ver o resultado:

#include <stdio.h>

int main()
{
    char letra1, letra2;

    printf("Insira um caractere: ");
    scanf("%c",&letra1);

    printf("Insira outro caractere: ");
    scanf("%c",&letra2);

    printf("Você digitou: '%c' e '%c'", letra1, letra2);
}


Eu digitei 'C' (a melhor letra do alfabeto), dei enter, e antes de digitar a próxima letra o progarma terminou, exibindo a seguinte mensagem:
"Você digitou: 'C' e '
'"

Nossa! Estranho, não? Será que hackeamos a linguagem C e descobrimos uma falha?
Não ;)

Note que digitei 'C' e enter...mas enter também é uma tecla, e é representada por '\n', lembra?
Ou seja, o C entendeu que nossa segunda letra era o enter!

A solução para isso é bem simples.
Na função scanf, dê um espaço entre a aspa " e o símbolo %c.
Nosso código fica assim:

#include <stdio.h>

int main()
{
    char letra1, letra2;

    printf("Insira um caractere: ");
    scanf("%c",&letra1);

    printf("Insira outro caractere: ");
    scanf(" %c",&letra2);

    printf("Você digitou: '%c' e '%c'", letra1, letra2);
}




Pronto! Agora funciona perfeitamente!
Pois esse simples espaço é um comando para o C desconsiderar o enter, tab ou espaço em branco.
Sim, precisa nem dar enter. Tente aí digitar uma letra, dar espaço (ou tab) e a outra.

Ok, mas só é possível fazer isso na scanf().
E na getchar(), getc() e fgetc()?

Limpando o buffer em C: fflush e __fpurge

Ainda no primeiro exemplo desse artigo ( o que dá problema ), digitamos a letra 'C', que é armazenada na variável 'letra1' e em seguida apertamos enter.
Esse caractere (enter), ficará armazenado no buffer do teclado (um memória temporária).

Em seguida, noss programa em C pede para que algo seja armazenado na variável 'letra2'.
Porém, antes do C receber um novo dado do usuário, ele checa se não tem mais alguma coisa armazenada no teclado (ele sempre faz isso...fez antes, para pegar a letra 'C'). E lá tem um caractere sim, o enter.

Então o programa pega esse caractere e o coloca na variável letra2, e é por isso que aparece uma quebra de linha em nosso programa.

Portanto, uma alternativa, caso não queria usar o espaço entre " e o %c na scanf, é limpar o buffer após cada scanf(), getchar(), getc() ou fgetc().

Para limpar o buffer em Windows, use: fflush(stdin)
Para limpar o buffer em Linux, use: __fpurge(stdin)

Veja como fica nosso programa original, funcionando do jeito que queríamos:


#include <stdio.h>

int main()
{
    char letra1, letra2;

    printf("Insira um caractere: ");
    scanf("%c", &letra1);

    fflush(stdin);
    __fpurge(stdin);

    printf("Insira outro caractere: ");
    scanf("%c", &letra2);

    printf("Você digitou: '%c' e '%c'", letra1, letra2);
}



Fica ao seu dispor escolher como vai ser.
E se habitue com essas coisas, em C há várias maneiras de se fazer várias coisas. Muitas vezes, porém, a solução que usamos não é muito segura ou portável, mas é a mais simples.

Limpar o buffer, por exemplo, nem sempre é algo desejável, e para programação mais profissional e segura não é recomendado que se use fflush por exemplo.

Mas para quem está  começando, não há problema algum ficar limpando o buffer após cada scanf, e o  scanf (embora seja arriscado e não indicado em alguns casos) é o mais usado.

Por isso, usaremos bastante o scanf ao longo de nosso curso de C.

15 comentários:

Vinicius disse...

Usar
getchar();
getchar();
}
no final dos programas, faz limpeza de buffer?

Isaac Santos disse...

Não Vinicius, você entendeu errado!

Pra entender melhor, faça o teste usando as 3 funções uma após outra. Ao fazer isso, ao tentar ler o caractere na segunda função o programa vai simplesmente colocar o enter (\n) e já pula pra terceira.

A limpeza de buffer serve pra evitar este tipo de problema.
Logo após a leitura, usa o fflush (no windows) ou __fpurge (no linux) que resolve isso.

obs.: se for usuário linux, o fpurge é antecedido por 2 underline e não apenas 1.

T+

Tamy Caroline Baron Dau disse...

Oii.. Estou fazendo um programa muito simples para descobrir o numero que o computador "pensou" usando a função rand().. mas ele sempre "pensa" no numero 41.. neste caso o fflush() nao deu certo.. Como limpo o buffet neste caso??

Anônimo disse...

e o comando setbuff(stdin,NULL);
mais recomendado ?

Tiago disse...

Olá!! Também pode resolver o problema simplesmente adicionando um espaço antes do %c. O código ficaria assim:

#include <stdio.h>

int main()
{
char letra1, letra2;

printf("Insira um caractere: ");
scanf(" %c",&letra1);

printf("Insira outro caractere: ");
scanf(" %c",&letra2);

printf("Você digitou: '%c' e '%c'", letra1, letra2);
}

Abraços.

Kim de Sousa disse...

No capítulo passado não se pediu pra usar a função scanf() e sim a função getc(stdin) então não tem como dar o espaço:
scanf(" %c",&letra2);

Com a função getc(stdin) eu achei outra solução:

int main(void)
{
char
letra1,
letra2;

printf("Insira um caractere: ");
letra1 = getc(stdin);
getchar(); //Esse getchar() sem atribuição lê o [Enter](que seria o \n) que antes era atribuido ao letra2
printf("Insira outro caractere: ");
letra2 = getc(stdin);
printf("Voce digitou: %c%c", letra1, letra2);

return 0;
}

Anônimo disse...

Nas minhas aulas meu professor disse para usar esse fflush(stdin), mas eu nunca entendi o porquê! E agora, lendo essa explicação, tudo ficou claro e agora até saber explicar eu sei. Obrigado :)

Joao Carlos Agostini disse...

Olá
Gostaria mais que se ensinasse a forma correta de proceder com esses comandos e não o atalho, mesmo simples. Pois a boca entorta com o uso do cachimbo.
obrigado.

Anônimo disse...

Qual é a forma mais adequada de resolver o problema do buffer?

Ghost Blogger disse...

#include

int main()
{char letra1, letra2;
printf("Insira um caractere: " );
scanf("%c%c",&letra1,&letra2);
printf("Você digitou: '%c' e '%c' ", letra1, letra2);}

Ghost Blogger disse...

Esse é melhor!
#include

int main()

{char letra1, letra2;
printf("Insira um/dois caractere:");
scanf("%c %c",&letra1,&letra2);
printf("Você digitou:'%c' e '%c'", letra1, letra2);}

Silas Abud disse...

Olá...
Estou tendo dificuldade parecida com a primeira situação neste post.

#include

int main(){

char A[15], B[15], C[15], D[15], E[15];
int A1, B1, C1, D1, E1;

printf("\n\nDigite o nome do primeiro município:\n* ");
scanf("%15[^\n]", A);

printf("Temperatura média:\n* ");
scanf("%d", &A1);


printf("\n\nDigite o nome do segundo município:\n* ");
scanf("%15[^\n]", B);

printf("\nTemperatura média:\n* ");
scanf("%d", &B1);

Quando digito o primeiro nome e a temperatura, quando aperto enter ele já pula para segunda temperatura.
Eu coloquei o getchar(); depois da primeira temperatura e funcionou, mas não sei se é a coisa correta a fazer neste caso.

dieime morais disse...

Deu certo para o que eu precisava.
Obrigado

Samuel disse...

Caramba, eu não estava conseguindo limpar o buffer.
Pensei que era _fpurge(stdin), com só uma underline, mas percebi agora que são duas __, demorei um pouco pra entender, só dava erro. haha
Valeu.

Rayane Santos disse...

Excelente! Obrigada...

Quase sempre não comento, mas dessa vez ajudou bastante mesmo!

Vlw

Gostou desse tutorial de C?
Sabia que o acervo do portal C Progressivo é o mesmo, ou maior que, de um livro ou curso presencial?
E o melhor: totalmente gratuito.

Mas para nosso projeto se manter é preciso divulgação.
Para isso, basta curtir nossa página no Facebook e/ou clicar no botão +1 do Google.
Contamos e precisamos de seu apoio.