Mais sobre entradas e saídas de dados

O objetivo deste texto é explicar o funcionamento básico dos comandos para entrada e saída de dados utilizando o linguagem C. Para isso existem as funções scanf e printf que devem ser "carregadas" a partir de uma instrução especial, a diretiva #include para os cabeçalhos stdio.h no cód. 1 (use como regra iniciar seus códigos sempre com essa linha).

Outro conceito importante para as funções scanf e printf é utilizar o formatador correto para cada "tipo" de variável. Neste texto ilustraremos os quatro formatadores básicos %d, %c, %f e %lf, respectivamente para os "tipos" int (inteiro), char (caractere), float (flutuante) e double (flutuante precisão dupla). Desse modo, se está iniciando na "arte da programação" na linguagem C, basta, por enquanto, testar apenas com int.

1. Qual a diferença entre declarar e usar uma variável?

Lembramos que uma variável pode ser entendida como um "nome" associado à uma posição de memória do computador (e a um tamanho): a posição de seu bit inicial e o número de bits usados para representar aquele tipo de variável. Como a linguagem C é compilada, nela é necessário declarar cada variável e isso (geralmente) é feito no início da função. Uma vez que variável foi declarada, pode-se usá-la (recebendo valores ou em expressões).

Outro conceito essencial às variáveis é o de atribuição de valor. Sua sintaxe em C, e em muitas outras linguagens, é usar do lado esquerdo da atribuição um nome de variável e do lado direito da atribuição uma expressão aritmética válida. No código 1, existem três (3) atribuições, a saber:   num = 3,   r = num / 2   e   r = num % 2.   Como em todas as três (3) expressões, só ocorrem variáveis ou constantes inteiras, o resultado das expressões serão necessariamente valores inteiros, mesmo na divisão! Experimente.

#include <stdio.h>
int main (void) {
  int num=5, q, r; // declara 3 variaveis para inteiro, de nomes 'num' (sendo iniciada com valor 5), 'q' e 'r'
  // Em C, se uma expressao envolver apenas inteiros, o resultado necessariamente sera' inteiro
  // Os operadores (binarios) basicos C sao os seguintes:
  //  +, -, *, /, % - respectivamente, para soma, subtracao, multiplicacao, divisao e resto da divisao
  q = num / 2; // 'q' recebera' o quociente da divisao inteira de 'num' por 2, entao recebera' 5/2 que sera' 2
  r = num % 2; // 'q' recebera' o resto da divisao inteira de 'num' por 2, entao recebera' 5%2 que sera' 1
  printf("num=%d, %d/%d=%d, %d\%%d=%d\n", num, num, 2, q, num, 2, r);
  }
Cód. 1. Código ilustrando a declaração de variáveis inteiras e divisão inteira (quociente e resto).

Como no código 1, a variável num é do tipo inteiro, o mesmo ocorrendo com a constante 2, então a operação é feita usando a precisão de inteiros (mas é possível obter o resultado em flutuante, bastaria usar num / 2.0) e desse modo, seu resultado é o quociente da divisão inteira.
Existe outra possibilidade de "forçar" o resultado a ser flutuante, usando o coversor de tipo para flutuante como em: float x = (float)num/2. Mas esse é outro tópico de estudo e será tratado nos textos sobre números "reais"/"flutuantes".

Saber mais A figura 1 ilustra a associação de espaço em memória (RAM) para duas variáveis, são associados 16 bits à variável de nome n para valores inteiros, seguido da associação de 32 bits para a variável de nome x, que deve guardar valores "reais". Nota-se que a variável "real" tem um esquema mais "complicado", ela implementa o conceito de ponto flutuante, usando o padrão IEEE 754 (o primeiro bit é o sinal s; os 8 bits seguintes correspondente ao expoente e e os últimos 23 à mantissa m - valor de x é s x m x 10e, sendo m entre 0 e 1).

Fig. 1. Representação da memória com agrupamentos em bytes (8 bits).

