Ir para conteúdo
VictorCacciari

[Tutorial] system("pause") e fflush(stdin)

Recommended Posts

Boas Pessoal!

 

Já faz algum tempo que eu vejo pessoas postando códigos sempre com os mesmos errinhos, que acabam por virar vícios lá na frente.

Claro que é a maneira mais fácil de resolver a maioria dos problemas "chatos" que encontramos quando estamos programando em C/C++.

Mas não é a forma mais confiável, não é portável e ainda é deselegante.

 

Para já, o campeão, é o famoso std::system("pause").

 

Antes de mais, usem o Code::Blocks, não é preciso usar system("pause") para ver o resultado do seu programa!

 

O Problema...

system("pause");

 

Motivo

Eu imagino que é para conseguir ler o resultado do código, ou falar para o usuário: "Pressione qualquer tecla para continuar."

(Não consigo pensar em outros motivos...)

 

Como contornar?

Eu espero que vocês conheçam a função scanf().

Se vocês conhecem o nosso amigo scanf, então sabem que ele lê uma string que segue uma formatação.

Então chega de "blá blá blá", e vamos ao que interessa!

 

O uso do operador, " * " (supressão), na string de formatação, diz para o scanf ler e descartar qualquer coisa.

Como funciona?

int numero;

printf("Digite dois números separados por um espaço: ");
scanf("%*d %d", &numero);

O código acima pegará apenas o segundo número digitado, pois nós falamos para o scanf ler e descartar o primeiro.

Nós podemos fazer isso para qualquer tipo de dados.

Vamos então pedir ao scanf ler e descartar um caractére, uma quebra de linha ('\n').

 

//... tooooodo o seu código aqui
printf("Pressione enter para continuar");
scanf("%*c");

ps.: Não usem as funções da <conio.h>, essa biblioeta também não é portável e está obsoleta, é muito antiga. Para ter mais controle sobre o input/output do console, busque por nCurses (para aqueles que usam Windows, pdCurses).

 

Aproveitando a onda do scanf, vamos a um outro problema, que tem uma solução muuuuito parecida.

 

O Problema...

Sujeira no buffer de entrada, limpa com fflush(stdin)

 

Motivo

Essa é fácil, ninguém quer um código maluco, com '\n' de sobra em todo o lado.

Você nunca passou por esse problema??

Do it yourself! :P

int main()
{
char cont='s';

	while(cont == 's')
	{
		printf("Deseja continuar? (s/n): ");
		scanf("%c", &cont);
	}
	return 0;
}
:o O meu pc ficou louco???

Não, fique calmo!

O que aconteceu aqui foi que quando digitamos 's' e apertamos enter, a string enviada foi "s\n", mas o scanf só leu o 's' e deixou o '\n' no buffer de entrada padrão (stdin), no próximo loop esse '\n' será lido automaticamente e vai sair do loop.

 

Como contornar?

Por que não devemos usar fflush(stdin) se ele "funciona" direito??

Na documentação da função está escrito: "effect undefined for input streams".

Se nem eles (que escreveram a função) sabem o que acontece, nós, simples mortais ^_^, muito menos!

 

Para resolver o problema, vamos de novo usar o operador de supressão do scanf.

e vamos falar para ele: "Hey, leia tuudo mas descarte o último caractére" (o último caractére sempre será um '\n').

 

Usando o mesmo código que gerou o problema:

