Ir ao conteúdo
  • Cadastre-se

C Funções fflush, _flushall, _fflush_nolock


Ir à solução Resolvido por arfneto,

Posts recomendados

Olá.

Tenho dúvidas sobre essas funções flush:

 

fflush: se o argumento for NULL, ela limpa todos os buffers abertos, mas ela funciona somente para a stdout, então como isso é possível?

 

_flushall: esta não precisa de argumentos, e limpa todos os buffers abertos, mas qual a consequência disso? O que de tão ruim pode acontecer em um contexto no qual desenvolvo um programa simples, de iniciante?

 

_fflush_nolock: isso seria, por exemplo, limpar enquanto se suja? Seria tipo a fflush assíncrona? Funciona somente para a stdout também?

 

O termo correto é "limpar"? O que melhor descreveria o comportamento dessas funções?

Link para o comentário
Compartilhar em outros sites

58 minutos atrás, Luccas Fernando disse:

fflush: se o argumento for NULL, ela limpa todos os buffers abertos, mas ela funciona somente para a stdout, então como isso é possível?

O 'f' em fflush e de arquivo (file) isso vem do antigo unix em que tudo era um arquivo, então em poucas palavras ela funciona em qualquer arquivo aberto.

 

1 hora atrás, Luccas Fernando disse:

_flushall: esta não precisa de argumentos, e limpa todos os buffers abertos, mas qual a consequência disso? O que de tão ruim pode acontecer em um contexto no qual desenvolvo um programa simples, de iniciante?

Isso depende de quão perto você está do kernel, um programa rodando em user space (espaço de usuário) não afetaria muito o sistema, mas um programa rodando em kernel space (espaço do kernel) travaria a maquina ao fazer um flush em todos os buffers.

No ponto de vista da linguagem C um flush em todos os buffers só é útil em caso de falha grave onde um processo secundário trava, é o primário fica sendo responsável por limpar toda a bagunça do secundário (isso só seria aceitável no user space).

 

todas as outras funções como _flushall e _fflush_nolock são funções do windows, fugindo do padrão do C, isso que dizer que não são portáveis.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Solução

 

Antes de tudo considere que fflush() só se aplica a arquivos --- fluxos, streams --- de saída. Pode funcionar na entrada para alguns compiladores mas pela documentação oficial isso gera comportamento indefinido --- UB, undefined behavior --- como aparece nos livros e na documentação padrão
 

1 hora atrás, Luccas Fernando disse:

O que de tão ruim pode acontecer em um contexto no qual desenvolvo um programa simples, de iniciante?

 

 

Provavelmente nada de ruim.

Mas um iniciante não tem razão para usar isso num programa, a menos que esteja sendo mal "iniciado". Programas, de iniciantes ou profissionais, devem consumir as entradas e gerar as saídas, conforme o propósito de cada programa. Nada mais. E saber o que está onde e porque.

 

Os sistemas são muito bons em tratar os buffers. Esses mecanismos como fflush() e setbuf() existem para situações que o sistema não pode prever ou entender. Vou dar uns exemplos depois.
 

O grande incentivo de fflush() para iniciantes --- assim como de setbuf() com 0 --- é limpar a entrada do teclado porque o iniciante não foi informado de que deve consumir tudo o que está na entrada. O sistema não sabe o que é uma entrada e se limita e ler as teclas e guardar no tal buffer, aquela entidade mágica que zoa os programas dos iniciantes e faz o programa seguir sem ler as coisas ou mesmo ler coisas que "ninguém" digitou.

 

stdin e stdout sempre usam buffers, e seria 1d10t@ não usar por uma questão de simples eficiência. stderr geralmente não, porque não seria esperto. Se não usassem buffers na entrada você não poderia por exemplo usar backspace e ficar editando a linha de entrada antes de teclar ENTER, certo?

 

Mas em alguns casos, como quando está digitando uma senha ou em algum jogo, se pode querer desligar isso, e está bem documentado tanto para Linux -- e MacOS e Unix e Android --- ou Windows. Em Windows se desliga LINE_INPUT chamando SetConsoleMode() e no Linux se chama ioctl() e manipula os valores da estrutura termio. Estou escrevendo de memória e se precisar de uma referência escreva de volta.

 

Mas em geral iniciantes não precisam disso.

 

E o "lixo" do teclado

 

Não existe "lixo de teclado", "lixo no buffer" ou coisas assim. Existem talvez livros ruins, instrutores com preguiça ou ruins mesmo, livros ruins e coisas assim.

 

1 hora atrás, Luccas Fernando disse:

fflush: se o argumento for NULL, ela limpa todos os buffers abertos, mas ela funciona somente para a stdout, então como isso é possível?


Não é possível. 
 

