Nós vimos uma forma de imprimir dados na tela do usuário utilizando a função printf(), e vimos que essa função recebe dois argumentos de entrada:
Forma básica da função printf(): printf("%tipo_de_saída", lista_de_variáveis). Vimos também que quando queremos escrever dados formatados na tela, usamos a forma geral da função, a qual possui os tipos de saída ou especificadores de tipo. Esses tipos especificam o formato de saída dos dados escritos pela função printf(). Cada tipo de saída é precedido por um sinal de %, e um tipo de saída deve ser especificado para cada variável a ser escrita. A função printf() pode ser usada para escrever praticamente qualquer tipo de dado. A tabela a seguir mostra alguns dos tipos suportados pela linguagem:
Alguns tipos de saída | |
---|---|
%c | escrita de um caractere (char) |
%d ou %i | escrita de números inteiros (int ou char) |
%u | escrita de números inteiros sem sinal (unsigned) |
%f | escrita de números reais (float ou double) |
%s | escrita de vários caracteres (string) |
%p | escrita de um endereço de memória |
%e ou %E | escrita em notação científica |
A função printf() aceita textos junto aos tipos de saída. Pode-se adicionar texto antes, depois ou entre dois ou mais tipos de saída:
1 #include <stdio.h>
2
3 int main(void) {
4
5 int x = 10;
6 printf("Total = %d\n", x);
7 printf("%d caixas\n", x);
8 printf("Total de %d caixas\n", x);
9
10 return 0;
11
12 }
Ao executar o programa, a saída que obtemos é a seguinte:
Isso permite que o comando printf() seja usado para escrever não apenas dados, mas sentenças que façam sentido para o usuário do programa.
Além de usarmos função printf(), podemos escrever dados na tela com a função putchar() (put character), que permite escrever um único caractere na tela. Sua forma geral corresponde:
int putchar(int char)
A função putchar() recebe como argumento de entrada um único valor inteiro. Esse valor será convertido em caractere e mostrado na tela. A função retorna:
Abaixo, alguns exemplos demonstrando a função putchar():
1 #include <stdio.h>
2
3 int main(void) {
4
5 char c = 'a';
6 int x = 65;
7 putchar(c); // Escreve o caractere 'a'
8 putchar('\n'); // Muda de linha
9 putchar(x); // Escreve o valor 65 como caractere
10 putchar('\n'); // Muda de linha
11
12 return 0;
13
14 }
Perceba, no exemplo anterior, que a conversão na linguagem C é direta no momento da impressão, ou seja, o valor 65 é convertido no caractere ASCII correspondente, no caso, o caractere "a". Além disso, o comando putchar() também aceita o uso de sequência de escape, como o caractere "\n" (nova linha).
Ao executar o programa, a saída que obtemos é a seguinte:
A função scanf() é uma das funções de entrada/leitura de dados da linguagem C. Seu nome vem da expressão em inglês scan formatted (leitura formatada). Basicamente, a função scanf() lê do teclado um conjunto de valores, caracteres e/ou sequência de caracteres conforme o formato especificado. Sua forma geral corresponde:
int scanf(const char *format, ...)
Na assinatura da função, observamos que um valor inteiro é retornado (o número de valores lidos) e há parâmetros definidos: o primeiro const char *format se trata de um ponteiro para uma string constante de nome format que pode receber uma string com ou sem especificadores de tipo e o segundo ... indica que inúmeras variáveis e/ou tipos de dados podem corresponder aos especificadores de tipo informados na string.
A função scanf() recebe dois argumentos de entrada:
Forma básica da função scanf(): scanf("tipo_de_entrada", &variavel). Os tipos de entrada especificam o formato de entrada dos dados lidos pela função scanf(). Cada tipo de entrada é precedido por um sinal de %, e um tipo de entrada deve ser especificado para cada variável a ser lida. Se quiséssemos ler duas variáveis, faríamos scanf("%tipo1 %tipo2", &var1, &var2), e assim por diante. Note que os formatos e as variáveis que armazenarão o dado com aquele formato devem ser especificados na mesma ordem (cada tipo para cada variável). Além disso, as variáveis devem ser separadas por vírgula (igual a função printf()).
Na linguagem C, é necessário colocar o símbolo & antes do nome de cada variável a ser lida pela função scanf(). Trata-se de uma exigência da linguagem C. Todas as variáveis que irão receber valores do teclado por meio de scanf() deverão ser passadas pelos seus endereços de memória. Isso é necessário porque de fato queremos alterar o valor da variável e, por esse motivo, é necessário passarmos seu endereço. Em funções, há uma diferença entre passar argumentos por valor e passar por referência, mas não vamos abordar isso por enquanto.
A função scanf() pode ser usada para ler praticamente qualquer tipo de dado. No entanto, ela é usada com mais frequência para a leitura de númeors inteiros e/ou de ponto flutuante (números reais). A tabela a seguir mostra alguns tipos de entrada suportados pela linguagem:
Alguns tipos de entrada | |
---|---|
%c | leitura de um caractere (char) |
%d ou %i | leitura de números inteiros (int ou char) |
%f | leitura de números reais (float) |
%s | leitura de vários caracteres (string) |
%lf | leitura de números reais (double) |
Abaixo, tem-se alguns exemplos de leitura de dados utilizando a função scanf():
scanf("%d%d", &x, &y);
scanf("%d %d", &x, &y);
Acima, ambos os comandos são equivalentes. Isso ocorre porque a função scanf() ignora os espaços em branco entre os tipos de entrada. Além disso, quando a função scanf() é usada para ler dois ou mais valores, podemos optar por duas formas de digitar os dados no teclado:
Abaixo, um programa demonstrando o uso da função scanf() lendo cada um dos tipos de dados básicos (com exceção do void):
1 #include <stdio.h>
2
3 int main(void) {
4
5 int x = 10;
6 printf("\nDigite um número inteiro: ");
7 scanf("%d", &x); // lendo um número inteiro
8
9 char c;
10 printf("\nDigite um caractere: ");
11 scanf("%c", &c); // lendo um caractere
12
13 float f;
14 printf("\nDigite um número real: ");
15 scanf("%f", &f); // lendo um número real de precisão simples
16
17 double d;
18 printf("\nDigite um número real: ");
19 scanf("%lf", &d); // lendo um número real de dupla precisão
20
21 return 0;
22
23 }
Perceba que definimos o valor da variável x como 10, mas em seguida lemos um valor para essa variável com scanf(). O valor dessa variável será trocado após a leitura (exceto se o usuário também digite o valor 10). Para o restante das variáveis c, f e d nenhum valor foi atribuído na inicialização. Observe o operador de endereço de memória & precedendo cada identificador de varíavel.
Ao executar o programa, a saída que obtemos é a seguinte:
Podemos ler inúmeras variáveis com o scanf() e, inclusive, imprimir o valor de cada uma após a leitura:
1 #include <stdio.h>
2
3 int main(void) {
4
5 int x = 10, y = 5;
6 float f = 40.55;
7 printf("Valor de x = %d\nValor de y = %d\nValor de f = %f\n", x, y, f);
8
9 printf("\nDigite novos valores para x, y e f: ");
10 scanf("%d %d %f", &x, &y, &f);
11
12 printf("Valor de x = %d\nValor de y = %d\nValor de f = %f\n", x, y, f);
13
14 return 0;
15
16 }
No código acima, cada uma das variáveis x, y e f foram inicializadas com valores pré-definidos, mas podemos alterar o valor dessas variáveis com a função scanf() na linha 10. Cada especificador de tipo deve corresponder, em ordem, a cada de tipo de variável listada após a vírgula. A função printf() foi usada para imprimir os valores das variáveis antes e depois da leitura com scanf().
Ao executar o programa, a saída que obtemos é a seguinte:
Além de usarmos função scanf(), podemos ler dados do teclado com a função getchar() (get character), que permite ler um único caractere do teclado. Sua forma geral corresponde:
int getchar(void)
A função getchar() não recebe argumentos de entrada e permite ler um único caractere do teclado. A função retorna:
Perceba, nesse exemplo, que a conversão na linguagem C é direta no momento da leitura, ou seja, embora a função retorne um valor do tipo int, pode-se atribuir uma variável do tipo char, em virtudo da conversão automática da linguagem C. Abaixo, um programa demonstrando o uso da função getchar():
1 #include <stdio.h>
2
3 int main(void) {
4
5 char c;
6 c = getchar();
7
8 printf("Caractere: %c\n", c);
9 printf("Código ASCII: %d\n", c);
10
11 return 0;
12
13 }
Ao executar o programa, a saída que obtemos é a seguinte:
Para lermos strings, podemos utilizar a função scanf():
1 #include <stdio.h>
2
3 int main(void) {
4
5 char str[10];
6 scanf("%s", str)
7
8 printf("\nString digitada: %s\n", str);
9
10 return 0;
11
12 }
Ao ler uma string, não é necessário passar o endereço da mesma, visto que uma string já faz uma referência ao endereço de sua primeira posição. Ao executar o programa, a saída que obtemos é a seguinte:
Com o scanf() nós conseguimos ler uma palavra, mas essa função não nos permite ler palavras compostas, pois a leitura é encerrada assim que um espaço é encontrado. Para isso, temos outras duas funções: gets() e fgets.
A função gets() é uma função da linguagem C que nos permite ler strings sem especificar quantos caracteres devem ser lidos. Seu nome vem da expressão em inglês get string (pegar string). Abaixo, um exemplo demonstrando o uso da função gets():
1 #include <stdio.h>
2
3 int main(void) {
4
5 char str[10];
6 gets(str);
7
8 printf("\nString digitada: %s\n", str);
9
10 return 0;
11
12 }
Nós conseguimos ler palavras compostas com gets(), mas dependendo do compilador em uso, podemos receber um aviso nos alertando que a função gets() é perigosa e não deve ser utilizada. Isso acontece porque gets() é uma função antiga da linguagem C que não faz controle de quantos caracteres serão lidos, ou seja, podemos ler mais caracteres do que uma determinada string suporta e, por esse motivo, essa função é suscetível a ataques de Buffer Overflow (uma das mais clássicas vulnerabilidades da linguagem C e envolve a manipulação arbitrária da região de memória Stack). Sendo assim, gets() é uma função obsoleta e seu uso deve ser evitado.
Ao executar o programa, a saída que obtemos é a seguinte:
A melhor forma de lermos string na linguagem C é utilizando a função fgets() que, ao contrário de gets(), nos permite controlar a quantidade de caracteres a serem lidos. Sua forma geral corresponde:
char *fgets(char *str, int n, FILE *stream)
Na assinatura da função, observamos que há três parâmetros definidos: o primeiro char *str se trata de um ponteiro para o endereço de memória da string a ser lida, o segundo int n é o número limite de caracteres que podem ser lidos e o terceiro FIlE *stream se trata de um ponteiro de onde a string será lida: FILE é uma macro pré-definida em stdio.h referente à arquivos, isso porque a função fgets() foi concedida objetivando ler informações armazenadas em arquivos, mas também nos permite ler dados da entrada padrão ou stdin (standard input), nesse caso, do nosso teclado. Abaixo, um exemplo demonstrando o uso da função fgets():
1 #include <stdio.h>
2
3 int main(void) {
4
5 char str[10];
6 fgets(str, 10, stdin);
7
8 printf("\nString digitada: %s\n", str);
9
10 return 0;
11
12 }
Quando falamos em strings, que nada mais são do que arrays de caracteres ocupando n bytes na memória, precisamos entender que as mesmas possuem índices, que "marcam" as suas posições. Todo array inicia com o índice 0, portanto, quando falamos, por exemplo, do índice 6 de um array, estamos falando de sua sétima posição. Cada índice possui um endereço de memória, sendo o índice 0 o endereço inicial daquela string. Também notamos a presença do caractere nulo no índice 5 finalizando a string Hello.
Ao executar o programa, a saída que obtemos é a seguinte:
Isto posto, vimos a melhor forma de lermos strings do usuário, fazendo o controle de quantos dados são enviados. Portanto, é sempre preferível o uso da função fgets() ao uso de scanf() e, principalmente, ao uso de gets() (nunca deve ser usado).