int main()
{
char cont='s';

	while(cont == 's')
	{
		printf("Deseja continuar? (s/n): ");
		scanf("%c%*c", &cont); //note a mudança aqui!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	}
	return 0;
}

Pois é...

Espero que tenha sanado algumas dúvidas dos iniciantes!

:lol:

 

Se gostarem eu fixo o tópico.

 

Até mais!

http://forum.imasters.com.br/public/style_emoticons/default/bye1.gif

  • +1 9

Compartilhar este post


Link para o post
Compartilhar em outros sites

É inútil. você posta e mesmo assim tem uns trolhas que ensinam a programar usando isso.

  • +1 2

Compartilhar este post


Link para o post
Compartilhar em outros sites

Isis...

 

Fazer o que??

A esperança é a última que morre... rsrsrs

 

Sempre tem os trolhas, mas o aluno interessado não aprende a programar pelo professor.

  • +1 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Boa Victor é bastante util quem começa com C ler isso..

eu msm qnd vem gente perguntar falo pra usar o system..

soh uma coisa o : %*c pode ser colocado em qlqr lugar do scanf?

tipo:

scanf("%*c%c", &cont);

e

scanf("%c%*c", &cont);

 

[]s

Compartilhar este post


Link para o post
Compartilhar em outros sites

QuitzAUMMM

 

Os livros não falam muito desse operador do scanf.

o operador pode ser usado em qualquer lugar, com qualquer tipo de dado, por exemplo:

 

dada uma string:

(teste1)(teste2)(teste3)

 

leia teste1 e teste3, e coloque em variáveis separadas, fazendo o uso de apenas 2 variáveis, uma para cada.

 

eu faria:

scanf("(%s)(%*s)(%s)%*c", &var1, &var2);

 

note que, a primeira ocorrência de * é para descartar a string que está ali, a segunda é para limpar o buffer de imediato...

Toda vez que eu uso scanf, eu ja adiciono "%*c" no final (quando não quero as quebras de linha).

 

Nota.: Isso funciona para as outras funções derivadas do scanf também, por exemplo: sscanf e fscanf

  • +1 2

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tá mas ai tem o problema no substituto do stdin.

 

E quando eu não sei a qntidade de strings. Tipo tenho este código

 

//...
char str[50];
printf("Digite o nome: ");
scanf("%s",&str);
//...

Bem na minha opiniãol deveriamos estimular mais o uso de um tipo especializado string e não em arrays de char e outra, usar uma fução que saiba como tratar o tipo string, pois não desmerecendo aos execelntes programadores que aquui se encontra, mas você não concordam que temos de parar com POG's, assim, minha opinião, que pode ser equivocada, nada impede de tal.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Paulo, não entendi a sua dúvida.

 

se você não sabe o número de strings, e quer gravar os espaços na string, use fgets!!! ps.: ele manda o '\n' para a string também, deixando o buffer limpo

ou, deixando o scanf um pouco mais complicado:

scanf("%[^\n]%*c", &str);

Na verdade, a definição de string é cadeia de caractéres, do ingles, char array. Em C++ existe a classe std::string, que na verdade é um conjunto de métodos para facilitar o uso do char*.

 

E eu não encontrei a gambiarra... onde está?

Compartilhar este post


Link para o post
Compartilhar em outros sites

se for usar fgets é soh depois você usar a magina do strlen-1 e do \0 e fim de papo ;D

Compartilhar este post


Link para o post
Compartilhar em outros sites

Assim em momento algum eu quis desmerecer, mas a explicação, cobre o problema de ter uma string que tenha mais de um espaço (o que eu chamei de numro de strings), quanto ao trabalhar com array de char, sei lá é minha opinião de que fica um pouco... "complicação denecessária", mas ai é de cada um.

E o objetivo vou acrescentar com criticas construtivas, espero que isso tenha ficado claro e parabens pela iniciativa.

 

Já a respeito da classe std::string u a conhecia já mas não acho ela muiiito o que eu queria pois no fim usa char[] igual e não encapsula essas tarefas de array.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas paulo,

 

se não vamos trabalhar com array de chars, vamos trabalhar como?

Não esquecendo que estamos falando de C...

 

em C++ o nosso amigo std::cin nos resolve tudo! (ou quase tudo...hahahaha)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas paulo,

 

se não vamos trabalhar com array de chars, vamos trabalhar como?

Não esquecendo que estamos falando de C...

 

em C++ o nosso amigo std::cin nos resolve tudo! (ou quase tudo...hahahaha)

 

É neste ponto você tem razão, acho que estou querendo demais, tipo to viajando para classes, eu sei.

 

Mas concorda que seria bom?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mais algumas razões para não usar system("pause") nem fflush(stdin):

 

- system, segundo o cplusplus.com ou qualquer outra referência, executa um comando do sistema, retornando um inteiro, e seu funcionamento é dependente do sistema (em outras palavras, o código não é portável). Muitos dos códigos que programadores iniciantes criam executam operações perfeitamente executáveis em qualquer sistema, mas esses programadores caem no descuido de usar um comando dependente de sistema, tornando um programa perfeitamente portável em um dependente da plataforma.

 

- fflush(stdin) vai limpar todo o fluxo de entrada de stdin, eu disse todo o fluxo. Daí, se fizermos um redirecionamento do fluxo de entrada para um arquivo, sei lá, entradas.txt, e esse arquivo contiver a stream:

a'\n'b'\n', o fflush vai limpar tudo depois do c, e não apenas o próximo '\n'. O segundo scanf do meu programa (supondo que ele possui dois scanfs para ler duas entradas char, tipo scanf ("%c", &var)) ficará comprometido e não lerá o caracter b.

Eu, por exemplo, as vezes redireciono o fluxo de entrada para um arquivo e o de saída para outro para facilitar os testes de entrada/saida de um algoritmo (apesar de que quase nunca eu programo).

Não que fflush seja uma função ruim, ela tem ótimos usos, mas esse não é um bom uso, visto que ele limita o uso de seu programa a receber dados apenas do teclado. Deixem pra usar fflush quando estiverem trabalhando com arquivos.

A solução proposto, que usa o * para descartar uma captura do fluxo, é uma solução mais adequada ao problema.

Vale lembrar que, apesar de vários scanf("%d", &var) sequênciais funcionarem perfeitamente, tal função não captura o '\n'. Logo, se usarmos um scanf ("%d", &var1) e logo após um scanf("%c", &var2), o segundo capturará o '\n' da stdin. o uso de um scanf("%d%*c", &var1) resolveria o problema.

Ah,fflush dá um warning quando usada com entradas pelo teclado (input streams). Porque eu não sei. Logo, até que alguém saiba porque essa função pode ter alguns comportamentos indefinidos e como evitar tais comportamentos, não usem fflush() a não ser para trabalhar com arquivos.

 

- Embora um scanf("%d%*c", &var1) resolva um problema de um iniciante, esse método não trada dádos inválidos.

Como exemplo, considere o código a seguir:

/* problema com o scanf:
 * ao realizar scanf com tipos diferentes, automaticamente a funcao le um '\n' vindo de uma scanf anterior que ainda permanecia no stdin
 * */

#include <stdio.h>

int main(void)
{
	int a;
	char b;
	char c;

	printf ("a: ");
	scanf ("%d%*c", &a);
	printf ("b: ");
	scanf ("%c%*c", &b);
	printf ("c: ");
	scanf ("%c%*c", &c);
	
	printf ("a = %d\n", a);
	printf ("b = %c\n", b);
	printf ("c = %c\n", c);
	
	return 0;
}
Esse programa eu fiz a alguns dias atrás quando eu "me toquei" desse problema do scanf e começei a estudá-lo.

Se o usuário entrar com um inteiro no primeiro scanf e um char no segundo, perfeito!!! Exemplo, a stream 85'\n' é uma boa entrada para o primeiro scanf.

O primeiro scanf "está dizendo": captura um inteiro e ignora o caracter que vem depois do inteiro.

Logo, ela captura 85, salva em a, e ignora o '\n'.

Agora experimente entrar com um "ab", isto é, a stream ab'\n'.

Ela procura o inteiro a partir do a. Como a não é um caracter que pode representar um inteiro, a função scanf retorna, isso mesmo, continua o ab'\n' no stdin. O segundo scanf quer capturar um caracter e ignorar o próximo. Logo, ele captura o "a" e captura e ignora o "b", e ainda resta o "\n" no stdin. Tal "\n" será capturado pelo terceiro scanf.

O fato de não existir um outro caractere para ser ignorado (lembre-se que nosso último scanf captura 1 char e ignora o outro) não trará problemas, pois ao acabar o fluxo de entrada, o stdin, a função scanf retorna.

Resultado: Bacalhau!!!

 

- Solução para o problema acima, que depois descobri que também é adotada por programadores que já não são tão iniciantes (o q um programador avançado usa não sei pq nunca olhei nenhum código mais avançado): capturar uma linha da stdin usando gets ou fgets e armazenar em uma string (array de char) grande o suficiente para ler os dados do teclado. Depois trata essa string e captura o que for dela necessário usando sscanf().

Veja o que o cplusplus.com fala sobre a gets(): (A fgets eu não estudei ainda)

Reads characters from stdin and stores them as a string into str until a newline character ('\n') or the End-of-File is reached.

The ending newline character ('\n') is not included in the string.

A null character ('\0') is automatically appended after the last character copied to str to signal the end of the C string.

Notice that gets does not behave exactly as fgets does with stdin as argument: First, the ending newline character is not included with gets while with fgets it is. And second, gets does not let you specify a limit on how many characters are to be read, so you must be careful with the size of the array pointed by str to avoid buffer overflows.

cplusplus.com

Isso é, o '\n' é capturado, assim não precisamos ter que nos preocupar com ele no stdin. O '\n' é convertido para '\0' na string.

Note que se a string for menor que a stream de entrada, a função gets ultrapassará os limites do array, ocorrendo assim uma operação perigosa ao sistema. Por isso é mais indicado o fgets(): não o uso porque eu não a estudei ainda, mas prefira fgets à gets.

Note o que o gcc me avisou quando eu criei um programa de teste para entender a gets antes de enviar este post:

gcc scanf.c -o scanf

/tmp/ccUDm33d.o: In function `main':

scanf.c:(.text+0x2f): warning: the `gets' function is dangerous and should not be used.

Bom pessoal, espero ter ajudado um pouco.

Vai uma dica: estudem e leiam livros, tentem compreender os conceitos para que não façam programas orientados a gambiarras!!!

Não adianta somente resolver o problema, resolvam corretamente, de forma eficiente e com um código bem escrito.

Não façam bacalhau, até porque manter e consertar um programa cheio de gambiarras custa muito mais do que desenvolver um programa bem feito!

 

- Mais uma coisa: Embora hoje eu tenha começado a estudar orientação à objetos, ainda estudo C, embora saiba que código estruturado está com seus dias contados. A justificativa é que, além de um programa OO ser, em baixo nível, uma implementação de código estruturada, a base que eu levo de C (uma das linguagens em que mais se aprende os conceitos da programação) é que está me dando suporte a resolver esses probleminhas que vão aparecendo. É a compreensão dos conceitos básicos que vai lhes permitir ter sucesso quando a coisa começar a complicar!!!

Compartilhar este post


Link para o post
Compartilhar em outros sites

po eu consegui entender a do system ("pause"), apliquei aqui e funcionou direito agora a do fflush nao to conseguindo implementar, se nao for abusar,teria como postar um programa que leia mas de 1 caracter usando fflush e esse mesmo programa nao usando ele?para eu poder entender a diferença?

Compartilhar este post


Link para o post
Compartilhar em outros sites

@fflush(stdin);

 

Isso é o que está na documentação:

int fflush (FILE * stream);

Libera o fluxo de fluxo e retorna zero em caso de sucesso ou EOF em caso de erro. Efeito indefinido para fluxo de entrada. fflush (NULL) libera todos os fluxos de saída.

 

Na documentação diz que pode gerar comportamento indefinido por se usar a função ? Eu posso estar enganado, mas o que eu entendi aí é que pode gerar efeito indefinido caso de um EOF. Talvez a execução do programa pare por conta disso levando a outros erros, mas talvez não. Nunca aconteceu comigo de o programa parar por causa dessa função. Talvez porquê eu usei elas nos lugares corretos. Tem razões para usar e para não usar. Como por exemplo, antes de um Scanf é legal usar, dentro do while. Já pra um redimencionamento para arquivo não é legal, como disse um user acima.

 

int i = 2
char nome[30];
while(i < 3)
{
   printf("\n\tDigite seu nome: ");
   if(nome != "") fflush(stdin); // Veriquei se a variável está vazia, se estiver não precisa do fflush(stdin)
   gets(nome);
}

É trabalhoso ? É. É desnessário ? É. Visto que pode fazer um TOKEN.

Mas só estou falando que na minha humilde opinião, não tem porque não usar a função em determinados casos.

 

Interessante que só pode gerar um comportamento indefinido para fluxo de entrada, mas pra fluxo de saída não. Porque ?

E não pode usar a função gets() também né , vocês ficam bravos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Caro Dee,

 

Na documentação diz que pode gerar comportamento indefinido por se usar a função ? Eu posso estar enganado, mas o que eu entendi aí é que pode gerar efeito indefinido caso de um EOF. Talvez a execução do programa pare por conta disso levando a outros erros, mas talvez não. Nunca aconteceu comigo de o programa parar por causa dessa função. Talvez porquê eu usei elas nos lugares corretos. Tem razões para usar e para não usar. Como por exemplo, antes de um Scanf é legal usar, dentro do while. Já pra um redimencionamento para arquivo não é legal, como disse um user acima.

 

Espero que você tenha lido o que eu escrevi no primeiro post, isto é, o meu artigo sobre o assunto.

Você não entendeu o que está escrito na documentação.

fflush RETORNA EOF no caso de ERRO.

O efeito é INDEFINIDO no caso de streams de ENTRADA.

 

Mas só estou falando que na minha humilde opinião, não tem porque não usar a função em determinados casos.

 

Se o efeito é INDEFINIDO, logo, não sabemos o que vai acontecer!

Não é correto utilizar fflush(stdin) NUNCA, como está escrito na documentação! (fantástico não? está tudo no manual!)

 

E, a função gets...

A gente não fica bravo quando utiliza-se essa função, ficamos FURIOSOS!

Afinal, a função é vulnerável...

Leia o segundo post deste tópico

 

Abraços

http://forum.imasters.com.br/public/style_emoticons/default/thumbsup.gif

Compartilhar este post


Link para o post
Compartilhar em outros sites

Caro Dee,

 

 

Na documentação diz que pode gerar comportamento indefinido por se usar a função ? Eu posso estar enganado, mas o que eu entendi aí é que pode gerar efeito indefinido caso de um EOF. Talvez a execução do programa pare por conta disso levando a outros erros, mas talvez não. Nunca aconteceu comigo de o programa parar por causa dessa função. Talvez porquê eu usei elas nos lugares corretos. Tem razões para usar e para não usar. Como por exemplo, antes de um Scanf é legal usar, dentro do while. Já pra um redimencionamento para arquivo não é legal, como disse um user acima.

 

Espero que você tenha lido o que eu escrevi no primeiro post, isto é, o meu artigo sobre o assunto.

Você não entendeu o que está escrito na documentação.

fflush RETORNA EOF no caso de ERRO.

O efeito é INDEFINIDO no caso de streams de ENTRADA.

 

Mas só estou falando que na minha humilde opinião, não tem porque não usar a função em determinados casos.

 

Se o efeito é INDEFINIDO, logo, não sabemos o que vai acontecer!

Não é correto utilizar fflush(stdin) NUNCA, como está escrito na documentação! (fantástico não? está tudo no manual!)

 

E, a função gets...

A gente não fica bravo quando utiliza-se essa função, ficamos FURIOSOS!

Afinal, a função é vulnerável...

Leia o segundo post deste tópico

 

Abraços

http://forum.imasters.com.br/public/style_emoticons/default/thumbsup.gif

 

FACEPALM!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom tópico.

Deve ser a forma mais fácil de limpar o buffer

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.