Jump to content
Localhost

Buffering

Recommended Posts

Localhost

Alguém me pode explicar por que razão é que quando compilo e executo o código que vou mostrar a seguir o programa espera os 3 segundos (das 3 iterações do ciclo) e só depois executa a função putchar?

#include <stdio.h>

int main (void)
{
    int k = 0;

    for (k = 0; k < 3; k++)
    {
 putchar ('.');
 sleep (1);
    }

    return 0;
}

Em vez de ir imprimindo e esperando 1 segundo, o programa espera 3 segundos e só depois imprime 3 pontos.


here since 2009

Share this post


Link to post
Share on other sites
Localhost

Não cheguei a entender a razão pela qual isto acontece. O que é que acontece exactamente no buffer?


here since 2009

Share this post


Link to post
Share on other sites
Baderous

O stdout é uma stream que tem um buffer associado (assim como o stdin e o stderr) para o qual se escrevem os caracteres que vão ser apresentados ao utilizador. O buffer pode ter 3 modos de acção diferentes para o caso do output:

1) No buffering, isto é, mal os dados estejam disponíveis na stream são apresentados ao utilizador;

2) Line-buffering, isto é, os dados são escritos para um buffer e só são apresentados quando é escrito um newline ou quando é atingido o tamanho máximo do buffer, sendo o buffer limpo;

3) Full-buffered, isto é, os dados são escritos para um buffer e são apresentados quando o buffer enche, sendo depois limpo.

Este comportamento é dependente da implementação, mas em Unix, o stdout é line-buffered, daí que siga o comportamento descrito. Ou seja, no teu caso, os caracteres estão a ser colocados no buffer e só são despejados quando o programa termina (quando termina, todos os buffers são flushed).

Existem várias formas de despejar o conteúdo no caso dos buffers line-buffered:

1) Fazer print para o stderr que é unbuffered;

2) Fazer print do caracter de mudança de linha;

3) Obrigar a despejar o conteúdo, fazendo um flush: fflush(stdout);

4) Tornar o stdout unbuffered: setvbuf (stdout, NULL, _IONBF, BUFSIZ);

Why does printf not flush after the call unless a newline is in the format string? (in C)

setvbuf

Share this post


Link to post
Share on other sites
Localhost

Excelente explicação. Obrigado.

Já agora, qual é o tipo de buffering que o stdin apresenta? E quando o mesmo é flushed o seu conteúdo vai para onde?

EDIT:

Vou mudar o título do tópico.


here since 2009

Share this post


Link to post
Share on other sites
brunoais

Já agora, qual é o tipo de buffering que o stdin apresenta? E quando o mesmo é flushed o seu conteúdo vai para onde?

A minha experiencia diz que não é suposto usar o fflush() para o stdin porque n faz sentido segundo o conceito da função.

O stdin é full-buffered. Guarda tudo até se ir buscar ou até ficar cheio. Mas não sei o que acontece qd fica cheio pq nunca experimentei.


"[Os jovens da actual geração]não lêem porque não envolve um telecomando que dê para mirar e atirar, não falam porque a trapalhice é rainha e o calão é rei" autor: thoga31

Life is a genetically transmitted disease, induced by sex, with death rate of 100%.

Share this post


Link to post
Share on other sites
Localhost

Full-buffered?

Sim, o comportamento da função fflush é indefinido para buffers de input.


here since 2009

Share this post


Link to post
Share on other sites
brunoais

Full-buffered?

Sim (troquei-me, sorry)


"[Os jovens da actual geração]não lêem porque não envolve um telecomando que dê para mirar e atirar, não falam porque a trapalhice é rainha e o calão é rei" autor: thoga31

Life is a genetically transmitted disease, induced by sex, with death rate of 100%.

Share this post


Link to post
Share on other sites
Baderous

O stdin não é full-buffered, é line-buffered, porque está associado a um terminal, por omissão (em sistemas Unix). Por exemplo, quando estamos a fazer um scanf (que está a colocar os dados no buffer do stdin), quando carregamos no ENTER ('\n'), o buffer é despejado. Só é full-buffered se tiver sido redireccionado para apontar, por exemplo, para um ficheiro de onde vão ser lidos os caracteres.

Share this post


Link to post
Share on other sites
Baderous

Quando se carrega no ENTER, os valores são atribuídos às variáveis, num scanf. Até lá estão no buffer.

Share this post


Link to post
Share on other sites
Localhost

E quando lemos strings o scanf não inclui o \n no buffer? É por essa razão que o buffer fica cheio?


here since 2009

Share this post


Link to post
Share on other sites
Baderous

Quando lês uma string com scanf, o '\n' fica no buffer, é por isso que surgem muitas vezes aqui questões a dizer que o programa avança uma instrução de leitura inesperadamente. O buffer que em que estás a pensar quando dizes que "fica cheio" não é o buffer associado às streams, mas o "buffer" para onde vão ser guardados os caracteres aquando da leitura da string, ou seja, a própria string alocada na stack. O tamanho do buffer associado às streams é dado pela macro BUFSIZ, a qual deve ter, no mínimo, um tamanho de 256. No GCC, é usado o valor 1024.