Você provavelmente entendeu errado ou talvez leu alguma tradução tosca. Não se trata de somente stdout. E não se trata de limpar e sim de descarregar :( 

 

Se o argumento for NULL fflush() limpa os buffers de todos os fluxos abertos. TODOS. Inclusive stdout claro. E isso não quer dizer apagar. Quer dizer descarregar. fflush() só está definido para fluxos de saída, e por isso se acaso funcione na entrada em alguma implementação, como a stdio da Microsoft, acaba apagando tudo como se algo tivesse lido tudo o que tinha no buffer, por simples analogia

 

Citação

flush não é limpar nem apagar

 

Mais a seguir.

 

1 hora atrás, Luccas Fernando disse:

_fflush_nolock: isso seria, por exemplo, limpar enquanto se suja? Seria tipo a fflush assíncrona? Funciona somente para a stdout também?

 

Não, não seria.

_fflush_nolock() nada tem a ver com os fluxos abertos em seu programa ou com sincronismo. É uma versão de fflush() que, no caso de seu programa ter múltiplos threads --- rodar várias funções ao mesmo tempo --- não bloqueia todas até o flush terminar, que é o que fflush() faz por padrão.

 

Exemplo

 

Se

  • você tiver um programa que emite notas fiscais, e ele emite até 10 notas ao mesmo tempo em 10 threads separados
  • faz cálculos com arquivos de cliente e preços e tal
  • ao terminar de preparar a nota TODOS threads enviam os dados para uma única função de impressão que acessa as impressoras e faz todo o resto, como gravar log e backup
  • só essa função grava em disco e acessa as impressoras

Então

  • Num dado momento pode ser preciso rodar um flush para liberar as impressoras e imprimir o que está pronto, por alguma condição especial, como uma mudança de horário ou de formulário nas impressoras.
  • O desenvolvedor sabe que só esse thread acessa os arquivos não há razão para bloquear todos os outros
  • então é seguro e bem mais rápido usar _fflush_nolock() para esvaziar a saída.
  • Esse é a razão do nolock: thread locking.
  • Só o cara que escreve o programa sabe se pode usar NOLOCK, claro. O sistema não vai saber.
  • Num programa comum, single-threaded, isso não faz a menor diferença, porque não vai travar ninguém mesmo ;) já que só existe um caminho de execução
1 hora atrás, Luccas Fernando disse:

O termo correto é "limpar"? O que melhor descreveria o comportamento dessas funções?


Provavelmente o termo correto seria, como eu disse, a tradução normal de flush: descarregar. Como eu disse, flush não apaga nada.

 

Veja o que diz a Microsoft sobre isso:
 

A função fflush libera o fluxo de fluxo. Se o fluxo foi aberto no modo de gravação
ou se ele foi aberto no modo de atualização e a última operação foi uma gravação, 
o conteúdo do buffer de fluxo é gravado no arquivo subjacente ou o dispositivo e 
o buffer são descartados. 
Se o fluxo tiver sido aberto no modo de leitura ou se o fluxo não tiver buffer,
a chamada para fflush não terá efeito e qualquer buffer será retido. Uma chamada
para fflush nega o efeito de qualquer chamada anterior ao ungetc para o fluxo.
O fluxo permanecerá aberto após a chamada.
Se o fluxo for nulo, o comportamento será o mesmo que uma chamada para fflush 
em cada fluxo aberto. Todos os fluxos abertos no modo de gravação e todos os
fluxos abertos no modo de atualização em que a última operação foi uma gravação
são liberados. A chamada não tem efeito em outros fluxos.

 

E o gnu

 

image.thumb.png.01a37417f52822f76cf94c04b1d0626a.png

 

Recomendo muito consultar a documentação, sempre.

 

Se é assim, pra que serve um flush() afinal?

 

  • Imagine que você tem um programa que transmite uma mensagem pela placa de rede, via TCP/IP, e quer enviar no máximo 1500 bytes por vez.  O sistema sabe isso, o valor de MAX_MTU. 
    • Quando chegar a isso o driver vai automaticamente transmitir a mensagem.
  • Imagine que você tem uma impressora que imprime linhas de até 132 colunas, como nos anos 80.
    • Assim que tiver uma linha no buffer ela imprime e começa a colocar os dados na próxima.
  • Imagine que você tem um painel LCD ligado ao seu micro, e ele tem uma linha de 80 colunas. Você vai colocando as letras no buffer.
    • Quando dá 80 o driver manda as linhas para o LCD e aparece lá na vitrine.
  • Imagine que você tem um furgão e ele aceita 1200KG de carga.
    • O sistema de saída quando chega ao peso libera a emissão do manifesto de carga e libera a carga para expedição.