2. O que são "entradas de dados" e "saídas de dados"?

Um algoritmo é uma sequência finita de passos, que ao ser aplicado à um conjunto de dados (entradas) deve produzir sempre as mesmas saídas. Por exemplo, o algoritmo da divisão ao ser aplicado sobre valores fixados a e b, deve produzir sempre o mesmo valor q (de tal forma que q * b = a).

Por outro lado, um programa, em C ou em qualquer outra linguagem, nada mais é que a implementação de um algoritmo na referida linguagem. Desse modo, para este programa ser usado, o usuário (aquele que está executando) deve fornecer um conjunto de dados de entrada, na ordem adequada (pois a/b geralmente não é o mesmo que b/a), para que o programa possa ser executado e produzir as saída desejadas.


Fig. 2. Ilustração da existência de um algoritmo que aplicado sobre as entradas produz as respectivas saídas.

Podemos usar o mesmo exemplo da divisão para ilustar a necessidade dos dados de entrada adequados. Para que um algoritmo para a divisão de dois números reais seja adequadamente executado, devem ser fornecidos os dois valores reais, o primeiro será o numerador e o segundo será o denominador. Assim, se o usuário digitar apenas um valor, o programa ficará parado até que ele digite o segundo valor.

Saber mais Vale observar que, em uma linguagem de programação, não existe a necessidade de implementar algo tão básico quanto à divisão de dois números, pois isso é feito por um algoritmo implementado diretamente no processador do computador. Desde a proposta do computador EDVAC, apresentada em documento de 1945 pelo matemático John von Neumann, existe uma unidade lógico/aritmética especialmente projetada para realizar as operações lógicas e aritméticas. Entretanto, nesse modelo o tratamento para "números reais" era feito com ponto fixo, enquanto hoje utilizamos ponto flutuante por permitir um maior escopo de valores.

3. Saídas de dados simples: inteiro, real e texto em C

A linguagem C preconiza o uso de formatador para cada tipo de variável, tanto para as entradas quanto para as saídas. Os formatadores básicos são:

Tab. 1. Formatadores de acordo com tipo, com exemplo para entra e para saída em C
Tipo de variávelTipo em C LeituraImpressão
Inteiroint a, b; scanf("%d %d", &a, &b);printf("%d %d\n", a, b);
Caracterechar a, b; scanf("%c %c", &a, &b);printf("%c %c\n", a, b);
Flutuantefloat a, b; scanf("%f %f", &a, &b);printf("%f %f\n", a, b);
Duplodouble a, b; scanf("%lf %lf", &a, &b);printf("%lf %lf\n", a, b);

Em C a conversão entre inteiros e caracteres é direta, basta trocar o formatador. Isso é possível devido à conversão inteiro-binário e binário-caractere. Para isso utiliza-se a primeira tabela de representação de caracteres que se popularizou, a tabela ASCII. Para saber mais consulte o texto introdutório sobre caracteres. Na tabela 1, o código à esquerda ilustra como testar a tabela ASCII.

Tab. 1. Exemplo de códigos para entrada e saída de inteiros e "reais" para o C 2
Exemplo de tabela ASCII Exemplo de tabela numérica ∑i=010 0.5i
#include <stdio.h>

void main (void) {
  int i;
  printf("Int | ASCII\n");
  for (i=48; i<91; i++) {
    printf(" %2d |  '%2c'\n", i, i);
    // "Pular" os caracteres:
    if (i==57) // 58=':' 59=';' 60='<' 61='='
      i = 64;  // 62='>' 63='?' 64='@'
    }
  }
#include <stdio.h>
void main (void) {
  float soma = 0, pot = 1;
  int i;
  printf("Int | Soma 0.5^0+...+0.5^i\n");
  for (i=0; i<11; i++) {
    soma += pot;
    printf(" %2d |             %8.2f\n", i, soma);
    pot *= 0.5 * pot;
    }
  }

