Universidade Federal de Minas Gerais
Instituto de Ciências Exatas
Departamento de Ciência da Computação

Algoritmos e Estruturas de Dados I

printf e scanf

A linguagem C define duas funções que podem ser usadas para escrever e ler valores. Os valores são escritos em um chamado “fluxo de saída” que normalmente corresponde a um dispositivo referido como tela ou console (isso será discutido mais à frente na disciplina). Os valores são lidos de um chamado “fluxo de entrada” que normalmente corresponde a um dispositivo referido como teclado.

O primeiro argumento é obrigatório para ambas funções. Se o programador invocar a função printf  ou scanf sem argumentos ( printf()ou  scanf()) uma mensagem de erro é emitida:
too few arguments to function 'printf' ou

too few arguments to function 'scanf'


 O primeiro argumento da função printf corresponde ao endereço de uma cadeia de caracteres, (uma instância de “string”, um string de especificação de conversão ou  string de formato). Esta cadeia de caracteres será lida e os caracteres serão escritos no terminal (ou console) a menos que estes caracteres sejam identificados como sendo especificações de conversão. As especificações de conversão em geral descrevem como escrever os demais argumentos da função

O seguinte trecho não especifica nenhuma conversão:

printf("exemplo de formato sem conversao\n");

O trecho abaixo, conforme discutido à frente, especifica a conversão de um argumento do tipo int. serão escritos na tela os caracteres media= seguidos de caracteres correspondentes ao valor associado à expressão contaX/contaY.

printf("media=%d\n", contaX/contaY);

Observe que uma característica interessante destas funções é a possibilidade delas serem invocadas com 1, 2, ..., n argumentos!

 A seguinte tabela mostra as conversões possíveis:

 

A função scanf deve ter como primeiro argumento um string (denominado string de especificação de conversão ou string de formato) que descreve como deve ser convertida a seqüência de caracteres da entrada. A seguinte tabela mostra algumas conversões:

Na parte inicial da disciplina vamos considerar as funções printf e scanf de forma isolada, mais à frente no material de entrada e saída veremos que estas funções têm relação com fprintf, sprintf, fscanf dentre outras.

A função printf tem como primeiro argumento um arranjo de caracteres (string) e pode ter zero ou mais argumentos seguindo este primeiro. Como parte do aprendizado de programação é importante elaborar vários programas lidando com os diferentes tipos de conversão que podem ser feitos.

Considere o programa abaixo.
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
  int i;
  i=123;
  printf("%d\n", i);
  system("PAUSE");
  return 0;
}

O fluxo de controle do programa acima corresponde a (i) definição da variável de tipo int com nome i, (ii) atribuição do valor 123 à variável i, (iii) invocação ou chamada da função printf com dois argumentos. O Objetivo básico do programador ao usar a função printf é exibir valores na tela ou em uma janela de um monitor, mas a função printf na verdade envia caracteres para uma entidade denominada “fluxo de saída” (output stream). Nos ambientes de desenvolvimento de programas em C o fluxo de saída da printf normalmente está conectado a uma tela ou janela.

O primeiro argumento da função printf deve corresponder a um arranjo de caracteres (string) e estes caracteres serão tratados da seguinte forma: (i) caracteres ordinários que são copiados para o fluxo de saída (ii) caracteres de especificação de conversões. A distinção entre estes dois tipo relaciona-se com o caractere ‘%’ (percento). Os caracteres que seguem o percento devem obedecer a várias regras e se estas regras não forem obedecidas o comportamento não é definido na linguagem C. No programa acima os caracteres ‘%’ e ‘d’ correspondem a uma especificação de conversão. Umas das regras da função printf é que, além do primeiro argumento, deve haver tantos argumentos quanto o número de especificações de conversão. O caractere ‘d’ após o percento no programa acima especifica que deve haver um argumento que será convertido como um valor do tipo int na base 10. Os caracteres ‘\’ e ‘n’ correspondem a caracteres ordinários e especificam o envio de um caractere “funcional” correspondente a uma “nova linha” (new line, daí o uso do n).

Substituindo a chamada da função printf acima, por esta outra:
  printf("%x\n", i);
ocorre a especificação de conversão do argumento como um valor do tipo int mas na base 16.

Considere o programa abaixo.
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
  double d;
  d=3.14;
  printf("%e\n", d);
  system("PAUSE");
  return 0;
}
Este programa é similar ao programa anterior mas agora é utilizada uma variável do tipo double e é especificada uma conversão de um tipo double para o formato [-]m.dddddde+/-xx Os dois programas podem ser combinados:

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