Isso é o normal. O que seria um flush() nesses casos?

  • Você sabe que só tem 7 letras, mas quer mandar pro LCD aquela linha: "FECHADO".
  • Você quer despachar o furgão com 300 kg porque ele tem prazo pra sair e o cliente contratou a carga expressa
  • Você quer imprimir uma linha em branco na line printer dos anos 80

Para essas coisas você faz um flush() do buffer correspondente e o sistema descarrega tudo pra saída. Para as outras coisas use seu cartão Mastercard . Ok, essa foi pobre :( 

 

 

 

 

 

 

54 minutos atrás, kgin disse:

Isso depende de quão perto você está do kernel, um programa rodando em user space (espaço de usuário) não afetaria muito o sistema, mas um programa rodando em kernel space (espaço do kernel) travaria a maquina ao fazer um flush em todos os buffers.

No ponto de vista da linguagem C um flush em todos os buffers só é útil em caso de falha grave onde um processo secundário trava, é o primário fica sendo responsável por limpar toda a bagunça do secundário (isso só seria aceitável no user space).

 

todas as outras funções como _flushall e _fflush_nolock são funções do windows, fugindo do padrão do C, isso que dizer que não são portáveis

 

Recomendo ler o que eu postei, e talvez a documentação do Unix/Linux/Mac/Windows.

 

Não é como está imaginando. _flush_nolock por exemplo não é função Windows, está disponível em UNIX e variantes e Windows para qualquer linguagem. São primitivas de I/O essenciais. 

 

NOLOCK tem a ver com multi-threading e não com falhas de sistema. Leia isso por exemplo em https://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html

 

Um programa em user space pode ser multi-threading e usar lock/nolock em flush, e pode usar flush() em centenas de arquivos. Nada tem a ver com proximidade do kernel. 

 

1 hora atrás, kgin disse:

todas as outras funções como _flushall e _fflush_nolock são funções do windows, fugindo do padrão do C, isso que dizer que não são portáveis.

 

Não, não são. Citei acima a documentação do GNU, mas pode ver aqui um trecho da documentação do Ubuntu 20 e a referência às versões unlocked de stdio:

 

UNLOCKED_STDIO(3) 
    Linux Programmer's Manual  
       UNLOCKED_STDIO(3)

NAME
       getc_unlocked, getchar_unlocked, putc_unlocked, putchar_unlocked - nonlocking stdio functions

SYNOPSIS
       #include <stdio.h>

       int getc_unlocked(FILE *stream);
       int getchar_unlocked(void);
       int putc_unlocked(int c, FILE *stream);
       int putchar_unlocked(int c);

       void clearerr_unlocked(FILE *stream);
       int feof_unlocked(FILE *stream);
       int ferror_unlocked(FILE *stream);
       int fileno_unlocked(FILE *stream);
       int fflush_unlocked(FILE *stream);
       int fgetc_unlocked(FILE *stream);
       int fputc_unlocked(int c, FILE *stream);
       size_t fread_unlocked(void *ptr, size_t size, size_t n,
                             FILE *stream);
       size_t fwrite_unlocked(const void *ptr, size_t size, size_t n,
                             FILE *stream);

       char *fgets_unlocked(char *s, int n, FILE *stream);
       int fputs_unlocked(const char *s, FILE *stream);

       #include <wchar.h>

 

 

 

 

 

 

 

 

 

 

  • Curtir 2
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

10 horas atrás, Luccas Fernando disse:

O termo correto é "limpar"? O que melhor descreveria o comportamento dessas funções?

Sim, porque é coerente com a mais presente das ações no procedimento; excluir [limpar/esvaziar] algo supérfluos na memória temporária ['buffer']. 'Flushing', é uma tática do caçador para forçar uma caça a sair de seu esconderijo, entende-se aqui que 'flush', é uma função da 'buffer' para forçar o tratamento dos dados nele contidos, esvazia ou liberar.

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

  • 4 semanas depois...
Em 22/03/2021 às 18:57, kgin disse:

todas as outras funções como _flushall e _fflush_nolock são funções do windows, fugindo do padrão do C, isso que dizer que não são portáveis.

Na documentação diz que o requisito de ambas é o cabeçalho stdio.h. São portáveis.

 

Em 22/03/2021 às 19:50, arfneto disse:

Pode funcionar na entrada para alguns compiladores mas pela documentação oficial isso gera comportamento indefinido

O que é exatamente um "comportamento indefinido"? Ou de fato não há nada que descreva o comportamento indefinido de fflush(stdin)?

 

Em 22/03/2021 às 19:50, arfneto disse:

Se o argumento for NULL fflush() limpa os buffers de todos os fluxos abertos. TODOS.

Mas então pode gerar comportamento indefinido, certo? Já que stdin está incluída nesse "todos".

 

Em 22/03/2021 às 19:50, arfneto disse:

Não se trata de somente stdout.

Somente fluxos de saída então?

 

Em 22/03/2021 às 19:50, arfneto disse:

e por isso se acaso funcione na entrada em alguma implementação, como a stdio da Microsoft, acaba apagando tudo como se algo tivesse lido tudo o que tinha no buffer, por simples analogia

E se não "funcionar"? Vai gerar comportamento indefinido?

Tem alguma forma de prever o que vai acontecer?

 

Em 22/03/2021 às 19:50, arfneto disse:

acaba apagando tudo como se algo tivesse lido tudo o que tinha no buffer, por simples analogia

Isso seria descarregar o buffer?

Descarregar seria retirar a carga... Caso funcione para a stdin, retirar algo que tenha restado de, por exemplo, uma leitura, não seria apagar?

 

Mais uma pergunta: como fflush se comporta quando o buffer está vazio?

Por exemplo, caso funcione para a stdin, e o buffer esteja vazio, segundo sua analogia, a função teria que esperar algo para ler da stdin, certo? Consequentemente, o programa iria pausar, mas isso não acontece...

Link para o comentário
Compartilhar em outros sites

1 hora atrás, Luccas Fernando disse:

O que é exatamente um "comportamento indefinido"?

 

1 hora atrás, Luccas Fernando disse:

de fato não há nada que descreva o comportamento indefinido de fflush(stdin)

 

Pois é: você respondeu à sua pergunta. Não consigo imaginar um comitê ISO tentando definir "exatamente" um comportamento indefinido, o tal UB na literatura --- undefined behavior.  Como descrever o indefinido? Algo poético talvez.

 

1 hora atrás, Luccas Fernando disse:

Somente fluxos de saída então?

 

Exato

 

1 hora atrás, Luccas Fernando disse:

E se não "funcionar"? Vai gerar comportamento indefinido?

Tem alguma forma de prever o que vai acontecer?

 

Não, não creio haver uma forma de prever o indefinido pois definido estaria.

 

1 hora atrás, Luccas Fernando disse:

Caso funcione para a stdin, retirar algo que tenha restado de, por exemplo, uma leitura, não seria apagar?

 

Se for implementado na entrada faz isso por exclusão. É o caso dos compiladores da Microsoft. Se não for, como está na documentação, UB.

 

1 hora atrás, Luccas Fernando disse:

Mais uma pergunta: como fflush se comporta quando o buffer está vazio?

 

Não faz diferença. Apenas retorna.

 

1 hora atrás, Luccas Fernando disse:

Por exemplo, caso funcione para a stdin, e o buffer esteja vazio, segundo sua analogia, a função teria que esperar algo para ler da stdin, certo? Consequentemente, o programa iria pausar, mas isso não acontece

 

flush em inglês é descarga. O flush só faz isso. Não vê se está vazio. Apenas torna vazio processando tudo que está pendente.

 

Não sei o que seria a analogia a que se refere. nem porque faria o programa pausar, esperando vir algo para simplesmente apagar. Seria algo ingênuo pra não dizer estúpido da parte do sistema.

 

O que diz o padrão
 

image.png.c0dd18c883da82265d2b1e4ea5fbe634.png

 

No item 2 diz que se não de saída ou de entrada/saída mas que a última operação não tenha sido de ENTRADA vai descarregar os dados. Caso contrário o comportamento é indefinido.

 

No item 3 diz que se o ponteiro for NULL descarrega todos os fluxos.

 

E porque alguém faria isso? 

 

Entenda que descarregar (flush)  não é apagar (erase, delete). Só que em especial para iniciantes que tem a intenção de "apagar lixo do teclado" a noção de flush() é sempre APAGAR. E a necessidade de flush para descarregar quase nunca aparece em programas para iniciantes, e assim mesmo desenvolvedores e até instrutores com alguma experiência tem essa noção equivocada.

 

Porque usar um flush()?

 

Imagine que você tem um painel LED simples, por exemplo

 

Que mostra 120 caracteres em blocos de 9 x 13 LED. O driver do painel ao chegar a 120x9 tirinhas de 13 LED de altura manda a linha pro aparelho e a mensagem acende no painel. Automático.

 

Se num dado momento você quer mandar só 30 o que você faz? Um flush() do buffer, e aí o driver manda o que tem.

 

 

image.png.502101eeb724073dc91c2162daa6157c.png

Imagine uma LINE PRINTER, aquelas de antigamente dos mainframes, que imprimem linhas de 132 colunas. Como essa IBM 1403 aqui à esquerda.

 

Ela só imprime a linha ao chegar na coluna 132. Quer imprimir uma linha com menos? Usa um flush.

 

Entenda: flush() descarrega os dados. Processa antes do normal. Não apaga nada

 

 

 

 

 

Considere o driver TCP/IP para comunicação: tem um parâmetro, MTU, que marca o tamanho do maior pacote possível. Numa transmissão via rede local ou Internet ao atingir esse valor o pacote é fragmentado e essa parte é transmitida. É automático, como nos outros exemplos. Mas se você quer mandar um pacote pequeno só com 40 bytes faz o que? flush(). Nào apaga nada. Processa tudo sem esperar a hora normal de processar.

 

Porque alguém ia querer flush() em todos os streams, usando NULL?

 

O mais óbvio e comum é quando você tem que encerrar o programa, ou tem um checkpoint importante, como um início de ciclo: você PRECISA processar todos os dados e quer ter um retorno disso antes de encerrar o programa e deixar por conta do sistema.

 

Um outro caso é segurança. Ter certeza que que os dados foram gravados, de modo que uma falha catastrófica na máquina não vai deixar uma transação efetivada sem ser gravada em disco.

 

Um outro caso é a liberação de locks em bancos de dados: uma operação numa coluna de uma tabela pode deixar um certo volume de dados travado sem acesso até o fim de uma transação e prejudicar a performance do sistema então se pode rodar um flush() antes do normal para garantir a liberação da tabela no banco de dados.

 

O comando sync no Unix/Linux e derivados

 

O que faz o sync? grava em disco tudo que possa estar ainda nos buffers de cache de disco.

 

Porque um flush() na entrada apaga e não descarrega?

 

No caso de um fluxo de saída, como o driver da placa de rede, a impressora, um printf(), o sistema sempre sabe o que fazer com os dados. Só não faz em tempo real porque tem a hora certa, por uma questão de eficiência ou de tamanho ou de cache mesmo. O disco é gravado em clusters, por exemplo. E o cluster pode ter por exemplo 4K bytes.  Se você tem menos para gravar o sistema vai esperar certas condições para mandar os dados. Acho que já expliquei o suficiente ;)

 