4. Entradas de dados em C utilizando apenas variáveis inteiras

Em C o processo para leitura de valores via teclado é tratado em lote, quer dizer, durante a execução de um programa, a cada momento que o usuário digitar algo (uma sequência de caracteres) e pressionar a tecla para registro (Enter), tudo o que foi digitado será armazenado em um reservatório (em Inglês buffer) e a cada momento que o programa encontrar um comando para leitura será usado a próxima "parte" disponível desse reservatório (armazendo-o em uma variável).
Para testar a existência desse reservatório, experimente testar o código 2, de dois modos:

  1. digitando todos os valores com um único Enter (CR) ao final (i.e. algo como 2 3 45 CR)
  2. digitando cada um dos 3 itens teclando Enter em diferentes locais (e.g. como: 2 CR 3 CR 45 CR).
Você deverá notar que em ambos os modos, todos os valores serão corretamente capturados, com qualquer variação de Enter (CR) adequado, os valores serão armazenados nas variáveis i1, i2 e j da mesma forma.

Tab. 2. Exemplo de código para entrada de valores usando apenas variáveis do "tipo" inteiro
Exemplo de código para entrada em C
#include <stdio.h>
void main (void) {
  int i1, i2; // pode-se digitar todas as variaveis do tipo "int" em uma unica linha
  int j; // mas tambem pode-se digitar um variavel para cada "int" declarado
  printf("Digite 3 itens inteiros, separados por espaco em branco: "); // algo como: 1 2 3 seguido de ENTER
  scanf("%d %d %d", &i1, &i2, &j); // um formatador %d para cada variavel tipo "int"
  // Experimente comentar a linha acima e testar com variacoes como abaixo (produzira' o mesmo resultado)
  // scanf("%d", &i1);
  // scanf("%d", &i2);
  // scanf("%d", &j);
  printf("i1=%d, i2=%d, j=%d\n", i1, i2, j); // o \n e' para mudar/quebrar de linha apos impressao
  }

Para ilustrar a compilação e execução do código da tabela 2, suporemos que ele tenha sido gravado com o nome intr_leitura_impressao.c. Assim, na figura 3, ao final de cada linha deve-se entender que o usuário tenha pressionado a tecla Enter: na primeira linha o comando gcc -o intr_leitura_impressao.o intr_leitura_impressao.c invoca o compilador gcc, compilando o programa intr_leitura_impressao.c e gerando o seu correspondente executável com nome intr_leitura_impressao.o; na segunda linha o usuário está invocando o executável intr_leitura_impressao.o e rodando-o; na terceira linha está mensagem do primeiro printf do referido código, seguido do que supomos ter o usuário digitado (o "texto" 2 3 45 seguido da tecla Enter); na última linha está o segundo printf do código, com os valores capturados e armazenados, respectivamente, nas variáveis i1, i2 e j.

leo@paprika3:~/tutorial/codigos$ gcc -o intr_leitura_impressao.o intr_leitura_impressao.c
leo@paprika3:~/tutorial/codigos$ ./intr_leitura_impressao.o
Digite 3 itens inteiros, separados por espaco em branco: 2 3 45
i1=2, i2=3, j=45
Fig. 3. Ilustração do terminal ao compilar e executar o código (compilado) do programa apresentado na tabela 2.

Experimente copiar e colar o código da tabela 2 em seu ambiente para programação C preferido. Procure alterar o código até que esteja certo de ter assimilado a sintaxe e os detalhes de cada conceito aqui explorado.

5. Entradas de dados em C usando int, char e float

Em C o processo para leitura de valores via teclado é tratado em lote, quer dizer, durante a execução de um programa, a cada momento que o usuário digitar uma sequência de valores e pressionar a tecla para registro (Enter), todos os valores digitados serão armazenado em um reservatório (em Inglês buffer) e a cada momento que o programa encontrar um comando para leitura será usado a próxima parte disponível do reservatório.
Para verficar a existência do reservatório, experimente testar o código 3, de dois modos:

  1. digitando todos os valores e um único Enter (CR) ao final (i.e. algo como 2 3 ABD D E 0.5 2.3 CR)
  2. digitando cada um dos 9 itens teclando Enter em diferentes locais (e.g. como: 2 CR 3 CR ABD CR D CR E CR 0.5 CR 2.3 CR).