int main(int argc, char *argv[]){
  int i; double d;
  i=123; d=3.14;
  printf("%i %d\n", i, d);
  system("PAUSE");
  return 0;
}
As duas especificações de conversão (correspondente aos dois caracteres percento) exigem dois argumentos.

O formato geral da especificação de conversão é:
%[flags][width][.prec][hlL]type

Os campos opcionais estão entre colchetes mas devem estar na ordem especificada acima. A tabela abaixo descreve os “flags”

Flag

Significado

-

posicione o valor à esquerda

+

o valor deve ser precedido de + ou -

espaço/branco

o valor positivo deve ser precedido de 1 caractere em branco

0

preencha o valor com zeros

#

preceder octais com 0, hex com 0x;
mostrar ponto decimal...

A tabela abaixo descreve “width.prec”:

número

tamanho mínimo da largura ou precisão

*

o próximo argumento da printf é a largura

.numero

numero minimo de digitos para int;
numero de casas decimais para e ou f
numero max de dig significativos para g
numero max de caracteres para s

.*

o próximo argumento da printf é o valor da precisão
(interpretado como acima)

Um h especifica short int, um l (letra l minúscula) especifica long int.

scanf()

A função scanf opera, de forma similar à printf, sobre um fluxo de caracteres, mas agora trata-se de um “fluxo de entrada”. Os caracteres obtidos na “entrada” (em geral a scanf opera sobre um fluxo de caracteres oriundo do teclado) devem ser tratados (ou convertidos e atribuidos aos demais argumentos) de acordo com os caracteres do primeiro argumento. No caso da printf os argumentos são expressões e estamos interessados no”right-value”, mas no caso da scanf estamos interessados em atribuir os valores obtidos do fluxo de entrada para variáveis e a especificação e implementação da scanf() espera que sejam fornecidos como parâmetros o endereço das variáveis onde devem ser armazenados os valores obtidos no fluxo de entrada (devemos usar “left-values”). Este assunto está relacionado com “ponteiros” e será detalhado em outra parte da disciplina. A forma mais simples de especificar que queremos atribuir um certo valor à variável X é usar a expressão &X (E comercial X, ou no jargão da linguagem C: endereço de X):

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

int main(int argc, char *argv[]){
  int i;
  scanf("%i", &i);
  printf("%i\n", i);
  system("PAUSE");
  return 0;
}

Um erro muito comum de programadores que usam a função scanf é esquecer de utilizar o caractere “E comercial” (&) antes do nome da variável que deve receber o valor convertido a partir dos caracteres do fluxo de entrada!
O programa acima usa a função scanf para obter um valor para a variável i a partir dos caracteres do teclado. Quando o programa acima é executado é invocada a função scanf (que fica aguardando os caracteres no fluxo de entrada , no caso o teclado). O programa acima aguarda uma ação do usuário sem avisá-lo... este tipo de programa não é considerado adequado e a boa disciplina de programação prescreve que os desenvolvedores de programas devem planejar a interação do programa com o “usuário” (em nossa disciplina vamos considerar que o “usuário” é sempre o próprio programador o que pode desculpar muitas coisas indevidas). O tema “interação” é tratado em outras disciplinas e aqui faremos uso da “intuição”, por enquanto deve ser explicitado apenas que este não é um tema simples e que existem muitas diretrizes sobre como tratar a “interação” do programa com o usuário. Um programa mais adequado é aquele onde o desenvolvedor (programador) informa ao usuário o que é esperado dele:
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
  int i;
  printf(“Entre com um valor do tipo int na base 10:”);
  scanf("%i", &i);
  printf("%i\n", i);
  system("PAUSE");
  return 0;
}

