Curso de C - 5. Entrada e Saída pelo Console
(2608 total de palavras neste texto) (5742 vizualização(ões)) 
Curso de C - 5. Entrada e Saída pelo Console
Nesta aula vamos aprender a criar programas que interagem com o usuário, lendo dados do teclado e escrevendo dados no monitor de
vídeo. Algumas funções já foram usadas e comentadas superficialmente, mas agora veremos estas mesmas a fundo.
O interessante em C é que as funções de entrada e saída, tanto pelo console como por arquivos, não são definidas como palavras-chave
da linguagem. Todo sistema de entrada e saída em C é definido por bibliotecas, o que torna a linguagem bastante flexível neste
aspecto.
Existem várias funções de entrada e saída pelo console que são exclusivas de um compilador ou de uma plataforma. Estas não serão
vistas aqui, por não fazerem parte do padrão C ANSI. Por exemplo, as funções de entrada e saída da biblioteca ncurses, as funções
da biblioteca conio.h da Borland, etc.
Desta aula em diante, os exercícios serão programas que vocês criarão com os recursos aprendidos até aqui.
5.1. Lendo e escrevendo caracteres
A função getchar() é usada para ler caracteres, um por vez, da entrada padrão.
Forma geral da função getchar():
#include <stdio.h>
int main()
{
char ch;
ch= getchar();
return 0;
}
Quando executamos o programa acima, a função getchar() obtém o próximo caractere da entrada padrão, só terminando a leitura após
o pressionamento da tecla ENTER. É importante lembrar que getchar() lê um caractere por vez, não sendo usado para ler cadeias de
caracteres (strings).
Existe um problema com getchar(): quando esta função efetua a leitura, fica uma "sujeira" no buffer de entrada, o que pode
atrapalhar e resultar em erros no programa. Uma forma de contornar isso é colocar, após a leitura, o próprio comando getchar():
#include <stdio.h>
int main()
{
char ch;
ch= getchar();
getchar(); /* isto limpa o buffer de entrada, eliminando as "sujeiras" deixadas pela leitura. */
return 0;
}
Em um programa simples como esse não dá para ver o problema, mas se usar getchar() em um loop, por exemplo, será visível.
Para imprimir um caractere por vez, temos a função putchar().
Forma geral da função putchar():
#include <stdio.h>
int main()
{
char ch;
printf("Digite um caractere: ");
ch= getchar();
putchar(ch);
return 0;
}
O programa acima efetuará a leitura do caractere e imediatamente o imprimirá, tendo a seguinte saída:
$ Digite um caractere: a
$ a
Assim como getchar(), putchar() imprime um caractere somente por vez, não sendo possível usá-lo para imprimir strings.
5.2. Escrevendo com printf()
Como já vimos em vários exemplos, a função printf() é uma função genérica de impressão. Pode-se imprimir na tela qualquer tipo de
dados, controlar o tamanho do campo de impressão, número de casas decimais, etc.
A forma geral da função printf() consiste em uma string de controle e uma lista de argumentos (que podem ser variáveis, expressões,
etc.). No caso de imprimir somente uma string, não é necessário argumentos (da mesmo forma que fizemos na aula 2).
Forma geral de printf():
printf("string de controle", argumentos);
A string de controle pode consistir de dois itens: o texto a ser exibido e comandos de controle que definem o formato do(s) tipo(s)
a ser(em) impresso(s). Vamos ver alguns exemplos:
printf("Inteiro com sinal: %d", num);
printf("Caractere: %c", ch);
printf("String: %s", str);
printf("Ponto flutuante decimal: %f", fp);
printf("Ponto flutuante em notação científica: %e", fp);
Os argumentos num, ch, str e fp são variáveis. Os caracteres %d, %c, %s, etc, são os comandos de controle
para cada tipo específico. Veja a seguir uma tabela desses comandos de controle:
| Comando |
Tipo a ser impresso |
| %c |
caractere |
| %d ou %i |
inteiro com sinal |
| %u |
inteiro sem sinal |
| %ld |
decimal longo |
| %x ou %X |
hexadecimal |
| %o |
octal |
| %f |
ponto flutuante decimal |
| %lf |
ponto flutuante longo (double) |
| %e ou %E |
ponto flutuante notação científica |
| %g ou %G |
usa %e ou %E ou %f, o que for mais curto |
| %n |
escreve o número de caracteres impresso até esse ponto |
| %p |
escreve um endereço |
| %s |
escreve uma string |
| %% |
escreve o caractere % |
Como vocês já viram em vários exemplos, para imprimir uma variável usando printf() basta colocar o comando de controle referente ao
tipo no lugar a ser impresso, e colocar a variável correspondente na lista de argumentos. Veja este programinha de exemplo:
#include <stdio.h>
int main()
{
int num= 10;
float fp= 3.28;
char ch= 'a';
char str[20]= "linuxdicas";
printf("%d %f %c %s", num, fp, ch, str);
return 0;
}
A ordem dos comandos de controle deve ser a mesma dos argumentos. No exemplo acima, na lista de argumentos, deve-se colocar a
variável inteira primeiro, porque %d aparece primeiro; a variável ponto flutuante em segundo, porque %f aparece em segundo, e assim
por diante.
Você pode usar também alguns caracteres especiais na string de controle. Estes caracteres auxiliam na formatação da impressão. Já
vimos o caractere de controle \n, que "pula" uma linha. Vamos ver mais alguns:
| Caractere |
Função |
| \n |
pular linha |
| \t |
tabulação |
| \b |
retrocesso |
| \r |
retorno do cursor |
| \" |
imprimir aspas |
| \\ |
imprimir barra |
| \0 |
nulo |
Faça testes com os programas já vistos até aqui usando estes caracteres. Treine bastante...
Especificador de Precisão
Você deve ter visto que ao imprimir um float, imprime-se várias casas decimais, muitas vezes até desnecessárias. Você pode
especificar o tamanho da casa decimal utilizando o especificador de precisão de printf(). Basta colocar, entre o % e o f,
um ponto (.) e o número de casas. Assim:
float fp= 3.15;
printf("%.2f", fp);
Neste exemplo, será impresso 3.15, e não 3.150000, porque você especificou que o printf() deveria imprimir 2 casas decimais
de precisão.
Este mesmo especificador pode ser usado em outros tipos de dados. Em uma string, determina-se o comprimento mínimo e máximo do campo
a ser impresso:
char str[20]= "linuxdicas";
printf("%3.5s", str);
Neste exemplo será impressa um string de no mínimo 3 e no máximo 5 caracteres.
Em inteiros, o especificador determina o número mínimo de dígitos, completando com zeros à esquerda quando necessário:
int num= 800;
printf("%.8d", num);
No exemplo acima, será impresso 00000800 (8 dígitos).
Especificador de Largura Mínima
De forma semelhante ao especificador de precisão, o especificador de largura mínima adiciona espaços ao campo para assegurar que
atinja o comprimento mínimo. Se a largura especificada for menor que o tamanho do número, nada será feito (o número será impresso
normalmente).
Para especificar a largura do campo de impressão, basta colocar um número entre o % e o comando de formato. Exemplo:
int x, y, z;
x= 20;
y= 200;
z= 2000;
printf("%5d %5d %5d\n", x, y, z);
printf("%5d %5d %5d\n", y, z, x);
Imprimiria:
20 200 2000
200 2000 20
Como você pode ver, a saída é toda alinhada, ou justificada, à direita. Você pode justificar à esquerda colocando um sinal de menos
(-) antes do número que especifica a largura do campo.
printf("%-5d", num);
Isto imprimiria um inteiro, com 5 dígitos no mínimo e justificado à esquerda.
Você está INTIMADO a refazer os exemplos das aulas anteriores, usando os modificadores e códigos de formatação vistos nesta aula. Só
assim, na prática, você aprenderá.
5.3. Lendo com scanf()
De modo semelhante a printf(), a função scanf() é a função genérica de leitura em C, podendo ler praticamente qualquer tipo de
dados primitivo. Sua sintaxe é parecida com printf(), onde há uma string de controle e uma lista de argumentos (variáveis). Uma
diferença importante é que scanf() recebe o endereço das variáveis nos argumentos, e não o valor, como em printf(). Vamos ver um
exemplo:
int num;
scanf("%d",&num);
Percebeu a diferença? Aqui a função scanf() está lendo um inteiro do teclado e armazenando em uma variável inteira num. Porque
esse & antes de num? Ele especifica que está sendo passado o endereço da variável para a função. Essa é uma forma de
passagem de parâmetros por referência, e veremos com detalhes na aula de funções. Nesta aula, basta você saber que esse & é
necessário antes das variáveis na qual scanf() armazenará o valor lido.
Os comandos de controle são praticamente os mesmos de printf():
| Comando |
Função |
| %c |
lê UM caractere |
| %d |
lê um inteiro com sinal |
| %ld |
lê um inteiro longo com sinal |
| %u |
lê um inteiro sem sinal |
| %lu |
lê um inteiro longo sem sinal |
| %x |
lê um número hexadecimal |
| %lx |
lê um número hexadecimal longo |
| %o |
lê um número octal |
| %lo |
lê um número octal longo |
| %f, %g ou %e |
lê um ponto flutuante |
| %p |
lê um ponteiro |
| %s |
lê uma string |
| %n |
atribui o número de caracteres lidos até este ponto à variável correspondente |
| %[] |
busca por um conjunto de caracteres |
Pode-se usar os caracteres especiais de controle (\n, \t, etc) para auxiliar na formatação, da mesma forma que printf().
Agora, vários exemplos. Supondo que a variável p é do tipo correspondente em cada caso:
Lendo um caractere:
scanf("%c", &p);
Lendo um inteiro com sinal:
scanf("%d", &p);
Lendo um inteiro sem sinal:
scanf("%u", &p);
Lendo um hexadecimal:
scanf("%x", &p);
Lendo um octal:
scanf("%o", &p);
Lendo um número em ponto flutuante:
scanf("%f", &p);
ou
scanf("%e", &p);
ou
scanf("%g", &p);
Lendo um ponteiro:
scanf("%p", &p);
Lendo uma string:
char p[20];
scanf("%s", p);
Você deve estar pensando: pô, o cara esqueceu de digitar o & no último exemplo (strings)... A leitura de strings é uma excessão
onde não se deve colocar o & antes da variável. Isto porque quando se declara uma string (um vetor de caracteres) a variável
sozinha já é um ponteiro para o início da string na memória (isto é, já é um endereço, não precisando colocar o & antes). Calma,
veremos nesta aula mesmo que scanf() não é a função ideal para a leitura de strings, e aprenderemos que fgets() é uma boa
escolha. Veremos mais também sobre endereços na aula de ponteiros.
O comando de controle %n faz o seguinte: ele atribui à variável associada o número de caracteres lidos até aquele ponto.
scanf("%d%n", &p, &num);
Suponhamos que você digite 1234. A variável p armazenará 1234 e a variável num armazenará 4 (1234 possui 4 caracteres).
Scanset
Um scanset funciona como um leitor seletivo de strings: ele só lê os caracteres que estão definidos no scanset e, quando encontra
um caractere que não está definido neste conjunto, ele pára a leitura ali, ignorando os caracteres restantes.
Como um exemplo vale mais que mil palavras, vejam este:
#include <stdio.h>
/* Teste do scanset */
int main()
{
int num;
char s1[20], s2[20];
scanf("%d%[aeiou]%s", &num, s1, s2);
printf("%d %s %s", num, s1, s2);
return 0;
}
Compile e execute esse programa. Digite como entrada 915eiammst.
A saída será:
915 eia mmst
Perceba que a variável s1 só armazenou os caracteres que estavam definidos no scanset. Os restantes (mmst) foram atribuídos a
s2.
Existe a versão "negada" do scanset, onde tudo pode ser lido, menos o que está no conjunto. Basta começar a lista com um ^:
scanf("%[^A-Z]", str);
Na linha acima, matamos três coelhos com uma cajadada só:
1) Neste exemplo, todos os caracteres são aceitos, menos os maiúsculos (A-Z);
2) Também podemos ver que um scanset aceita uma faixa de caracteres (A-Z, a-z, 0-9, etc);
3) De quebra, podemos sacar que, como a linguagem C, um scanset diferencia MAIÚSCULAS de minúsculas.
Modificadores de Formato
scanf() também possui modificadores de formato, como em printf(). No caso de ler uma string, podemos fazer que uma variável leia
somente até um determinado número de caracteres. Isto ajuda evitar um vazamento de memória, que poderia comprometer a segurança do
seu programa.
Para especificar o tamanho do campo a ser lido, basta colocar o número entre o % e o código de controle. Por exemplo: suponhamos
que no trecho abaixo
char str[15];
scanf("%s", str);
o usuário digite otorrinolaringologista. Isso fatalmente causaria um erro.
Utilizando o modificador de formato:
char str[15];
scanf("%14s", str);
Mesmo que o usuário digite otorrinolaringologista, só seriam armazenados os 14 primeiros caracteres, ou seja, otorrinolaring.
Os outros caracteres ficariam na memória, e se houver outra chamada a scanf(), os caracteres que não foram lidos ("ologista") serão
atribuídos a variável.
Controlando a entrada
Você pode também especificar que determinado campo será lido, mas não armazenado. Basta colocar um * entre o % e o código do formato.
Por exemplo:
scanf("%c%*c%c", &ch1, &ch2);
Se você digitar a-c, o hífen (-) será lido, mas não será atribuído a nenhuma variável.
Um problema com scanf()
Agora pode ser que não seja notado, mas scanf(), em alguns compiladores ou sistemas, pode deixar algum "vestígio" no buffer de
entrada, o que atrapalharia uma eventual próxima leitura. Isto é notado principalmente dentro de loops, lendo várias vezes seguidas.
Lembra que getchar() sofria do mesmo mal? Então, o remédio é o mesmo... Basta colocar um getchar() solitário após a leitura com
scanf() que o buffer é limpo. Assim:
scanf("%d", &num);
getchar(); /* o faxineiro getchar() limpa a sujeira do scanf() */
Ufa!! Chega de scanf()... Vamos ver agora as funções de leitura de strings.
5.4. Lendo e escrevendo strings
Nós vimos que scanf() é uma função para leitura de praticamente todos os tipos primitivos, até de strings. Mas existem funções de
leitura e escrita de strings que facilitam a vida do programador e tornam o código mais seguro. Veremos a leitura com gets(), e
porque não usá-la; leitura com fgets(), e porque usá-la; e a escrita com puts(),
Lendo com gets() e fgets()
Ler uma string com gets é muito fácil:
char str[20];
gets(str);
Como você viu, a função gets tem um único argumento, a variável string. Mas essa facilidade tem um preço. gets() é considerada uma
função insegura, pois não controla se o usuário vai digitar além do que cabe na string. No exemplo acima, se o usuário digitar
uma string de mais de 20 caracteres, a leitura é feita, mas ocorre um vazamento de memória, que pode ter graves consequências. O
próprio compilador gcc "desencoraja" o uso da função gets(), com a seguinte mensagem:
the 'gets' function is dangerous and should not be used.
Em contrapartida, temos a "salvadora da pátria": a função fgets(). Você, que já conhece C, me diria: "Mas essa função não serve
para ler de um arquivo?". Na teoria sim. Mas em C, temos 3 arquivos especiais que são abertos quando um programa em C é
executado: stdin, stdout e stderr.
No caso da função fgets(), é a stdin que nos interessa. Esse arquivo especial refere-se a entrada padrão (o teclado).
Portanto, é esse o "arquivo" que fgets() usará para ler strings do teclado.
A forma geral da função fgets() é a seguinte:
fgets(char *str, int tamanho, FILE *fp);
Portanto, para poder ler uma string fazemos assim:
#include <stdio.h>
int main()
{
char str[20];
fgets(str, 19, stdin);
puts(str);
return 0;
}
Neste caso, se você digitar otorrinolaringologista, fgets() armazenará somente otorrinolaringolog, ignorando o resto. Isto
porque você especificou que fgets() só poderia ler uma string de no máximo 19 caracteres. Viu a vantagem? Tente fazer você mesmo e
teste com várias entradas.
Escrevendo com puts()
Escrever strings com puts é muito fácil... puts() só possui um argumento, a própria string:
char str[20]= "linuxdicas";
puts(str);
Simples, fácil e rápido...
OBS: uma string em C é um vetor (ou matriz) de caracteres. Nós estudaremos sobre matrizes e vetores em breve, além de estudar as
funções de manipulação de strings.
Nesta aula você aprendeu:
- como ler caracteres com getchar() e escrever caracteres com putchar();
- as vantagens e desvantagens de getchar() e putchar();
- como escrever com printf(), suas vantagens e desvantagens;
- como ler com scanf(), suas vantagens e desvantagens;
- como ler strings com gets() e fgets();
- porque não usar gets();
- porque usar fgets();
- como escrever strings com puts();
Na próxima aula veremos as estruturas de decisão de C (if..else, switch, etc);
=== Exercícios ===
1) Escreva um programa que leia 3 números inteiros, calcule a média aritmética e escreva a resposta para o usuário.
2) Faça um programa que, dado o valor de uma compra, calcule o ICMS a ser pago; considere uma taxa de 12%.
3) Dado um número com 3 algarismos, faça um programa para inverter a ordem de seus algarismos.
4) Dado um valor em anos, faça um programa que converta para segundos.
Powered by txt2tags
|