Provavelmente seu professor não explicou isso direito pra você!

Algumas pessoas me veem como um dinossauro da programação. Afinal, um sujeito que ainda trabalha com C e Assembly, ora bolas, não pode ser bom da cabeça! Aliás, sou tão jurássico que não me lembro se já expliquei para os novatos o que vai abaixo… Então, perdoem minha senilidade precoce (só tenho 45 anos, ora bolas!) e vamos lá…

Há alguns (muitos) anos, quando fiz faculdade, tive um professor de “Linguagem C” e, felizmente, pra mim, eu tinha bem mais experiência no assunto do que ele. Eis um problema que deixava o sujeito encabulado e, se você também passou por isso, provavelmente também achou estranho… Vocẽ já deve ter topado com um programinha assim:

#include <stdio.h>

#define MAX_NAME_SIZE 128

int main(int argc, char *argv[])
{
  char nome[MAX_NAME_SIZE];

  printf("Digite um nome: ");
  scanf("%s", nome);

  printf("Seu nome é \"%s\".\n", nome);

  return 0;
}

O ‘surpreendente’ é que o programinha acima não se comporta como o seu criador espera. Invariavelmente acontece o que se segue:

$ ./teste
O programa fica esperando pela entrada aqui...
Fred<ENTER>
Digite um nome: Seu nome é "Fred".
$

O texto em azul foi a string que eu digitei. Notou que o primeiro printf() só imprime depois que o usuário digitou a string? É como se ambos os printf tivessem sido executados depois do scanf()!!

O motivo disso é que existem pelo menos 3 streams default disponíveis para qualquer programa: stdin, stdout e stderr. Os dois primeiros são bufferizados (se é que essa palavra existe em português!). Isso quer dizer que tudo aquilo que você digita ou escreve vai para um buffer antes que seja disponibilizado para a função (no caso de scanf()) ou ser escrito na tela (no caso de printf()). Este buffer só é ‘descarregado’, automaticamente, em duas condições: Quando estiver cheio, ou quando um caracter ‘\n’ (new-line) for recebido. É dito então que stdin e stdout são “bufferizados por linha”.

A string no primeiro printf não contém um ‘\n’ no final e é pequena o suficiente para caber inteiramente no buffer de stdout. A string, no segundo printf, contém um ‘\n’ no final, causando a ‘descarga’ do conteúdo do buffer para a tela.

Como resolver este ‘problema’? Existem algumas maneiras:

  • Mude o comportamento do stream stdout:

Você pode dizer a um stream (seja este um dos default ou um criado com a função fopen()) que quer mudar o tamanho do buffer. Isso é feito com a função setvbuf(). Com essa mesma função você pode dizer ao stream que ele não deve usar um buffer. Assim:

setvbuf(stdout, NULL, _IONBF, 0);

Assim, stdout não tem mais um buffer (NULL), este “buffer” tem tamanho zero e o flag _IONBF diz: “NÂO USE UM BUFFER”.

Dai pra frente, qualquer manipulação do stream stdout será não-bufferizada.

  • Ou, você mesmo pode descarregar o buffer:

Você pode querer manter a característica bufferizada de stdout e apenas em alguns casos ‘descarregar’ o buffer para a tela. Para isso existe a função fflush(). “Flush”, em inglês, significa “descarrga”. Depois do primeiro printf() você pode chamar:

fflush(stdout);

O stream stderr, por default, é não-bufferizado.

Qual é o tamanho desses buffers? Depende da implementação… mas, existe uma constante definida em stdio.h, chamada BUFSIZ. No Linux (pelo menos aqui no meu) ela é definida como 8192 [bytes].

Anúncios

5 comentários sobre “Provavelmente seu professor não explicou isso direito pra você!

  1. São aspectos interessantes e tão clássicos quanto usar o ed para editar os fontes.

    Ficar imaginando o que é um buffer quando não se sabe ainda o que é um if?

    É bem compreensível que literalmente nínguem da “nova geração” programe em “C” ou use linux.

    Requer “miolo”.

    Se um destes novatos passasse uma semana usando o ed, talvez entendessem o que seria um “sed”, um buffer, modo ‘vi’sual, um arquivo “no disco”, fork, spawn, re, etc.

    É por estas e outras que eu recomendo o Python para fazer estas “coisinhas”, em vez de se usar “C”. Os resultados aparecem e o tempo livre pode ser usado para estudar mais.

    marz@pistache:~/Desktop$ python
    Python 2.7.3 (default, Mar 13 2014, 11:03:55)
    [GCC 4.7.2] on linux2
    Type “help”, “copyright”, “credits” or “license” for more information.
    >>> n=raw_input(‘Nome:’)
    Nome:marz
    >>> print n
    marz
    >>>

    {}’s
    MaRZ

    PS: continue programando em “C”, por favor! Volta e meia eu leio e compilo seus artigos, reflito sobre os seus objetivos e sempre chego a conclusão que é didática. Melhor, autodidática!

    1. Sim… È o jeito mais fácil, mas imagine que o sujeito queira um prompt:

      “Digite seu nome: █”

      Não dá para colocar um ‘\n’ e continuar na sequẽncia, dá? ;)

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s