No caso da entrada, se tem que processar os dados e não tem ninguém pra consumir vai fazer o que? Como o sistema vai processar isso? Só pode apagar mesmo. E isso é complicado, porque só tem um teclado por exemplo. E o teclado tem um outro buffer, porque normalmente opera em LINE MODE em Windows  e Linux. E tem ungetc() que pode "des-ler" algo e devolver pro buffer. E pode ter outros thread e outras aplicações lendo dados. Uma z0n@ afinal.

 

Por isso a noção de flush() que é descarregar, processar tudo que tem pendente, é estranha na entrada. O flush() em stdin seria algo como uma chamada a fgets() ler um ENTER, o popular newline: aí faz sentido e os dados são enviados para o consumidor, que foi o programa que chamou fgets(), e o sistema faz o flush desses dados, ungetc() à parte.

 

 

 

 

 

 

 

  • Obrigado 2
Link para o comentário
Compartilhar em outros sites

28 minutos atrás, arfneto disse:

Não, não creio haver uma forma de prever o indefinido pois definido estaria.

No caso eu me referia a prever se fflush(stdin) funcionaria ou geraria UB.

Como você disse:

28 minutos atrás, arfneto disse:

Se for implementado na entrada faz isso por exclusão. É o caso dos compiladores da Microsoft. Se não for, como está na documentação, UB.

