Algoritmos e Estruturas de
Dados I
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; |
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; |
.* |
o próximo argumento da
printf é o valor da precisão |
Um h especifica short int, um
l (letra l minúscula) especifica long int.
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]