A função scanf lê uma ou mais linhas para obter caracteres a serem usados nas conversões. Um possível problema para entender o funcionamento da função scanf()  é um certo tratamento desigual para as diferentes conversões. Quando é especificado no primeiro parâmetro uma conversão do tipo %d a função scanf() irá ignorar diversos caracteres para poder encontrar uma sequência de caracteres entre ‘0’ e ‘9’; mas com relação a conversão %c a função scanf() não ignora nenhum caractere (nem mesmo o caractere correspondente ao momento em que o usuário teclou “enter” (sim existe um caractere denotando “enter”)!!! A função scanf retorna ao ponto de chamada somente após ter processado toda a especificação de conversão dada pelo primeiro argumento. O caractere de mudança de linha é tratado como caractere em branco exceto na conversão %c. Podemos combinar os programas acima em um programa onde duas variáveis são definidas, é usada a função scanf para obter valores para serem usados em atribuições e a função printf exibe os valores lidos:

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

int main(int argc, char *argv[]){
  int i=123456; double d=123.456;
  printf("entre com um valor do tipo int e um valor do tipo double\n");
  scanf("%i%Le", &i, &d);
  printf("valor de i:%d valor de d:%e\n", i, d); 
  system("PAUSE");     
  return 0;
}

Observe no programa acima o uso do modificador ‘L’ junto com a especificação de conversão %e (“%Le”). Sem este modificador a função scanf trata o parâmetro como sendo float (e, f e g correspondem a float!)

O programa abaixo usa as características de análise da entrada da função scanf para elaborar um programa onde o usuário pode especificar de forma interativa dois operandos e uma de quatro operações. Observe que tanto a printf quanto a scanf são funções que retornam um valor do tipo int. O valor retornado se relaciona ao número de conversões feitas. No caso da scanf ela retorna a constante EOF quando não consegue fazer todas as conversões especificadas.

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

int main(int argc, char *argv[]){
  double op1, op2;
  char operador;
  printf("Entre com (i)operando (ii)operacao (iii)operando\n");
  while(scanf("%Le %c %Le", &op1, &operador, &op2)==3){
    switch(operador){
      case '+': printf("%g\n", op1+op2); break;
      case '-': printf("%g\n", op1-op2); break;
      case '*': printf("%g\n", op1*op2); break;
      case '/': printf("%g\n", op1/op2); break;
      default: printf("operador invalido!\n");
    }
    printf("Entre com (i)operando (ii)operacao (iii)operando\n");
  }   
  system("PAUSE");     
  return 0;
}

aqui estão algumas interações:

Entre com (i)operando (ii)operacao (iii)operando
1/2
0.5
Entre com (i)operando (ii)operacao (iii)operando
1*3
3
Entre com (i)operando (ii)operacao (iii)operando
10e10*10e20
1e+032
Entre com (i)operando (ii)operacao (iii)operando
123+456
579
Entre com (i)operando (ii)operacao (iii)operando

Para terminar o programa basta causar um fim de fluxo (ou fim de arquivo) digitando um ou mais caracteres adequados ao “mapeamento de teclas” da sua plataforma (usualmente Ctrl Z ou Ctrl D).

A conversão %s corresponde a atribuir uma sequência de caracteres da entrada a uma sequência de posições de memória. Este tipo de conversão tem sentido depois de estudar o material de arranjos, emparticular o assunto arranjo de caracteres. Um trecho típico é:
char ac[300];
printf(“Digite uma sequencia de caracteres para ser lida:”);
scanf(“%s”, ac);

A função scanf lê os caracteres da entrada e coloca na área de memória correspondente a ac até encontrar (i) um espaço ou (ii) caractere de tabulação ou (iii) caracteres de delimitação de linha ou (iv) indicação de fim de arquivo.

 

A função printf é do tipo int e retorna o número de caracteres escritos no fluxo (ou um número negativo se ocorreu um erro). O seguinte trecho de programa é tão “válido” quanto qualquer outro trecho de programa na linguagem C que ignora o valor retornado por uma função:

  printf("%d\n", printf("abc\n"));

Execute o trecho acima e veja se o que é impresso é o que você espera.

A função scanf é do tipo int e retorna o número de itens de entrada convertidos e atribuidos. O trecho
  printf("%d\n", scanf("%i%i%i",&k1,&k2,&k3 ));

imprime o valor 3 caso a execução da função scanf seja “normal” envolvendo a leitura e atribuição de três valores.

Apêndices:

Sobre a impressão de long long no DEV-C++

long long on Windows/Dev-C++/MinGW

[sarcasm on]

(because Moldova is probably the last country where programming competitions are still held on Windows)

[sarcasm off]

The long long data type has been introduced in C as part of the C99 standard. On 32-bit machines it is a 64-bit signed integer data type. The printf/scanf formatting string for this type is %lld.

Using Dev-C++ and the MinGW port of the GNU C Compiler on Windows, one would expect programs to run the same way they do with GCC on Linux. But no! Apparently MinGW calls the Windows libraries, so every time you call printf or scanf you’ll end up using Microsoft’s version of the functions!

And of course Microsoft cares about backwards compatibility, so when C99 was published they didn’t switch to the standard, keeping their non-standard

%I64d  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

 formatting string instead!

[subsides into an angry stream of unintelligible muttering]