Enviar um café pro programador

Pode me ajudar a transformar cafeína em código?

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:

Vinicius disse...

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

Unknown 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+

Unknown 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.

Unknown 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 :)

joagostini 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?

Unknown disse...

#include

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

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

Unknown 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.

Anônimo disse...

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.

Anônimo disse...

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.

Unknown 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

Márcio Eduardo Moraes Winter disse...

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

Felipe San disse...

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.

Unknown disse...

Show de bola \o/\o/\o/\o/\o/

Unknown disse...

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

Fabio disse...

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.

Unknown disse...

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
[...]
}

Anônimo disse...

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

sorocabana disse...

para limpeza segura do buffer usem

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

inicialize a variável
char c:

Unknown disse...

#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.

Ana disse...

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

Anônimo disse...

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