sábado, 24 de outubro de 2009

Imprimindo na tela (printf)

No último post da série sobre código fonte nós analisamos (pelo menos, tentamos analisar ;) linha a linha um típico código fonte em C. Eu deixei um desafio para que você descobrisse o que fazia o comando da linha 11 do código analisado, no caso, system ("pause"). Descobriu?
Antes que os mais puristas venham me criticar sobre o uso deste comando, já que existem outros que atingem o mesmo objetivo; e de forma melhor, já que não deixam o código fonte dependente do sistema (leia-se Rwindow$); deixem que eu me defenda. Também não gosto deste comando, mas utilizo-o por 2 motivos:
1 - É o comando ensinado pela maioria dos professores de C (não é verdade?);
2 - Como uso o Dev C++, estou seguindo a recomendação da página oficial desta IDE (duvida? veja aqui, em inglês).

Isto posto, vamos ao assunto de hoje.

O comando printf
Por falar no código fonte do post anterior, você deve lembrar que falamos que o comando printf (linha 10) era muito importante e iríamos fazer um post só para ele, não é? Pois bem, este é o post.

Imprimindo...
O comando printf foi criado para imprimir. Isso você já deve ter percebido, mas imprimir o que e onde?
É aí que entra você, programador (gostou de ser chamado de programador???)!
Quem diz o que e onde o printf vai imprimir é você. Como você vai fazer isto? É o que veremos agora. Vamos começar com o onde.

A saída padrão
O comando printf pode imprimir em qualquer lugar. Por qualquer lugar, entenda a tela, uma impressora, um arquivo (existe uma função especializada, construída a partir do printf, para imprimir em arquivos (fprintf)), etc.
O alvo da impressão feita pelo printf é a saída padrão (stdout) que, geralmente, é a tela (existem formas de alterar isto).

Impressão formatada
Certamente você já ouviu falar em formatar (dar formato, forma, organizar segundo um padrão). A impressão feita pelo printf é formatada, ou seja, tem uma forma definda.
A forma que esta impressão terá é definida pelos parametros que são passados à função printf. Estes parametros ficam entre os ( ), como já vimos antes.

Os parametros da printf
Seguindo a teoria de que é fazendo que se aprende, vamos ao Dev C++.
Abra o Dev C++, crie um novo Arquivo fonte e copie o código abaixo.

//Exemplo 01 de printf

#include <stdio.h>

#include "stdlib.h"


int main( )

{

printf("Ola mundo!");

system ("pause");

}



Este código simples, é a minha versão do "Hello world", o programa mais simples feito em C (o mais simples que exibe algo na tela).
Pode compilar e executar.
Reparou na saída?

Olá mundo!Pressione qualquer tecla para continuar. . .

Não era isto que queríamos escrever! Era para aparecer só o Ola mundo!.
A frase Pressione qualquer tecla para continuar. . . é impressa pelo system("pause"). Só que ela está 'colada' no texto que escrevemos. Não ficaria mais 'bonito' se esta frase ficasse numa linha separada do nosso texto? Para isto é só imprimir um "enter". E como se faz isso?
O printf consegue imprimir qualquer caracter imprimível (verdade?). E os não imprimíveis, como é o caso do "enter" que nós precisamos?

Imprimindo o que não pode ser impresso
Para resolver esta questão complicada, o C dispõe de um recurso simples. Qual recurso é este?
Os códigos de escape, também conhecidos como códigos de barra invertida.
O símbolo '\' é conhecido no C como símbolo de escape. Ele é usado para tornar possível a impressão de alguns caracteres de controle e alguns símbolos especiais do C.
Os mais usados estão nesta tabela:




Conforme podemos ver, o 'enter' que precisamos é o '\n'.
Quanto aos símbolos especiais, um exemplo é o caracter ". Este caracter delimita o que vai ser impresso pelo printf. Agora, imagine que você queira imprimir um texto que tenha um outro texto delimitado por ". Exemplo: ("alguma "coisa""). A forma correta seria ("alguma \"coisa\"").
Substitua o comando da linha nn por este:

printf("Alo mundo!\n");

Agora, recompile e execute.
Ficou 'bem melhor'.

Imprimindo valores desconhecidos
Salve o seu Arquivo fonte do exemplo anterior e crie um novo em branco.
Copie o código abaixo.


//Exemplo 2 de printf

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

int main( )
{
int num1=5, num2=2, resultado;



resultado=num1 / num2;
printf("O resultado da divisao de num1 por num2 eh igual a resultado.\n");
system ("pause");
}



Compile e execute.

Repare na saída. Você pode dizer, só olhando para a frase impressa, qual o valor armazenado nas variáveis? É bem capaz que você diga quais são os valores, porque viu no código fonte quais são eles. Sendo assim poderíamos substituir o comando da linha 9 por este:

printf("O resultado da divisao de 5 por 2 eh igual a 2.5.\n");

E se não tivesse nenhum valor no código fonte? Se tivéssemos dado ao usuário do programa a possibilidade de digitar os valores para a nossa soma (o que é muito comum)?
Entra em cena aqui mais um parametro de formatação da função printf, os especificadores de formato.
Os principais estão na tabela abaixo:




Agora que conhecemos este recurso do C, vamos corrigir o nosso código fonte para exibir os dados que desejamos mesmo sem saber o valor deles. É só substituir o comando da linha 9 por este:

printf("O resultado da divisao de %d por %d eh igual a %d.\n",num1, num2,resultado);

Note que usamos o especificador %d que imprime um número inteiro com sinal (poderíamos usar %i ou %u, já que nossos números são 'pequenos').
Além disto, o número de parametros do nosso comando printf ficou maior. Vejamos o porque disto.
Os especificadores de formato funcionam como comandos de substituição. Onde aparece o especificador de formato deverá ficar o valor que queremos mostrar; neste caso, o conteúdo das variáveis num1, num2 e resultado, exatamente nesta ordem. É importante observar isto, a ordem de impressão dos valores é a mesma em que eles são passados para a função printf.
Pode recompilar e executar.
Funcionou?
Na verdade não, correto? Nós sabemos que 5 dividido por 2 é igual a 2,5. E por que foi impresso 2?
A resposta é o tipo.
O tipo escolhido para a variável resultado foi int que armazena números inteiros. O resultado da divisão de um número inteiro por outro número inteiro na matemática nem sempre dá um número inteiro, veja o nosso exemplo. Para resolver isto temos que mudar o tipo da variável resultado para float.
Assim as linhas 6 e 7 ficarão assim:

int num1, num2;
float resultado;

Corrija, recompile e execute.
Além de não funcionar, agora ficou pior. Tá aparecendo que o resultado é 0 (zero). Por quê?
Porque não alteramos o printf, que deveria ficar assim:

printf("O resultado da divisao de %d por %d eh igual a %f.\n",num1, num2, resultado);

Novamente, recompile e execute.
Funcionou agora? Nada ainda, não é? Por quê?
Simples: Apesar da variável resultado ser do tipo float, o resultado da divisão continua dando um inteiro para o computador (ele não sabe das regras matemáticas!).
Temos duas soluções para isto (tem outra, mais veremos isso mais tarde):
A 1ª é mudar o tipo da variável num2 para float. A divisão de um número armazenado numa variável do tipo inteiro por outro número armazenado numa variável do tipo float tem como resultado um número com casas decimais (tipo float).
A 2ª é mais drástica. Mudar o tipo de todas as variáveis para float.

Tente a 1ª que já resolve. Não esqueça de mudar o comando printf da linha 9. Sabe como, certo?
Experimente esquecer e veja o que acontece.

Agora funcionou, não é?
Funcionar, funcionou; mas ainda não tá legal. Tá aparecendo um monte de zeros desnecessários.

Os especificadores de largura de campo
Dentre os parametros de formatação do printf, tem um que ainda não vimos que resolve isso. São os especificadores de largura de campo. Pode ficar tranquilo, que esses não precisam de tabela (rsrsrs).
Na verdade, os especificadores de largura de campo trabalham juntos com os especificadores de formato. É um número (e ponto) que fica entre o símbolo % e a letra do formato.
No nosso exemplo, para imprimir 2 e 2.5, usaríamos o seguinte especificador de largura de campo:

printf("O resultado da divisao de %d por %.0f eh igual a %.1f.\n",num1, num2, resultado);

Que significa que desejamos imprimir o valor armazenado na variável num2 com 0 (nenhuma) casas decimais, e o valor armazenado na variável resultado com uma casa decimal.

Corrija, recompile e execute (De novo! De novo!).

Finalmente, funcionou da forma esperada.

Agora você já pode fazer seus próprios teste.
Altere o nosso código fonte de exemplo para que ele faça a soma de 2 inteiros e exiba o resultado usando o comando abaixo:

printf("O resultado da soma de %03d + %3d eh igual a %d.\n",num1, num2, resultado);

Sabe explicar o que está acontecendo?

Nos 'vemos' no próximo post.

7 comentários:

  1. tenho um problema para impressao de strings. pode me add no msn: tbs_sales@hotmail.com

    ResponderExcluir
  2. vc falou q existe outros comandos que fazer a funçao do system ("pause"), qual é esse comando e qual biblioteca ele usa?

    ResponderExcluir
  3. Muito obrigado! Esclareceu perfeitamente minha dúvida!

    ResponderExcluir
  4. Este comentário foi removido pelo autor.

    ResponderExcluir
  5. A rotina printf e sem sombra de duvidas a mais usada da linguagem c, pois todo programa tem textos, pelo menos a grande maioria deles não acha, o printf esta armazenado na biblioteca stdio junto com o camando scanf
    Se você quiser ver um vídeo sobre o assunto sugiro este

    https://goo.gl/ODLKNM

    ResponderExcluir
  6. Como é que imprimo por exemplo todas as encomendas realizadas ?

    ResponderExcluir
  7. Imagine que após a implementação com sucesso da aplicação da aplicação de gestão da base de dados de fornecedores e produtos foi contratado para implementar o módulo de gestão de encomendas a fornecedores. Para isso terá de guardar a informação relativa às encomendas (idEncomenda (nº entre 1 e 1000), idFornecedor (nº entre 1 e 1000), idProduto (nº entre 1 e 1000), quantidade (nº entre 1 e 1000), preço (nº entre 0 e 1000,00) e a data da encomenda (10 carateres)).


    Crie uma função/método que apresente no ecrã todas as encomendas realizadas de um determinado produto.

    ResponderExcluir