Nos compiladores da Microsoft então, caso implementado na entrada, exclui o que tiver lá, torna vazio, independentemente se já estava. Seria isso?

 

28 minutos atrás, arfneto disse:

Entenda que descarregar (flush)  não é apagar (erase, delete).

Deixa ver se entendi: como o termo não é apagar, então não deveria ser implementado na entrada, certo? Já que você disse que quando implementado na entrada, o buffer é descarregado por exclusão do que tem lá, e esse não seria o propósito de fflush.

 

28 minutos atrás, arfneto disse:

O flush só faz isso. Não vê se está vazio. Apenas torna vazio processando tudo que está pendente.

Há como implementar uma função que faça isso? Que funcione para a stdin?

As que eu vi pausam o programa quando o buffer está vazio, e servem apenas para consumir os dados que restaram de uma leitura, algo como:

int c;
while((c = fgetc(stdin)) != '\n' && c != EOF);
Link para o comentário
Compartilhar em outros sites

49 minutos atrás, Luccas Fernando disse:

Deixa ver se entendi: como o termo não é apagar, então não deveria ser implementado na entrada, certo? Já que você disse que quando implementado na entrada, o buffer é descarregado por exclusão do que tem lá, e esse não seria o propósito de fflush

 

Exato. flush é uma instrução para terminar tudo que está pendente. No caso da saída é trivial: o sistema sempre sabe o que é porque, por exemplo, o driver de disco tem uma cache, a impressora, a placa de rede. Quando vem a instrução o sistema descarrega (flush) os dados. Não apaga p0rr@ nenhuma. Processa. Os streams (fluxos) tem seus buffers por uma questão óbiva de eficiência.

 