Você deverá notar que em ambos os modos todos os valores serão corretamente capturados, com qualquer variação de Enter (CR) adequado, os valores serão armazenados nas variáveis i1, i2, c1, c2, c3, c4, c5, x e y da mesma forma.

Tab. 3. Exemplo de código para entrada de valores (para os "tipos" inteiro, caracteres e "reais")
Exemplo de código para entrada em C
#include <stdio.h>
void main (void) {
  int i1, i2;
  char c1, c2, c3, c4, c5;
  float x, y;
  printf("Digite 9 itens, separados por espaco em branco (2 int, 5 char, 2 float): "); // neste ordem: inteiro, inteiro, caractere,...
  scanf("%d %d %c %c %c %c %c %f %f", &i1, &i2, &c1, &c2, &c3, &c4, &c5, &x, &y);
  // Experimente comentar a linha acima e testar com variacoes como abaixo (mesmo resultado)
  // scanf("%d %d", &i1, &i2);
  // scanf("%c %c %c %c %c", &c1, &c2, &c3, &c4, &c5);
  // scanf("%f %f", &x, &y);
  printf("i1=%d, i2=%d, c1=%c, c1=%c, c1=%c, c1=%c, c1=%c, x=%f, y=%f\n", i1, i2, c1, c2, c3, c4, c5, x, y);
  }

Para ilustrar a compilação e execução do código da tabela 3, suporemos que ele tenha sido gravado com o nome intr_leitura_impressao.c. Assim, na figura 4, ao final de cada linha deve-se entender que o usuário tenha pressionado a tecla Enter: na primeira linha o comando gcc -o intr_leitura_impressao.o intr_leitura_impressao.c invoca o compilador gcc, compilando o programa intr_leitura_impressao.c e gerando o seu correspondente executável com nome intr_leitura_impressao.o; na segunda linha o usuário está invocando o executável intr_leitura_impressao.o e rodando-o; na terceira linha está mensagem do primeiro printf do referido código, seguido do que supomos ter o usuário digitado (o "texto" 2 3 ABD D E 0.5 2.3 seguido da tecla Enter); na última linha está o segundo printf do código, com os valores capturados e armazenados, respectivamente, nas variáveis i1, i2, c1, c2, c3, c4, c5, x, y.

leo@paprika3:~/tutorial/codigos$ gcc -o intr_leitura_impressao.o intr_leitura_impressao.c
leo@paprika3:~/tutorial/codigos$ ./intr_leitura_impressao.o
Digite 9 itens, separados por espaco em branco (2 int, 5 char, 2 float): 2 3 ABD D E 0.5 2.3
i1=2, i2=3, c1=A, c1=B, c1=D, c1=D, c1=E, x=0.500000, y=2.300000
Fig. 4. Ilustração do terminal ao compilar e executar o código (compilado) do programa apresentado na tabela 2.

Experimente copiar e colar cada um dos códigos acima em seu ambiente para programação C preferido. Procure alterar o código até que esteja certo de ter assimilado a sintaxe e os detalhes de cada conceito aqui explorado.

Leônidas de Oliveira Brandão
http://line.ime.usp.br

Alterações :
2024/03/13: novos paragrafos na secao 1 sobre quociente e resto da divisao inteira;
2024/03/13: 2 novos paragrafos introdutorios; nova secao 4 (anterior tornou-se 5); acertos nas legendas das antigas tab. 2 e fig. 3
2022/03/27: indices de secoes (4)
2021/04/26: nova fig. 3
2021/04/22: pequenos acertos, alterações visibilidade desse rodapé
2020/08/17: primeira versão do texto