Share this post


Link to post
Share on other sites
Localhost

Então por que é que essa tal instrução de leitura é "avançada"?

Vê se este código e os seus respectivos comentários estão correctos:

scanf ("%s", buf1); // lê para a string buf1 e deixa no buffer associado à stream o \n
scanf ("%s", buf2); // como encontra o \n no buffer associado à stream, esse mesmo buffer é flushed e buf2 vai receber o que se encontra na stream
// neste caso é, então, lido o despejo da stream


here since 2009

Share this post


Link to post
Share on other sites
Baderous

Deixa-me clarificar aqui uma coisa que acho que é isso que está a fazer confusão. Quando falo no flush do stdout refiro-me a despejar todo o seu conteúdo para o terminal. No caso do stdin, é feito o flush do conteúdo para as variáveis em questão, ele não é completamente despejado, daí os problemas do "tens de limpar o buffer de entrada porque tem lá lixo". Ele é despejado quando recebe um '\n' por parte do utilizador.

Para esse caso, a tua interpretação do 1º scanf está correcta. No 2º caso, o '\n' já estava lá, não foi introduzido na nova leitura, logo o flush só acontece quando lá colocas um novo '\n', desde que este seja precedido por caracteres non-whitespace:

Whitespace character: the function will read and ignore any whitespace characters (this includes blank spaces and the newline and tab characters) which are encountered before the next non-whitespace character. This includes any quantity of whitespace characters, or none.

Se testares isso, se na 2ª leitura só fizeres ENTER, ele não sai da função enquanto não colocares lá outro caracter e deres o ENTER.

Share this post


Link to post
Share on other sites
Localhost

O que eu queria dizer era que como na 2ª leitura vai encontrar o \n na stream vai logo passar à próxima instrução. Certo?

O pequeno "hack" do espaço em branco é justamente não permitir que passe logo à próxima instrução sem ler apenas o \n. Certo?


here since 2009

Share this post


Link to post
Share on other sites
Baderous

O que eu queria dizer era que como na 2ª leitura vai encontrar o \n na stream vai logo passar à próxima instrução. Certo?

O pequeno "hack" do espaço em branco é justamente não permitir que passe logo à próxima instrução sem ler apenas o \n. Certo?

Não estou certo do que vou dizer, mas penso que o passar à próxima instrução devido à existência de um '\n' no buffer acontece em todos os casos de leitura (ints, chars, etc), excepto para as strings. O hack do espaço em branco permite ignorar o '\n' nesses tais casos. Mas isto já não tenho a certeza, pelos testes que fiz parece-me ser esse o comportamento.

Share this post


Link to post
Share on other sites
daj

Considerando o texto http://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html

Input white-space characters (as specified by isspace ) shall be skipped, unless the conversion specification includes a [ , c , C , or n conversion specifier.
s: Matches a sequence of bytes that are not white-space characters.

e o código:

scanf("%s", buf1);
scanf("%c", &c1);
scanf("%s", buf2);
scanf("%c", &c2);
scanf("%s", buf3);

A execução será:

scanf("%s", buf1): O buffer não tem dados por isso é pedida uma linha ao utilizador. A função fica bloqueada até receber um enter e o conteúdo dessa linha é colocado no buffer. Suponha-se que o utilizador insere "aaa<space><space><space>bbb<enter>". É lido "aaa" para o buf1 porque a seguir vem um "white-space character" (<space>, <tab>, <newline>, <vertical-tab> ou <form-feed> - neste caso é um espaço). Buffer fica com "<space><space><space>bbb<enter>".

scanf("%c", &c1): Existem dados no buffer por isso não é pedido nada ao utilizador. É lido "<space>" para c1. Buffer fica com "<space><space>bbb<enter>".

scanf("%s", buf2): Ainda existem dados. Sendo pedida uma string, são despachados os white-space characters do início (ver primeira citação). Buffer fica com "bbb<enter>". É lido "bbb" para buf2 porque a seguir vem um white-space character (neste caso, um enter). Buffer fica com "<enter>".

scanf("%c", &c2): Ainda existem dados. É lido "<enter>" para c2. Buffer fica vazio.

scanf("%s", buf3): Buffer está vazio, por isso é pedida uma nova linha ao utilizador. Mesmo comportamento que o primeiro scanf.

Estou correcto na minha análise e, se sim, ajudou alguém?

Share this post


Link to post
Share on other sites
Localhost

@daj: já pensaste que ele pode ler tudo para o stdin e só depois (quando é despejado) fazer um género de uma filtragem dos dados? Ou seja, lê tudo para o buffer e depois quando encontra o \n é despejado e "envia" para a variável tudo menos espaços em branco, etc. e deixa também no buffer o \n.

Podemos tirar daqui muitas teorias mas não estou a encontrar nenhuma conclusiva.


here since 2009

Share this post


Link to post
Share on other sites
Baderous

O que o daj exemplificou vai de encontro ao que eu tinha dito e que penso ser a explicação correcta.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.