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.

28 comentários:

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

    ResponderExcluir
  2. 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+

    ResponderExcluir
  3. 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??

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

    ResponderExcluir
  5. 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.

    ResponderExcluir
  6. 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;
    }

    ResponderExcluir
  7. 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 :)

    ResponderExcluir
  8. 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.

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

    ResponderExcluir
  10. #include

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

    ResponderExcluir
  11. 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);}

    ResponderExcluir
  12. 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.

    ResponderExcluir
  13. Comigo nenhum desses funcionou no Linux, então usei o comando setbuf(stdin, NULL); e resolvi. Acho que ele também funciona para Windows, pois setbuf parece ser um acrônimo para set buffer, ou seja, configurar o Buffer.

    ResponderExcluir
  14. Anônimo que perguntou qual a maneira mais adequada de resolver o problema do Buffer, não existe. Simples assim. Você escolhe como vai fazer.

    ResponderExcluir
  15. Deu certo para o que eu precisava.
    Obrigado

    ResponderExcluir
  16. 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.

    ResponderExcluir
  17. Excelente! Obrigada...

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

    Vlw

    ResponderExcluir
  18. No Linux, ao tentar compilar o programa, dá o seguinte erro:

    winter@winter-desktop:~/C$ cc problemas_getc.c
    problemas_getc.c: In function ‘main’:
    problemas_getc.c:12:2: warning: implicit declaration of function ‘__fpurge’ [-Wimplicit-function-declaration]
    __fpurge(stdin);

    Para solucionar o problema, basta adicionar no cabeçalho do arquivo:

    #include

    E voilà! Funciona! :)

    Fonte:
    http://man7.org/linux/man-pages/man3/fpurge.3.html

    ResponderExcluir
  19. Olá.
    Estou com um "pequeno problema" se assim posso dizer
    programo c no linux, e obviamente utilizando o compilador gcc,
    toda vez que compilo o código onde utilizo o __fpurge dá um aviso de declaração implícita da função, porem funciona, só que me incomoda esse aviso, poderiam me dizer o porque? Ou não tem nenhum problema.

    ResponderExcluir
  20. Não é recomendado o uso de fflush(stdin), ele é usado para trabalhar com stdout.
    Mais aconselhável para limpar buffer de entrar, na maioria dos caso e não todos, é o setbuf(stdin, NULL)

    http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1052863818&id=1043284351

    ResponderExcluir
  21. Segundo o pessoal da softblue uma forma de limpar o buffer é criar um ponteiro de char no inicio do programa e cada vez que quizer limpar o buffer é só dar um scanf passando %c &c:
    Exemplo:
    int main()
    {
    char *c;

    char letra1, letra2;

    printf("Insira um caractere: ");
    scanf("%c", &letra1);
    // limpeza do buffer
    scanf("%c",&c);


    printf("Insira outro caractere: ");
    // limpeza do buffer
    scanf("%c", &letra2);


    printf("Você digitou: '%c' e '%c'", letra1, letra2);
    // é so colar "scanf("%c",&c);" para cada limpeza

    scanf("%c",&c);
    return 0;
    }


    scanf("%c",&c);
    return 0;
    }
    Isso faz o terminal "segurar a tela" para leitura no final tb para quem usa visual express como eu.
    Caso tenha alguem interessado em aprender c podemos criar um grupo de whats e aprender c juntos.

    ResponderExcluir
  22. Tamy Caroline, sobre a função rand(), sei q a resposta está meio atrasada, mas o caso da função rand() sempre pensar, ao executar inicialmente um programa, o mesmo valor, se dá por causa da semente que se continua o mesmo. No caso, para o computador mudar a semente, é recomendado que vc utilize o tempo como semente:
    #include "stdio.h"
    #include "time.h"
    #include "stdlib.h"
    int main (){
    srand(time(NULL)); //mudando a semente para o tempo do computador
    //depois vc segue com a função rand normalmente e cada nova execução, ele gerará valores diferentes
    [...]
    }

    ResponderExcluir
  23. O que o Fabio disse acima está correto. o comando fflush(stdin) não é recomendado para limpar o stdin.
    Sugiro o uso do modo como ele fez p limpar o stdin e também deixo duas funções que são ótimas para limpar o stdin:

    void limpa_linha() {
    scanf("%*[^\n]");
    scanf("%*c");
    }

    ou essa:

    void fflush_stdin() {
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF);
    }

    Para usar essas funções, qualquer q seja, é só chamar ela depois de cada scanf e resolve o problema do stdin

    ResponderExcluir
  24. para limpeza segura do buffer usem

    while((c = getchar()) != '\n' && c != EOF){}; //sempre após scanf()

    inicialize a variável
    char c:

    ResponderExcluir
  25. #include
    #include
    #include
    #include
    #include
    #include

    main (void)
    {
    setlocale(LC_ALL,"portuguese");
    system ("color 3E");

    char letra1, letra2;

    printf("Insira um caractere: ");
    letra1 = fgetc(stind);

    fflush(stdin);

    printf("Insira outro caractere: ");
    letra2 = fgetc(stind);

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

    }

    Só não funcionou com o "fgetc(stind);"

    Não entendi porque isso aconteceu. Fiquei a me perguntar se essa função pertence ao linux. utilizo o windows.

    ResponderExcluir
  26. jean carlos conceição experimente "fgetc(stdin);" stind deve ser uma gralha.

    ResponderExcluir
  27. Parabéns pelo criador desse curso. É muito bom mesmo, ensina coisas que nem na faculdade eu vi.

    ResponderExcluir

É quase impossível criar centenas de páginas voltadas para programação C e não cometer algum erro.

- Se notar algum conceito, letra ou trecho de código errado, deixe sua correção

- Se perceber uma maneira melhor ou mais eficiente de fazer algo, deixe sua ideia

- Se algo não ficar claro ou for confuso, nos avise

Aos poucos vamos aumentando e melhorando a qualidade de nosso material, e para isso contamos com sua ajuda.