Só que no caso da entrada não tem como processar. Quem vai ler? O que seria uma descarga? Não faz sentido o próprio conceito. Se for para implementar o que se pode fazer é simplesmente descartar tudo.

 

54 minutos atrás, Luccas Fernando disse:

Há como implementar uma função que faça isso? Que funcione para a stdin?

 

https://docs.microsoft.com/pt-br/windows/console/flushconsoleinputbuffer

 

Em Windows, 

 

BOOL WINAPI FlushConsoleInputBuffer(
  _In_ HANDLE hConsoleInput
);

 

55 minutos atrás, Luccas Fernando disse:

Há como implementar uma função que faça isso? Que funcione para a stdin?

 

Eu já postei algumas maneiras de fazer isso aqui.

 

Entenda que você sempre pode desligar o LINE INPUT no Windows e o Cooked Mode no Linux. E aí o programa não para. É o que se faz por exemplo quando tem algo ligado no lugar do teclado, como uma leitora de cheques ou de cartões.

 

E outra opção é usar, como eu já te mostrei, WaitForSingleObject(). Aqueles 4 programas de menu que eu te mostrei, em C e C++, fazem esse controle.

 

Em Linux desligue Cooked Mode, use ioctl() para deixar VMIN e VTIME = 0 e cada read() retorna com o que tem pra ler. Nunca para. Eu usava isso para enganar o compilador anos atrás e seguir o programa. O compilador COBOL em Unix, que iria parar sempre mas era escrito em C e usava read() afinal ;) e sempre funcionou. Veja em termio.h no Linux ou no Mac. No Windows use SetConsoleMode().

 

Reveja aqueles programas de menu quando puder. Podem te dar uma ideia mais precisa. Não tenho aqui agora...

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

10 horas atrás, arfneto disse:

Entenda que você sempre pode desligar o LINE INPUT no Windows e o Cooked Mode no Linux.

Mas daí ele só não vai esperar por um Enter, certo? Se o buffer estiver vazio, ainda vai pausar o programa aguardando por uma tecla.

 

10 horas atrás, arfneto disse:

Em Windows, 

 




BOOL WINAPI FlushConsoleInputBuffer(
  _In_ HANDLE hConsoleInput
);

Eu fiz o seguinte, só pra testar:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main()
{
    int _int;
    char _char;

    scanf("%d", &_int);
    FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));

    scanf("%c", &_char);

    printf("\nint = (%d)\nchar = (%c)\n", _int, _char);

    return 0;
}

Mas o '\n' não foi descartado...

Sei que não deveria estar usando nesse contexto, mas acho que deveria funcionar. Estou fazendo alguma coisa errada?

 

10 horas atrás, arfneto disse:

E outra opção é usar, como eu já te mostrei, WaitForSingleObject(). Aqueles 4 programas de menu que eu te mostrei, em C e C++, fazem esse controle.

Acho que só criei 3 postagens aqui no fórum de C/C#/C++, não achei onde comenta sobre isso.

Link para o comentário
Compartilhar em outros sites

13 horas atrás, Luccas Fernando disse:

Mas então pode gerar comportamento indefinido, certo? Já que stdin está incluída nesse "todos"

????

 

Eu te mostrei a documentação. Lá está escrito que é indefinido A MENOS que seja um fluxo de saída ou de update em que a ultima operação não tenha sido de saída. Preste atenção ao texto.

 

18 minutos atrás, Luccas Fernando disse:

Mas daí ele só não vai esperar por um Enter, certo? Se o buffer estiver vazio, ainda vai pausar o programa aguardando por uma tecla

Errado. Não faria sentido. Não vai esperar por nada. Vai ler o que tem pra ler e retornar instantaneamente. Escreva um programa e experimente. Eu postei uns programas que fazem isso para menus, completos em C e C++ com código e exemplos de execução, neste mesmo forum. Procure aqui no material que eu postei. Tem essa função no forum.

 

22 minutos atrás, Luccas Fernando disse:

Sei que não deveria estar usando nesse contexto, mas acho que deveria funcionar. Estou fazendo alguma coisa errada?

 

Não há sentido em usar scanf() para ler uma letra só em um char no modo normal, Leia em um vetor e pegue a primeira letra, E TESTE o retorno. Teste o retorno de scanf() e do Flush.

 

23 minutos atrás, Luccas Fernando disse:

Acho que só criei 3 postagens aqui no fórum de C/C#/C++, não achei onde comenta sobre isso

 

Essa eu sei responder!!! Confundi você com o usuário @Lucca Rodrigues :( my bad

 

 

 

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

@arfneto 

16 minutos atrás, arfneto disse:

Errado. Não faria sentido. Não vai esperar por nada. Vai ler o que tem pra ler e retornar instantaneamente.

Eu não sei desligar o line input, mas achei por aí um programa que aparentemente faz isso, e o que ocorre é que o programa pausa porque o buffer está vazio:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main() {
    HANDLE _handle = GetStdHandle(STD_INPUT_HANDLE);
    DWORD _mode;

    GetConsoleMode(_handle, &_mode);
    SetConsoleMode(_handle, _mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));

    // o programa para aqui porque o buffer esta vazio
    fgetc(stdin);

    SetConsoleMode(_handle, _mode);

    return 0;
}

 

image.png.ce81fc8d7b9787e752b6e756a9d7463d.png

Daí se aperto alguma tecla:

image.png.83739523a5bbe2d7e7cfeb78f87fe650.png

Link para o comentário
Compartilhar em outros sites

11 horas atrás, arfneto disse:

No Windows use SetConsoleMode()

 

43 minutos atrás, Luccas Fernando disse:

Eu não sei desligar o line input,

 

Juntando esses dois trechos dá em algo: eu já tinha te dito como fazer, em Windows e Linux. E para Windows @Lucca Rodriguespostou aqui o link para os programas completos que fazem algo assim e que eu havia postado tempos atrás. Tentou ler os programas? Rodar algum?

 

E se juntar com a documentação, em português, escrita pelos donos do campo, da bola e das camisas em https://docs.microsoft.com/pt-br/windows/console/setconsolemode pode ter uma boa ideia de como fazer isso

 

Seu exemplo está correto. Não faz nada porque é só um exemplo de como escrever.

 

Apenas edite o programa para ficar de acordo com o que precisa.

Ainda sobre esse 🚄:

 

  • Se você usa o programa DENTRO de um IDE espere por possíveis problemas com as funções de console porque elas podem z0@r o IDE, ou o contrário. Teste sempre do modo normal.

 

  • Entenda que os programas de console estão tendo um renascimento, por causa do mundo cloud,  e voltando aos '80 --- as tais nuvens são híbridas Windows/Linux e não tem interface gráfica --- e a Microsoft investiu uma fortuna na mudança dos paradigmas de programação para a console. O modelo de programação recomendado agora é o de Virtual Terminal, alinhado com os tty do Linux. Pode ler algo em a nova console vs. o modelo classico. A ideía é chegar perto da portabilidade entre Linux e Windows. Recomendo ler se pretende escrever programas desse tipo

 

  • eu postei um programa aqui, aparentemente (pelo nome) em abril de '20 que você pode usar pra estar uma outra possibilidade, que não implica em mudar line input e coisas que podem interferir no IDE. Vou postar o código aqui de novo e sugiro que rode em sua máquina e entenda o mecanismo, que é a API de console do Windows afinal. Eis uns exemplos da tela do programa 
     

image.thumb.png.041a6754daceed5d970874a3559760d5.png

 

Nesse caso ao rodar o programa no Terminal e digitando ENTER clube ENTER você vê o que tem no buffer. SEM LER. O programa não lê nada.

 

 

 

 

 

E

 

 

image.thumb.png.ee3ff8fac8d557eb4220a29c4026d164.png

 

Os dados estão lá. Mas ao digitar '.' o programa limpa o buffer...

 

 

 

 

 

 

 

 

 

 

 

Seguindo 

image.thumb.png.9ca7188009240e98919fb3b5c91b9242.png

Digitando 4x a seta pra baixo, 4 backspace um ENTER e a seta para esquerda você vê que as setas não são capturadas desse modo, o backspace retorna 8 como esperado, o ENTER retorna 13 e assim por diante.

 

Programar com essas coisas é muito chato --- eu acho --- mas entenda que esse é um modo de fazer. Assim não reprograma a entrada porque é algo problemático se seu programa cancelar e deixar a entrada instável. O programa não para então atende o esperado. É trivial ler as setas em separado, as teclas de função, o shift esquerdo diferente do direito, saber se o cara digitou o + do teclado numérico ou do teclado comum, mas é chato. Para um outro exemplo veja o que eu escrevi nos exemplos de menus cujo link o @Lucca Rodriguesgentilmente postou acima.

 

Por favor não diga que o programa de exemplo parou :D Trata-se de outra função. Ele parou em outro ponto e não na leitura. E parou porque a ideia é essa: mostrar o buffer de stdin sem ler nada. Entenda que esse programa não tem sequer um read(), ou fgets() ou qualquer operação de entrada....

 

Eis o código

 

#include "_arfneto.h"

void prepara_tela();

int main(int argc, char** argv)
{
    struct _INPUT_RECORD linha[40];
    HANDLE H = GetStdHandle(STD_INPUT_HANDLE);
    prepara_tela();
    int N = 0;
    int antes = N;
    while (1)
    {
        char c = 0;
        PeekConsoleInput(H, linha, 40, (LPDWORD)&N);
        if (N != antes)
        {   // se mudou algo nessa leitura
            gotoYX(10, 20);
            for (int i = 0; i < N; i += 1)
            {
                if (linha[i].EventType == KEY_EVENT)
                {
                    if (linha[i].Event.KeyEvent.bKeyDown)
                    {
                        c = linha[i].Event.KeyEvent.uChar.AsciiChar;
                        if ((isprint(c)) && (c != ' '))
                            printf("%4c", c);
                        else
                            printf("%4d'", c);
                    };	// if
                    if (c == '.')
                    {
                        Beep(600, 50);
                        FlushConsoleInputBuffer(H);
                        prepara_tela();
                        c = 0;
                    }
                    else
                        if (c == '/')
                        {
                            printf("\n\n\n\n");
                            return 0;
                        }
                };
            };
        };
        gotoYX(11, 8);
        Sleep(1000);
    };  // while()
    return 0;
};

void prepara_tela()
{
    cls();
    gotoYX(12, 8);
    mensagem_em_video_reverso("'.' limpa tudo, '/' termina");
    gotoYX(10, 8);
    mensagem_em_video_reverso("Para ler:");
};
// fim

 

O exemplo é trivial. Essas funções são auto-explicativas:

  • cls() limpa a a tela do jeito oficial do Windows
  • gotoYX() posiciona o cursor do jeito simples, linha e coluna, e não do jeito chato cartesiano
  • mensagem_em_video_reverso() tem o cuidado de usar SUAS definições de cor para inverter o video e mostrar uma mensagem

 

Eis o header 

 

#pragma once

// define para as cores para simplificar
#define		_preto_            0
#define		_azul_             1
#define		_verde_            2
#define		_ciano_            3
#define		_vermelho_         4
#define		_magenta_          5
#define		_marron_           6
#define		_cinza_claro_      7
#define		_cinza_escuro_     8
#define		_azul_claro_       9
#define		_verde_claro_     10
#define		_ciano_claro_     11
#define		_vermelho_claro_  12
#define		_magenta_claro_   13
#define		_amarelo_         14
#define		_branco_          15

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

// funcoes para a console

int			cls();
void		gotoYX(int, int);
void		mensagem_em_video_reverso(char* mensagem);
void		text_color(int, int);

// fim de _arfneto.h

 

E as funções afinal

 

#pragma once
#include "_arfneto.h"

int		cls()
{	// limpa a tela no windows, do jeito oficial
    CONSOLE_SCREEN_BUFFER_INFO		info;
    HANDLE		H = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD		origem = { 0,0 };
    int			total;
    if (H == INVALID_HANDLE_VALUE) return -1;
    GetConsoleScreenBufferInfo(H, &info);
    int r = FillConsoleOutputCharacter(H, (TCHAR)' ',
        info.dwSize.X * info.dwSize.Y,
        origem, &total);
    int s = FillConsoleOutputAttribute(
        H, info.wAttributes,
        info.dwSize.X * info.dwSize.Y,
        origem, &total);
    SetConsoleCursorPosition(H, origem);
    return 0;
};	// end cls()

void	gotoYX(int linha, int coluna)
{
    static COORD	coord;
    HANDLE			H = GetStdHandle(STD_OUTPUT_HANDLE);
    coord.X = coluna; coord.Y = linha;
    SetConsoleCursorPosition(H, coord);
    return;
};	// gotoXY

void	mensagem_em_video_reverso(char* mensagem)
{
    HANDLE	H = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO		info;
    GetConsoleScreenBufferInfo(H, &info);
    WORD foreground = info.wAttributes & 0xF;
    WORD background = info.wAttributes & 0xF0;
    text_color(background, foreground);
    printf("%s", mensagem);
    text_color(foreground, background);
    return;
}	// mensagem_em_video_reverso()

void	text_color(int letras, int fundo)
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), letras | (fundo << 4));
}	// text_color

// fim de _arfneto.c

 

Claro, pode juntar em um só arquivo se preferir.

 

Pode servir para o seu caso. Em geral é o que eu recomendo e o que eu usava.

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário 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

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

 

GRÁTIS: ebook Redes Wi-Fi – 2ª Edição

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!