Os computadores convencionais se baseiam no conceito de uma memria principal que consiste de clulas elementares, cada qual identificada por um endereo. O contedo de uma clula o valor da mesma. O valor de uma clula pode ser lido e/ou modificado. Esta modificao feita pela substituio de um valor por outro. Alm disso, circuitos permitem o acesso a uma clula de cada vez. Com poucas excees, as linguagens de programao podem ser consideradas como abstraes, em nveis diferentes, do comportamento destes computadores convencionais. Em particular, o conceito de varivel introduzido como uma abstrao de clulas de memria, e o conceito de comando de atribuio como uma abstrao destrutiva destas clulas. Uma varivel caracterizada por um nome e dois atributos bsicos: valor e tipo. O nome usado para identificar e fazer referncia varivel. O valor de uma varivel representado, de forma codificada, na rea de memria amarrada varivel. Este cdigo interpretado de acordo com o tipo da varivel. O tipo de uma varivel pode ser considerado como uma especificao da classe de valores que podem ser associados varivel, bem como das operaes que podem ser usadas para criar, acessar e modificar estes valores. A amarrao entre uma varivel e o valor armazenado na rea de memria correspondente , em geral, dinmica, j que este valor pode ser modificado por operaes de atribuio. Uma atribuio como b a causa o armazenamento de uma cpia do valor da varivel a na rea de memria amarrada varivel b. Algumas linguagens, entretanto, permitem o congelamento da amarrao entre uma varivel e o seu valor quando a amarrao estabelecida. A entidade resultante , sob qualquer aspecto, uma constante simblica definida pelo programador. Por exemplo, em Pascal se pode escrever:
const pi = 3.1416;
e ento usar pi em uma expresso como circunferncia:= 2 * pi * raio;. A varivel pi est amarrada ao valor 3.1416, e este valor no pode ser modificado; isto , o compilador acusa erro se existir uma atribuio pi. Poderamos usar o valor 3.1416 diretamente na expresso acima, dispensando o uso da constante simblica pi (circunferncia:= 2 * 3.1416 * raio;). Neste caso, chamamos 3.1416 de constante literal.
2.2 A Pseudo-linguagem LPE
Nos itens subseqentes iremos descrever as regras sintticas e semnticas de uma linguagem de construo de algoritmos chamada Linguagem de Programao Estruturada (LPE). Esta linguagem tem por objetivo permitir a elaborao de um algoritmo sem termos que nos preocupar com as regras rgidas de uma linguagem de Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 2 16/05/00
programao real. Apesar da LPE possuir um certo formalismo, nos permitido "afrouxar" as suas regras sintticas quando for conveniente. Isto pode ser feito pelo fato da LPE no ser uma linguagem de programao real; isto , no existe nenhum tradutor de LPE que nos permita executar um programa escrito nesta linguagem em um computador real. Um programa em LPE ter o seguinte lay-out:
constantes <nome> = <valor>;
<nome> = <valor>; variveis <nome> : <tipo>;
<nome> : <tipo>; incio <comando>;
<comando>; fim.
No lay-out acima, tudo o que estiver em negrito faz parte da sintaxe da linguagem; e o que estiver entre < > ser posteriormente substitudo por construes dos programadores. Esta construes, porm, tero tambm que obedecer s regras da LPE.
2.2.1 Nomes
Um nome de uma varivel, ou de uma constante simblica, uma seqncia de no mximo 32 caracteres alfanumricos, alm do caracter sublinhado ( _ ). O primeiro caracter tem que ser obrigatoriamente alfabtico. A linguagem LPE no faz distino entre caracteres maisculos e minsculos.
Exemplo:
a nome_aluno c8 valor xyz
2.2.2 Tipos
2.2.2.1 Inteiro
O tipo inteiro usado para representar valores inteiros positivos e negativos. As operaes disponveis para o tipo inteiro so representadas pelos operadores + Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 3 16/05/00
(soma), - (subtrao), * (multiplicao), div (quociente da diviso inteira), e mod (resto da diviso inteira).
2.2.2.2 Real
O tipo real usado para representar valores decimais com representao finita. As operaes disponveis para o tipo real so representadas pelos operadores + (soma), - (subtrao), * (multiplicao), / (diviso).
2.2.2.3 Lgico
O tipo lgico usado para representar os valores lgicos verdadeiro e falso. As operaes disponveis para o tipo lgico so representadas pelos operadores (e), (ou), e ~ (no).
2.2.2.4 Caracter
O tipo caracter usado para representar qualquer cadeia de caracteres tais como nomes, endereos e etc. A LPE no oferece nenhuma operao sobre os valores do tipo caracter.
2.2.3 Constantes Literais
Uma constante literal uma constante cujo nome a representao escrita do seu valor. Por exemplo, 21 a representao decimal de um objeto de dados cujo valor 21.
Exemplo:
12.450 -0.5 1.0 /* tipo real */ verdadeiro falso /* tipo lgico */ 1 -12 234 /* tipo inteiro */ 'PUC-Rio' 'A' '123' ' ' /* tipo caracter */
2.2.4 Comandos e Expresses
2.2.4.1 Expresses
Uma expresso uma frmula ou regra de computao que sempre determina um valor ou resultado. Uma expresso consiste de operandos e operadores. Os operadores da LPE j foram apresentados nos itens relativos aos tipos de dados. Os operandos so constantes e variveis. Se vrios operadores ocorrem em uma expresso, a ordem de execuo das operaes precisa ser especificada; quer seja pelo emprego explcito de parnteses, quer seja pelas regras implcitas da linguagem (veja a tabela abaixo). Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 4 16/05/00
Exemplo:
3 /* uma constante uma expresso */ abc /* uma varivel uma expresso */ n mod 2 salrio * 1.25 (renda_bruta - desconto) * 0.15
Neste ponto iremos apresentar os operadores relacionais. Os operadores relacionais so amplamente usados na matemtica e dispensam apresentaes. Estes operadores so usados para relacionar duas expresses; criando uma nova expresso. O valor desta nova expresso sempre uma valor lgico; isto , verdadeiro ou falso. Os operadores relacionais so os seguintes:
= (igual) (diferente) > (maior) < (menor) >= (maior ou igual) <= (menor ou igual)
As regras implcitas de precedncia dos operadores da LPE so mostradas na tabela abaixo (do maior para o menor):
onde o termo <varivel> dever ser substitudo por uma varivel declarada na seo variveis, e o termo <expresso> deve ser substitudo por uma expresso vlida na linguagem LPE. Esta expresso tem que ser de um tipo que seja compatvel com o tipo da varivel esquerda do operador de atribuio (). O comando de atribuio ir avaliar a expresso direita do operador de atribuio, e substituir o valor da varivel esquerda do operador pelo valor da expresso. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 5 16/05/00
Exemplo:
idade 3; /* uma constante uma expresso */ x n mod 2; salrio salrio * 1.25; imposto (renda_bruta - desconto) * 0.15; endereo 'Rua JK, 23';
2.2.4.3 Comando de Leitura
O comando de leitura tem a seguinte sintaxe:
leia(<var1>,<var2>,,<varN>);
onde os termos <varK> devem ser substitudos por variveis declaradas na seo variveis, exceto por variveis do tipo lgico. O comando de leitura tem por objetivo transferir dados de um perifrico, por exemplo o teclado, e armazen-los nas variveis fornecidas no comando.
Exemplo:
leia(matrcula,nome,idade,sexo);
2.2.4.4 Comando de Escritura
O comando de escritura tem a seguinte sintaxe:
escreva(<exp1>,<exp2>,,<expN>);
onde os termos <expK> devem ser substitudos por expresses vlidas na linguagem LPE, exceto por expresses que tm valor do tipo lgico. O comando de escritura tem por objetivo transferir dados da memria principal para um perifrico; por exemplo um monitor de vdeo.
Exemplo:
escreva('O Nome do Aluno e ',nome); escreva('Salrio - ',salrio,' Imposto - ',salrio*0.1);
2.2.5 Estrutura de Controle
2.2.5.1 Bloco
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 6 16/05/00
Um bloco consiste de um conjunto de comandos delimitados pelas palavras incio e fim. Um bloco pode ser interpretado como sendo um comando composto por vrios outros comandos, e cuja a execuo tem efeito igual ao obtido pela execuo dos vrios comandos nele inseridos. Na sintaxe da LPE, todas as vezes que aparecer o smbolo <comando>, indicando a obrigatoriedade de se codificar um comando; este poder se substitudo por um bloco.
Exemplo:
variveis i, j, k : inteiro; abc: real; incio i 0; j 0; incio k (i + j) * 3; abc k * 8.5; escreva(abc); fim; escreva(i,j,k); fim.
2.2.5.2 Seleo
O comando de seleo tem duas formas:
se <expresso lgica> ento <comando>;
nesta forma a <expresso lgica> avaliada inicialmente. Se o valor da expresso for verdadeiro, o <comando> executado. Se o valor da expresso for falso, o <comando> no executado.
Exemplo
variveis mdia, nota1, nota2, nota3 : real; incio escreva('Informe as notas do aluno'); leia(nota1, nota2, nota3); mdia (nota1+nota2+nota3)/3; se mdia >= 5.0 ento escreva('Aluno Aprovado'); escreva(mdia); fim.
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 7 16/05/00
se <expresso lgica> ento <comando1>; seno <comando2>;
nesta forma a <expresso lgica> avaliada inicialmente. Se o valor da expresso for verdadeiro, o <comando1> executado e o <comando2> no. Se o valor da expresso for falso, o <comando2> executado e o <comando1> no.
Exemplo
variveis mdia, nota1, nota2, nota3 : real; incio escreva('Informe as notas do aluno'); leia(nota1, nota2, nota3); mdia (nota1+nota2+nota3)/3; se mdia >= 5.0 ento escreva('Aluno Aprovado'); Incio escreva('Informe as notas do aluno'); leia(nota1,nota2,nota3); media <- (nota1+nota2+nota3)/3; media>=5.0? escreva('Aluno aprovado); escreva(media); sim no Fim
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 8 16/05/00
seno escreva('Aluno Reprovado'); escreva(mdia); fim.
2.2.5.3 Repetio
O comando de repetio tem a seguinte sintaxe:
enquanto <expresso lgica> faa <comando>;
neste comando a <expresso lgica> avaliada inicialmente. Se o valor da expresso for verdadeiro, o <comando> executado e a <expresso lgica> avaliada novamente. Este ciclo repetido at que a <expresso lgica> seja avaliada como falso. importante notar que se a <expresso lgica> for avaliada inicialmente como falso o <comando> no ser executado nenhuma vez. Outra observao importante que o <comando> dever alterar de alguma forma as variveis presentes na <expresso lgica>. Se isto no ocorrer, a <expresso lgica> ser sempre avaliada como verdadeiro (supondo que a <expresso lgica> seja avaliada como verdadeiro na primeira avaliao) e o <comando> ser executado continuamente. Diremos ento que o "programa entrou em um looping infinito". Incio escreva('Informe as notas do aluno'); leia(nota1,nota2,nota3); media <- (nota1+nota2+nota3)/3; media>=5.0? escreva('Aluno aprovado); escreva(media); sim Fim escreva('Aluno reprovado); no
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 9 16/05/00
Exemplo
O exemplo a seguir exibir no monitor de vdeo todos os nmeros inteiros entre 0 e 99.
Incio cont<-0; cont<100? sim Fim no escreva(cont); cont<-cont+1;;
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 10 16/05/00
3. A Linguagem C
3.1 Tipos de Dados
Os tipos das variveis e das constantes de um programa escrito na lingugem C so baseados em um dos cinco tipos bsicos de dados existentes:
char uma varivel ou uma constante do tipo char pode armazenar um nico caracter ASCII, ou um nmero inteiro entre 128 e 127. int - uma varivel ou uma constante do tipo int pode armazenar um nmero inteiro entre 32768 e 32767. float - uma varivel ou uma constante do tipo float pode armazenar um nmero decimal com seis a sete dgitos de preciso, na faixa de 3.4E-38 a 3.4E+38. double - uma varivel ou uma constante do tipo double pode armazenar um nmero decimal com quatorze a quinze dgitos de preciso, na faixa de 1.7E-308 a 1.7E+308. void o tipo void ser descrito posteriormente.
3.2 Nomes de Identificadores
O padro ANSI C define os nomes de variveis, funes, constantes e vrios outros objetos definidos pelo programador como identificadores. Tais identificadores so compostos por um ou mais caracteres. O primeiro caracter deve ser um caracter alfabtico ou o caracter sublinhado ( _ ), e os demais caracteres podem ser quaisquer caracteres alfanumricos ou o caracter sublinhado ( _ ).
O padro ANSI determina que os identificadores podem ter qualquer tamanho, mas pelo menos os primeiros seis caracteres devem ser significativos. Porm, o nmero mximo de caracteres significativos pode variar de compilador para compilador. Por exemplo, se o seu compilador reconhece apenas os primeiros 31 caracteres de um identificador como significativos, identificadores diferentes mas com os 31 caracteres iniciais iguais sero tratados como sendo o mesmo identificador. Uma outra caracterstica da linguagem C que as letras maisculas e minsculas so tratadas como sendo distintas. Logo, os identificadores nome, Nome e NOME, so trs identificadores distintos.
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 11 16/05/00
3.3 Variveis
As variveis so declaradas em trs lugares bsicos em um programa C: dentro das funes, na definio dos parmetros das funes, e fora de todas as funes. Estas so variveis locais, parmetros formais e variveis globais, respectivamente. Todas as variveis usadas em um programa C devem ser declaradas antes de serem usadas. A forma geral de uma declarao de variveis :
<tipo> <var1>,<var2>,<var3>,...,<varN>;
Exemplo
int i,j,k; float salario; char letra,ch;
Uma varivel pode ser inicializada no momento da sua declarao. A forma geral de uma inicializao :
<tipo> <var1>=<constante>;
Exemplo
int i=0,j,k=6; float salario=600.25; char letra='G',ch='@';
As variveis globais so inicializadas apenas no comeo do programa, enquanto as variveis locais so inicializadas todas as vezes que a funo na qual elas foram declaradas for ativada.
3.4 Constantes
A linguagem C permite o uso tanto de constantes literais como de constantes simblicas. A maneira como cada constante representada depende do seu tipo. As constantes do tipo char so envolvidas por aspas simples. Por exemplo, 'a' e '%' so constantes do tipo char. As constantes dos tipos numricos (int, float ou double) so representadas por uma seqncia de dgitos decimais, sinalizada ou no, podendo ter ou no um ponto decimal (se o tipo for float ou double). Por exemplo, +123, 15, -16.25 e 8.0 so constantes numricas. Uma constante simblica em C representada antravs do uso do modificador de tipo de acesso const. Isto feito precedendo-se a declarao de uma varivel com a palavra reservada const. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 12 16/05/00
Exemplo
const int i=0,k=6; const float salario=600.25;
Envolver todas as constantes do tipo char com aspas simples funciona para a maioria dos caracteres que podem ser impressos. Uns poucos, porm, so impossveis de entrar pelo teclado. Por essa razo, a linguagem C possui constantes especiais de caracter de barra invertida. Alguns desses caracteres so listados abaixo.
Cdigo Significado
\n Nova linha (LF) \r Retorno do carro (CR) \" Caracter " \' Caracter ' \0 Caracter nulo (zero binrio) \\ Caracter barra invertida
3.5 Expresses
As expresses combinam operandos e operadores pra produzir um nico resultado. Os operandos podem ser constantes, variveis ou valores retornados pelas funes. Assim como as variveis e as constantes, o resultado de uma expresso possui um tipo, que o mesmo dos operandos envolvidos na expresso. Quando constantes e variveis de tipos diferentes so misturadas em uma mesma expresso, devemos levar em conta tambm a converso de tipos. Essa converso segue algumas regras bsicas:
a) char e short so sempre convertidos para int; b) float sempre convertido para double; c) A converso de tipos segue a seguinte precedncia: double ou float long unsigned int, char ou short
Isto , se, por exemplo, um dos operandos for do tipo float, toda a expresso ser convertida e o resultado ser do tipo double. As expresses so avaliadas de acordo com o que se denomina precedncia dos operadores. A introduo de operadores com precedncias distintas altera a ordem de avaliao da expresso, sendo que os de maior precedncia so avaliados antes do que os de menor precedncia. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 13 16/05/00
3.6 Operadores
3.6.1 Operador de Atribuio
O primeiro operador a se visto o operador de atribuio ( = ). A forma geral de uma operao de atribuio :
<var>=<expresso>;
Nesta operao, a expresso direita do operador de atribuio avaliada, e o resultado da avaliao passa a ser o novo valor da varivel esquerda do operador.
Exemplo
soma=a+b; fator=3.0; circunf=2*pi*raio;
A linguagem C permite que mltiplas atribuies sejam feitas em uma nica linha de cdigo.
Exemplo
a=b=c=3.0;
Aps a execuo do cdigo acima, a trs variveis tero o mesmo valor (3.0).
Quando em uma operao de atribuio a expresso direita do operador de um tipo diferente do tipo da varivel esqueda do operador, ocorre uma converso de tipos. Isto , o valor da expresso convertido para o tipo da varivel esquerda do operador. O processo de converso de tipos pode resultar em perda de informao, como pode ser visto na tabela abaixo.
Tipo Destino Tipo Expresso Possvel Informao Perdida
signed char char Se valor > 127, o destino negativo char short int Os 8 bits mais significativos char int Os 8 bits mais significativos char long int Os 24 bits mais significativos int long int Os 16 bits mais significativos int float A parte fracionria e possivelmente mais float double Preciso, o resultado arredondado double long double Preciso, o resultado arredondado Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 14 16/05/00
3.6.2 Operadores Aritmticos
A linguagem C suporta os operadores aritmticos usuais:
O operador % no pode ser aplicado a operandos do tipo float e do tipo double. A precedncia dos operadores aritmticos a usual.
Em C, qualquer atribuio entre parnteses e considerada uma expresso que tem o valor da atribuio que est sendo feita.
Exemplo
a=3+2*(b=x/2); Esta construo equivalente a: b=x/2; a=3+2*b;
3.6.3 Relacionais e Lgicos
A linguagem C suporta os seguintes operadores relacionais:
> (maior) >= (maior ou igual) < (menor) <= (menor ou igual) == (igual) != (diferente)
E os seguintes operadores lgicos:
&& (e) || (ou) ! (no)
A tabela abaixo mostra a precedncia dos operadores lgicos e relacionais (do maior para o menor): Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 15 16/05/00
Maior ! > >= < <= == != && Menor ||
Uma caracterstica bastante peculiar da linguagem C o seu conceito de verdadeiro e falso. A linguagem C no possui um tipo de dados lgico (ou booleano). Em C, qualquer expresso cujo valor seja diferente de zero avaliada como verdadeira. Em contrapartida, qualquer expresso cujo valor seja igual a zero avaliada como falsa. As expresses que usam operadores lgicos e relacionais devolvem zero quando o resultado falso, e um quando o resultado verdadeiro.
3.7 Comandos de Controle
3.7.1 Bloco de Comandos
Um bloco de comandos simplesmente um grupo de comandos relacionados que so tratados como uma unidade. Os comandos que constituem um bloco esto logicamente conectados. Um bloco comea com um { e termina com um } correspondente. Os programadores normalmente usam blocos de comandos para criar estruturas multicomandos associadas com um outro comando como, por exemplo, um if. No entanto, podemos inserir um bloco de comandos em qualquer ponto do programa onde seja possvel colocar um outro comando qualquer.
Exemplo
#include <stdio.h> void main(void) { int i; { /* bloco de comandos */ i=120; printf("%d",i); } }
3.7.2 Comando if
Um desvio condicional usado quando se deseja escolher um dentre dois comandos ou bloco de comandos a ser executado, dependendo de uma condio. A linguagem C possui duas formas para o desvio condicional: Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 16 16/05/00
if(<expresso>) <comando>;
A expresso avaliada. Se o seu valor for verdadeiro, comando executado. O processamento continua no primeiro comando aps o if.
Exemplo
#include <stdio.h> void main(void) { int i; printf("Entre com o valor de a"); scanf("%d",&a); if(a>0) printf("a e maior que zero\n"); }
if(<expresso>) <comando1>; else <comando2>;
A expresso avaliada. Se o seu valor for verdadeiro, comando1 executado; seno, comando2 executado. O processamento continua no primeiro comando aps o if.
Exemplo
#include <stdio.h> void main(void) { int i; printf("Entre com o valor de a"); scanf("%d",&a); if(a>0) printf("a e maior que zero\n"); else printf("a e menor ou igual a zero\n"); }
Nas duas formas acima, comando pode ser tanto um comando simples quanto um bloco de comandos.
3.7.3 Comando while
O comando while usado quando se deseja repetir um comando ou bloco de comandos equanto uma dada condio permanecer verdadeira. A forma do comando while : Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 17 16/05/00
while(<expresso>) <comando>;
A execuo do while segue os seguintes passos:
a) a <expresso> avaliada; b) se a expresso for verdadeira o comando executado e o fluxo de execuo retorna para o passo a). Caso no seja, o fluxo da execuo prosseguir do comando seguinte ao while.
Na sintaxe descrita acima, comando pode ser tanto um comando simples quanto um bloco de comandos.
3.8 Funes de E/S pelo Console
As funes printf() e scanf() realizam entrada e sada formatada; isto , elas podem ler e escrever dados em vrios formatos. A funo printf() escreve dados no vdeo, equanto a funo scanf() l dados do teclado. As duas funes podem operar sobre quaisquer dos tipos de dados primitivos fornecidos pela linguagem C, incluindo carcteres, nmeros inteiros e nmeros decimais (ponto flutuante). Como os cabealhos das funes printf() e scanf() esto no arquivo STDIO.H, necessrio incluir a diretiva de compilao #include <stdio.h> no incio de todos os programas C que utilizam estas duas funes.
3.8.1 Funo de Sada printf()
O prottipo da funo printf()
int printf(<string_de_controle>,<lista_de_argumentos>);
O string_de_controle consiste de trs tipos de dados. O primeiro tipo formado por caracteres que sero exibidos no monitor. O segundo contm comandos de formato, que definem a maneira pela qual os argumentos subseqentes sero mostrados. O terceiro composto por caracteres como o \n (caracter de mudana de Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 18 16/05/00
linha), que no podem ser digitados diretamente pelo teclado, e que so escritos em C utilizando a combinao do carater \ (barra invertida) com outros caracteres. Um comando de formato comea com o caracter % (percentagem) seguido pelo cdigo do formato. Deve haver o mesmo nmero de argumentos, na lista_de_argumentos, que o nmero de comandos de formato. A associao entre um comando de formato e o argumento correspondente feita pela ordem que ambos ocupam, da esquerda para a direita. Por exemplo, o comando
printf(A nota do aluno %d e %5.2f ,1234,8.5);
exibir no vdeo
A nota do aluno 1234 e 8.50
Abaixo so mostrados alguns comandos de formato da funo printf().
Cdigo Formato %c Caracter %d Inteiro com sinal %i Inteiro com sinal %e Notao cientfica (e minsculo) %E Notao cientfica (E maisculo) %f Ponto flutuante %s String de caracteres %u Inteiro sem sinal %% Escreve o caracter %
Um nmero inteiro colocado entre o sinal de percentagem e o comando de formato atua como um especificador de largura mnima do campo. Por exemplo, %5d e %05d. Para especificar o nmero de casas decimais a ser escrito em um nmero em ponto flutuante, coloque um ponto decimal seguido do nmero de casas decimais desejado aps o especificador de largura mnima do campo. Por exemplo %8.4f.
3.8.2 Funes de Entrada scanf()
O prottipo da funo scanf()
int scanf(<string_de_controle>,<lista_de_argumentos>);
O string_de_controle usa a maioria dos cdigos de formatao utilizados em printf(). Logo, usa-se %d para ler um nmero inteiro, %f para ler um nmero em ponto flutuante, %c para ler um caracter, e etc. Os espaos em branco no string_de_controle so ignorados. A lista_de_argumentos composta por uma lista de variveis; cada uma das quais precedida pelo caracter &. Deve haver o mesmo nmero de variveis, na lista_de_argumentos, que o nmero de comandos de formato. A associao entre um Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 19 16/05/00
comando de formato e a varivel correspondente feita pela ordem que ambos ocupam, da esquerda para a direita. Por exemplo, o comando
scanf(%d %f,&matricula,¬a);
faz com que o programa pare a execuo e espere que sejam digitados um nmero inteiro e um nmero decimal. Os nmeros podem ser separados por um ou mais espaos em branco, Tab, ou Enter. O primeiro nmero lido ser atribuido varivel matricula, e o segundo varivel nota. A funo scanf() interrompe a sua execuo quando todos os dados forem lidos, ou quando um dado no for compatvel com o comando de formato correspondente.
3.9 Funes de E/S de Caracteres
Ao escrevermos um programa aplicativo qualquer, freqentemente nos deparamos com a necessidade de ler do teclado (ou exibir no vdeo) dados que so compostos exclusivamente por caracteres. Por exemplo:
opes de menu; respostas (s (sim) ou n (no)); cadeias de caracteres.
Para tal, a linguagem C nos fornece um conjunto de funes que sero vistas a seguir.
3.9.1 A Funo getchar()
O prottipo da funo getchar()
#include <stdio.h> int getchar(void);
A funo getchar() l um caracter do teclado. Se for executada com sucesso ela retornar o caracter lido (cdigo ASCII). Se ocorrer falha ou for detectado o fim-de- arquivo, ela retornar EOF. Outro aspecto importante que ela suporta o redirecionamento de STDIN.
Exemplo
#include <stdio.h> #include <ctype.h> void main(void) { char letra; printf("Digite S ou N para continuar e tecle ENTER\n"); do{ Linguagem e Tcnica de Programao I
A funo putchar() exibe um caracter no monitor. Se for executada com sucesso ela retornar o caracter exibido (cdigo ASCII). Se ocorrer falha ela retornar EOF. Outro aspecto importante que ela suporta o redirecionamento de STDOUT.
A funo getchar() armazena os caracteres lidos do teclado em um buffer at que se tecle Enter. Nesse momento os caracteres digitados ficam disponveis para o programa. Pode-se usar a tecla de Backspace para alterar uma entrada incorreta. O "sistema" acrescenta o caracter de new line (\n) cadeia de caracteres quando for detectado que um Enter foi teclado.
As funes getche() e getch() fazem entrada direta pelo teclado. Nesta modalidade os caracteres so disponibilizados para o programa assim que eles forem digitados, no sendo obrigatrio teclar Enter. Desta forma, no se pode usar a tecla de Backspace para alterar uma entrada incorreta, sendo necessrio implementar a correo atravs de programao. O "sistema" acrescenta o caracter de retorno de carro (\r) cadeia de caracteres quando for detectado que um Enter foi teclado.
3.9.5 A Funo getche()
O prottipo da funo getche()
#include <conio.h> int getche(void);
A funo getche() l um caracter do teclado. Se for executada com sucesso ela retornar o caracter lido (cdigo ASCII). Se ocorrer falha ela retornar EOF. Outro aspecto importante que ela no suporta o redirecionamento de STDIN.
Exemplo
#include <stdio.h> #include <conio.h> #include <ctype.h> void main(void) { char letra; printf("Voce quer Continuar? S ou N?\n"); do{ letra=toupper(getche()); }while ((letra!='S')&&(letra!='N')); if (letra=='S') printf("\nSua resposta foi SIM\n"); else printf("\nSua resposta foi NAO\n"); }
3.9.6 A Funo getch()
O prottipo da funo getch()
#include <conio.h> int getch(void);
A funo getch() semelhante funo getche(), porm, os caracteres lidos do teclado no so exibidos automaticamente no vdeo. Uma boa utilizao para getch() na digitao de senhas. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 22 16/05/00
Exemplo
#include <stdio.h> #include <ctype.h> #include <conio.h> void main(void) { char letra; printf("Digite uma cadeia de caracteres e tecle ENTER\n"); do{ letra=toupper(getche()); }while ((letra!='\r'); }
3.9.7 Sada Direta
O prottipo da funo putch()
#include <conio.h> int putch(int letra);
A funo putch() exibe um caracter no monitor. Se for executada com sucesso ela retornar o caracter exibido (cdigo ASCII). Se ocorrer falha ela retornar EOF. A funo putch() se comunica diretamente com o BIOS (ou diretamente com a memria de vdeo) do PC. Por outro lado, putchar() se comunica com o sistema de arquivos do sistema operacional, deixando para este a comunicao com o BIOS (ou com a memria de vdeo). A funo putch() no converte um caracter de new line (\n) em uma seqncia de retorno de carro e alimentao de linha.
3.9.8 Funes Adicionais de Caracteres
Abaixo esto relacionadas algumas funes teis na manipulao de caracteres. Os cabealhos destas funes esto definidos no arquivo CTYPE.H.
Cabealho Descrio
int isalnum(int ch); Devolve um valor diferente de zero se o argumento for uma letra ou um dgito; caso contrrio devolve zero. int isalpha(int ch); Devolve um valor diferente de zero se o argumento for uma letra; caso contrrio devolve zero. int isdigit(int ch); Devolve um valor diferente de zero se o argumento for um dgito; caso contrrio devolve zero. int islower(int ch); Devolve um valor diferente de zero se o Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 23 16/05/00
argumento for uma letra minscula; caso contrrio devolve zero. int isupper(int ch); Devolve um valor diferente de zero se o argumento for uma letra maiscula; caso contrrio devolve zero. int tolower(int ch); Devolve o equivalente minsculo se o argumento for uma letra; caso contrrio o argumento devolvido sem alterao. int toupper(int ch); Devolve o equivalente maisculo se o argumento for uma letra; caso contrrio o argumento devolvido sem alterao.
3.10 O Comando switch
A linguagem C possui um comando de seleo mltipla, switch, que testa sucessivamente o valor de uma expresso contra uma lista de constantes inteiras ou de caracter. Quando o valor coincide, os comandos associados quela constante so executados. A forma geral do switch
switch(<expresso>) { case <constante1>: <seqncia de comandos> break; case <constante2>: <seqncia de comandos> break; . . . default: <seqncia de comandos> }
A execuo deste comando segue os seguintes passos:
1. A <expresso> avaliada; 2. se o valor da expresso for igual a uma das constantes, a seqncia de comandos associada a esta constante ser executada. Os comandos seguintes continuaro a ser executados at o fim do switch, ou at que um comando break seja encontrado; 3. se o valor da expresso no for igual a nenhuma das constantes presentes no comando, e a opo default tiver sido includa no comando switch, os comandos associados ao default sero executados. Caso a opo default no esteja presente, o processamento prosseguir a partir do comando seguinte ao switch.
Exemplo Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 24 16/05/00
#include<stdio.h> void main(void) { char ch; int i=0;
while((i<=10) && (ch=getch())!='#') { switch(ch) { case 'A': case 'O': i++; putchar('E'); break; case ' ': putchar('!'); case 'L': i+=2;; putchar('$'); break; default: i++; putchar('?'); } } printf("\nAcabou"); }
Observaes:
a) Pode haver um ou mais comandos seguindo o case. Estes comandos no precisam estar escritos entre chaves; b) a expresso em switch(<expresso>) deve ter um valor compatvel com um inteiro; isto , podem ser usadas expresses dos tipos char e int com todas as suas variaes. O uso de expresses do tipo float ou double ir causar erro na compilao; c) o comando break provoca a sada imediata do switch. Se no existir um break seguindo os comandos associados a um case, o programa prosseguir executando todos os comandos associados ao case abaixo. Esta caracterstica pode ser interessante em algumas circunstncias.
3.11 O Comando for
O formato geral do comando for da linguagem C encontrado, de uma forma ou de outra, em todas as linguagens de programao baseadas em procedimentos. Contudo, em C, ele fornece maior flexibilidade. A forma geral do comando for
O comando for permite muitas variaes. Entretanto, a <expresso1> , geralmente, um comando de atribuio que usado para colocar um valor na varivel de controle do lao. A <expresso2> uma expresso relacional que determina quando o lao acaba. A <expresso3> o incremento que define como a varivel de controle do lao varia a cada vez que o lao repetido. As trs sees principais do comando for devem ser separadas por pontos-e-vrgulas. Uma vez que a <expresso2> seja avaliada como falso, a execuo do programa continua no comando seguinte ao for.
Exemplo
#include<stdio.h> void main(void) { int i;
for(i=1;i<=10;i++) printf("%d \n",i) }
4. Funes
Freqentemente, uma certa seqncia de comandos deve ser repetida em vrios lugares de um programa. A fim de economizar o tempo gasto com o trabalho de copiar estas seqncias, vrias linguagens de programao, entre elas a linguagem C, incluem o conceito de subprograma (ou subrotina): atribui-se um nome uma seqncia de comandos, e faz-se referncia a este nome nos vrios lugares do programa onde a seqncia em questo deveria ser repetida. O uso das funes tem por objetivo tambm dividir grandes tarefas de computao em tarefas menores, facilitando assim o gerenciamento de grandes sistemas e aumentando a confiabilidade dos mesmos. Funes apropriadas podem freqentemente esconder detalhes de operao de partes de um programa que no necessitam ser conhecidas por outras partes do mesmo programa. Isto facilita a evoluo dos sistemas atravs do tempo j que as mudanas nos requisitos dos sistemas no so necessariamente propagadas por todas as sees de cdigo dos programas. Alm disso, o uso de funes aumenta a confiabilidade dos sistemas na medida em que mais e mais programadores usem o mesmo cdigo em seus programas, aumentando assim a probabilidade de descobrir eventuais erros, e facilitando a correo dos mesmos nos diverso programas que fazem uso do cdigo em questo.
4.1 Estrutura das Funes em C
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 26 16/05/00
A estrutura de uma funo na linguagem C bastante semelhante da funo main(). A nica diferena que main() a primeira funo a ser chamada quando o programa executado. Todas as funes em C comeam com um nome seguido de parnteses (que envolve ou no uma lista de argumentos) e, aps isto, chaves que envolvem o corpo da funo.
4.2 Chamando Funes
As funes por ns desenvolvidas so chamadas do mesmo modo que as funes da biblioteca C (printf(), getchar(), scanf() e etc): minhaFuncao(). Os parnteses que seguem o nome so necessrios para que o compilador possa diferenciar a chamada de uma funo de uma varivel que no foi declarada. Visto que a chamada de uma funo constitui um comando de programa, esta deve ser encerrada por ponto-e-vrgula.
minhaFuncao();
Porm, na definio de uma funo o ponto-e-vrgula no pode ser usado.
minhaFuncao()
Exemplo
#include <stdio.h>
int fatorial(int num) { int fat=1;
for(;num>1;num--) fat*=num; return fat; }
void main(void) { int num;
printf("Informe um Numero >= 0\n"); scanf("%d",&num); if(num<0) printf("O Numero num nao e um numero natural\n"); else printf("O fatorial de %d e %d\n",num,fatorial(num)); }
interessante notar no exemplo acima que a funo fatorial() foi definida antes da funo main(), de onde ela chamada. Isto se deve ao fato de o compilador ter que conhecer o nome, o nmero e os tipo dos parmetros, e o tipo do valor a ser retornado Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 27 16/05/00
pela funo no momento em que for encontrada uma chamada funo. Se a funo no tiver sido declarada, o compilador acusar um erro. Podemos alternativamente declarar apenas o cabealho da funo (nome, nmero e tipo dos parmetros, e o tipo do valor a ser retornado pela funo) antes de referenci- la, postergando assim a sua definio. Quando da definio da funo, o cabealho anteriormente declarado no pode ser modificado.
Exemplo
#include <stdio.h>
int fatorial(int);
void main(void) { /* Os mesmos comandos do exemplo anterior */ }
int fatorial(int num) { int fat=1;
for(;num>1;num--) fat*=num; return fat; }
4.3 Variveis Locais
As variveis declaradas dentro de uma funo so chamadas variveis locais, e so conhecidas somente dentro da funo onde so declaradas. Por exemplo, a varivel fat do exemplo acima conhecida somente dentro do escopo da funo fatorial(), sendo invisvel s demais funes, incluindo main(). Se inclussemos em main() a instruo
printf(%d,fat);
teramos um erro de compilao, pois main() no conhece a varivel fat. Uma varivel local conhecida em C como varivel automtica, pois ela automaticamente criada quando a funo ativada e destruda na sada da funo.
4.4 Variveis Globais
Uma varivel dita global quando for declarada fora do escopo de qualquer funo. Com isto, ela torna-se acessvel a todas as funes, desde o ponto da declarao da varivel at o fim do programa. Como as variveis globais so acessveis por todas as funes do programa fonte, elas se constituem em uma alternativa para a troca de informaes entre funes. No Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 28 16/05/00
exemplo a seguir, ao invs de passarmos um nmero como parmetro para a funo fatorial() iremos declarar a varivel num como global. Iremos declarar tambm a varivel fat como global; dessa forma a funo no retornar nenhum valor explicitamente.
Exemplo
#include <stdio.h>
int num,fat;
void fatorial(void) { fat=1;
for(;num>1;num--) fat*=num; }
void main(void) { printf("Informe um Numero >= 0\n"); scanf("%d",&num);
if(num<0) printf("O Numero num nao e um numero natural\n"); else { fatorial(); printf("O fatorial de %d e %d\n",num,fat); } }
As variveis globais devem ser usadas com muita cautela pois, por serem acessveis a todas as funes, aumentam a interdependncia entre as mesmas (acoplamento), dificultando o reuso das funes e propagando eventuais modificaes em uma funo por todo o cdigo, o que algo indesejvel. Desse modo, no ser aceito durante o curso o uso de variveis globais.
4.5 Passando Dados Para a Funo Chamada
O mecanismo usado para transmitir informaes para uma funo chamado parmetro. No exemplo que est sendo usado, a funo fatorial(), a definio da funo a seguinte:
int fatorial(int num)
nela informamos ao compilador que a funo fatorial() requer um parmetro, chamado num, que do tipo int. A varivel num na verdade uma nova varivel e Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 29 16/05/00
chamada de parmetro formal, funcionando exatamente como uma varivel local da funo; isto , criada quando a funo inicia sua execuo e destruda quando a funo retorna.
4.6 Passando Dados Para a Funo Chamada - Passagem por Valor
Quando uma funo chamada devemos fornecer os argumentos (ou parmetros reais) para a funo. Os argumentos podem ser variveis, constantes ou quaisquer expresses, desde que os tipos dos argumentos sejam compatveis com os tipos dos parmetros. Em C, todos os argumentos de funes so passados por valor. Isto significa que funo chamada dada uma cpia dos valores dos argumentos. Os valores dos argumentos so armazenados em reas de memria temporrias, alocadas quando a funo ativada, e so acessveis atravs das variveis declaradas como parmetros no cabealho da funo. Desta forma, em C, uma funo no pode alterar o valor de uma varivel passada como parmetro pela funo chamadora; ela s pode alterar a cpia.
4.7 O Valor Retornado por uma Funo
J vimos funes da biblioteca C que retornam um valor. Por exemplo:
ch=getchar();
para que uma funo retorne um valor explicitamente necessrio usar o comando return, cuja sintaxe :
return expresso;
O comando return tem dois usos importantes. Primeiro, voc pode us-lo para devolver um valor e retornar imediatamente para a instruo seguinte (dentro da funo chamadora) instruo que causou a chamada da funo em questo. Segundo, voc pode us-lo, sem a expresso, para causar a sada imediata da funo na qual ele se encontra; isto , o return far com que a execuo do programa volte para a funo chamadora assim que for executado, o que ocorre, em geral, antes da ltima instruo da funo. Pode-se escrever quantos return forem necessrios dentro de uma funo. Existe um return implcito aps o ltimo comando de uma funo. Logo, mesmo que no se escreva nenhum comando return em uma funo, o fluxo de execuo do programa retornar para a funo chamadora aps a execuo do ltimo comando de uma funo.
4.8 O Tipo do Valor Retornado por uma Funo
O tipo de uma funo determinado pelo tipo de valor que ele retorna e no pelo tipo dos seus argumentos. Uma funo pode retornar um nico valor de qualquer tipo Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 30 16/05/00
primitivo da linguagem C, isto : char, int e float. Porm, como devemos declarar funes que no retornam valor algum? O comit de padro do American National Standards Institute (ANSI) batizou as funes que no retornam valores como tendo o tipo void. O tipo void tambm usado no cabealho das funes que no recebem parmetros. O melhor exemplo de tais funes a funo main(). A funo main(), que pode receber parmetros e retornar um valor, tem sido at agora usada nos nossos exemplos como
void main(void);
Isto que dizer que a funo main(), nos nossos exemplos, no recebe nenhum parmetro e no retorna nenhum valor.
4.9 Passando Dados Para a Funo Chamada - Passagem por Referncia
Apesar de a linguagem C suportar apenas a passagem de parmetros por valor, podemos simular a passagem de parmetros por referncia passando o endereo de uma varivel como argumento para uma funo. Na funo chamada, o parmetro correspondente ao argumento em questo declarado como sendo um ponteiro para uma rea de memria. Uma referncia ao parmetro, na unidade chamada, tratada como uma referncia posio de memria cujo endereo passado como argumento. A varivel usada como argumento ento compartilhada; isto , pode ser modificada pela funo chamada. O exemplo abaixo mostra uma funo que troca os valores de duas variveis inteira.
Exemplo
#include <stdio.h>
void troca(int *x, int *y) { int aux;
aux=*x; *x=*y; *y=aux; }
void main(void) { int a=10,b=20; troca(&a,&b); }
No exemplo acima, vamos supor que o compilador tenha alocado as reas de memria $1000 e $2000 para as variveis a e b respectivamente. Desta maneira, ao chamarmos a funo troca() com os argumentos (as variveis a e b) sendo precedidos Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 31 16/05/00
pelo operador & (o operador & retorna o endereo de uma varivel), os endereos $1000 e $2000 so passados como argumentos. Os parmetros x e y declarados na funo troca() tambm ocupam reas de memria; por exemplo, $6000 e $7000 respectivamente. Porm, estes parmetros no armazenam valores inteiros, como o caso das variveis a e b. Ao invs disso, eles armazenam endereos de memria. Estas reas de memria, por sua vez, armazenam valores inteiros. Isto indicado na declarao dos parmetros ao preced-los com o operador * (o operador * informa que uma varivel um ponteiro; isto , armazena um endereo de memria). Logo, no exemplo acima, aps a chamada da funo troca(), os parmetros x e y iro armazenar os endereos $1000 e $2000. Nas instrues da funo troca(), todas as vezes que os parmetros x e y forem precedidos pelo operador *, estaremos acessando as reas de memria cujos os endereos esto armazenados nos parmetros x e y. Neste exemplo, os endereos so $1000 e $2000, endereos das variveis a e b respectivamente. Desta maneira, ao alterarmos os contedos das reas de memria apontadas pelos parmetros x e y ($1000 e $2000), estaremos alterando o contedo das variveis a e b, definidas na funo main(). As figuras abaixo ilustram o que foi dito acima. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 32 16/05/00
10 $1000 20 $2000 $1000 $6000 $2000 $7000 Logo Aps a Chamada da Funo troca() 20 $1000 20 $2000 $1000 $6000 $2000 $7000 Logo Aps a Execuo da Instruo *x=*y; 20 $1000 10 $2000 $1000 $6000 $2000 $7000 Logo Aps a Execuo da Instruo *y=aux; Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 33 16/05/00
5 Vetores
Nem sempre os tipos bsicos (int, char e float) so suficientes para exprimir estruturas de dados em algoritmos. Por exemplo, considere o problema no qual se deseja calcular a mdia das notas dos alunos de uma turma e exibir no vdeo aquelas que forem menores do que a mdia. Para calcular a mdia so necessrias duas variveis: uma para armazenar o somatrio das notas e outra para armazenar o nmero de alunos da turma. Porm, para exibir as notas maiores do que a mdia necessrio armazen-las para posterior comparao com a mdia, pois o clculo da mdia s poder ser feito aps a leitura de todas as notas. A alternativa de ler os dados de entrada (neste caso, as notas) duas vezes est fora de questo. Com as estruturas de dados de que dispomos at o momento, na verdade apenas variveis de tipos bsicos, seria necessrio criar tantas variveis quantas fossem as notas a serem processadas. Para um nmero pequeno de alunos, digamos 5, no haveria muitos inconvenientes. Porm, para um nmero muito grande de alunos esta abordagem tornaria impraticvel a redao do algoritmo. A partir do que dito, fica clara a necessidade de se dipor de novos tipos de dados para modelar problemas como o que foi descrito acima. Um destes tipos, o vetor, ser estudado nesta unidade.
5.1 Usando Vetores
Para o problema sendo abordado, seria ideal uma estrutura de dados que armazenasse todas as notas, e que pudesse ser referenciada pelo conjunto ou por cada nota individualmente:
Exemplo:
notas
Um vetor um tipo de dado usado para representar uma certa quantidade de valores homogneos. Podemos imaginar um vetor como uma srie de variveis do mesmo tipo referenciadas por um nico nome, onde cada varivel diferenciada atravs de um nmero chamado de ndice. Colchetes so usados para conter o ndice. A declarao int notas[5] aloca memria para armazenar 5 inteiros, e informa que notas um vetor de 5 elementos. Uma vez declarado o vetor, precisamos de um modo para referenciar seus elementos individualmente. Isto feito atravs de um nmero entre colchetes seguindo o nome do vetor. Observe que este nmero tem significados (semntica) diferentes quando referencia um elemento do vetor e na declarao do mesmo; onde indica o seu tamanho. 5,0 3,0 8,0 .......... 9,5
1 2 3 100 Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 34 16/05/00
Quando referenciamos um elemento do vetor, este nmero especifica a posio do elemento na lista. Os elementos do vetor so sempre numerados por ndices iniciados por zero. O elemento referenciado pelo nmero 2, notas[2], no o segundo elemento do vetor, e sim o terceiro, pois a numerao comea em zero. Assim,o ltimo elemento do vetor possui um ndice que uma unidade menor do que o tamanho do vetor. Nos nossos programas utilizaremos uma varivel inteira, por exemplo i, como ndice de um vetor. Esta possibilidade, associada ao uso de estruturas de repetio, como while e for, torna os vetores verdadeiramente teis.
Exemplo:
notas[i]=2.5;
O programa abaixo a implementao do problema das notas abordado acima.
Exemplo:
#include <stdio.h> #define AND && #define MAXALUNO 10
void main(void) { int qtdAlunos=0,i; float soma=0.0,media,nota,notas[MAXALUNO];
printf("Informe uma Nota - Nota Negativa Termina\n"); scanf("%f",¬a); while(nota>=0 AND qtdAlunos<MAXALUNO) { notas[qtdAlunos]=nota; soma+=nota; qtdAlunos++; printf("Informe uma Nota - Nota Negativa Termina\n"); scanf("%f",¬a); }
if(qtdAlunos>0) { media=soma/qtdAlunos; printf("Media da Turma - %5.2f\n",media);
A necessidade de procurar uma informao em uma tabela, em um vetor, ou em um catlogo uma tarefa muito comum na rea da computao. Por exemplo, procurar o telefone de uma pessoa em um catlogo telefnico, procurar o valor do IPVA em uma tabela que fornea o valor do imposto em funo do modelo e do ano de fabricao do veculo, consultar a quantidade existente em estoque de uma determinada mercadoria em uma tabela que armazena a quantidade de itens da mercadoria e o cdigo da mesma. Como esta funo muito utilizada, importante utilizarmos algoritmos que a executem de maneira eficiente; ou seja, no menor tempo possvel.
5.2.1 Busca Seqencial
Neste mtodo, o processo de busca pesquisa a tabela seqencialmente desde o seu incio. A chave de cada elemento da tabela comparado com a chave de busca. Se houver igualdade entre a chave de busca e a chave do elemento da tabela a busca termina, sendo que o ndice do elemento na tabela geralmente a informao de que se necessita. Se o final da tabela for atingido e a chave no for encontrada, temos a condio na qual nenhum elemento da tabela tem uma chave igual a chave de busca.
Exemplo
#include <stdio.h> #define MAX 10
void main(void) { int i,cod, int tabCod[MAX]={145,567,84,27,300,432,13,608,913,47}; int qtd,tabQtd[MAX];
for(i=0;i<MAX;i++) { printf("Informe a Qtd do Produto %3d\n",tabCod[i]); scanf("%d",&tabQtd[i]); }
printf("Informe o Codigo do Produto\n"); scanf("%d",&cod);
if(i==MAX) printf("Produto Inexistente\n"); else Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 36 16/05/00
{ printf("Informe a Quantidade\n"); scanf("%d",&qtd); if(qtd>tabQtd[i]) printf("Estoque Insuficiente - %3d\n",tabQtd[i]); else { tabQtd[i]-=qtd; printf("Venda Efetuada\n"); } } printf("Informe o Codigo do Produto\n"); scanf("%d",&cod); }
printf("POSICAO DO ESTOQUE\n\n"); printf("CODIGO QTD\n\n"); for(i=0;i<MAX;i++) printf(" %3d %3d\n",tabCod[i],tabQtd[i]); }
Uma empresa possui dez tipos de parafusos em estoque, onde cada um dos tipos possui um cdigo de identificao (145, 567, 84, 27, 300, 432, 13, 608, 913, 047). No incio do dia o programa de controle de estoque pede ao estoquista que fornea a quantidade em estoque (em Kg) de cada um dos tipos de parafusos, armazenando-as em um vetor. Da em diante, o programa em ir ler continuamente cdigos de parafusos do teclado e consultar a tabela para verificar se existe o tipo de parafuso com o cdigo fornecido pelo vendedor. Caso o cdigo exista, o programa ir solicitar ao vendedor a quantidade de parafusos a ser vendida. Se a quantidade existente no estoque for suficiente para atender ao pedido, a mensagem Venda Efetuada ser exibida no monitor e o estoque ser atualizado. Caso contrrio, a mensagem a ser exibida ser Estoque Insuficiente, juntamente com a quantidade em estoque do produto solicitado. No final do programa, que ser detectado quando o cdigo digitado for zero, dever ser exibida no monitor a listagem dos produtos com os seus respectivos nveis de estoque.
5.2.2 Busca Seqencial com Sentinela
O algoritmo anterior pode ser otimizado inserindo-se a chave procurada no final do vetor (este deve, portanto, poder armazenar um elemento a mais). A busca termina quando a chave for encontrada, o que certamente ocorrer. Porm, se a chave for encontrada na ltima posio da tabela saberemos que ela no se encontra na tabela original (sem o ltimo elemento, inserido artificialmente). O programa abaixo tem a mesma funcionalidade do exemplo anterior, porm o algoritmo de busca implementado o de busca seqencial com sentinela. As diferenas para o algoritmo de busca seqencial, visto anteriormente, est assinalada em negrito. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 37 16/05/00
Exemplo
#include <stdio.h> #define MAX 10
void main(void) { int i,cod,int int tabCod[MAX+1]={145,567,84,27,300,432,13,608,913,47}; int qtd,tabQtd[MAX+1];
for(i=0;i<MAX;i++) { printf("Informe a Qtd do Produto %3d\n",tabCod[i]); scanf("%d",&tabQtd[i]); }
printf("Informe o Codigo do Produto\n"); scanf("%d",&cod);
if(i==MAX) printf("Produto Inexistente\n"); else { printf("Informe a Quantidade\n"); scanf("%d",&qtd); if(qtd>tabQtd[i]) printf("Estoque Insuficiente - %3d\n",tabQtd[i]); else { tabQtd[i]-=qtd; printf("Venda Efetuada\n"); } } printf("Informe o Codigo do Produto\n"); scanf("%d",&cod); }
printf("POSICAO DO ESTOQUE\n\n"); printf("CODIGO QTD\n\n"); for(i=0;i<MAX;i++) printf(" %3d %3d\n",tabCod[i],tabQtd[i]); }
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 38 16/05/00
Nos dois casos descritos acima, partimos da premissa de que a distribuio das chaves na tabela no obedece a nenhuma ordem preestabelecida. Desta forma, podemos esperar que, ao encontrarmos uma determinada chave, esta possa estar na primeira posio, ou na ltima posio, ou em qualquer outra posio da tabela com igual probabilidade. Assim, estes algoritmos executaro, em mdia, n/2 comparaes, onde n o nmero de elementos da tabela. Diz-se ento que a complexidade destes algoritmos da ordem de n e escreve-se O(n).
5.2.3 Busca em Vetores Ordenados
Quando uma tabela estiver ordenada segundo uma chave (chave de ordenao), que seja a mesma chave usada em uma busca, podemos usar um algoritmo mais eficiente do que os algoritmos vistos anteriormente. Neste caso, no ser necessrio examinar todos os elementos de uma tabela para constatar que nenhum elemento da tabela atende ao critrio de busca. Para isso, basta que encontremos um elemento da tabela cujo valor da chave de ordenao seja maior do que valor da chave de pesquisa.
Exemplo
int busca(int vet[],int tam, int chave, int *pos) { *pos=0; while(*pos<tam) if(vet[*pos]==chave) return 1; else if(vet[*pos]>chave) return 0; else (*pos)++; return 0; } Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 39 16/05/00
5.2.3.1 Busca Binria
O mtodo mais ficiente para uma busca em uma tabela ordenada a busca binria. Basicamente, o algoritmo da busca binria consiste em compararmos a chave de busca com a chave de ordenao do elemento "central" da tabela. Se elas forem iguais, a busca termina com sucesso; caso contrrio, se a chave de busca for maior do que o valor da chave de ordenao do elemento "central", o mesmo mtodo de busca ser aplicado aos elementos direita do elemento "central". Caso a chave de busca seja menor do que o valor da chave de ordenao do elemento "central", o mesmo mtodo de busca ser aplicado aos elementos esquerda do elemento central. Em ambas as situaes, os elementos restantes so desprezados.
A cada iterao, o algoritmo de busca binria reduz o tamanho da tabela metade do tamanho da iterao anterior. Logo, o nmero mximo de comparaes que sero feitas ser log 2 n. Diz-se ento que a complexidade deste algoritmos da ordem de log n e escreve-se O(log n). 20 30 40 50 60 70 80 90 10 100 incio fim meio Chave de Busca = 60 1a. Iterao 20 30 40 50 60 70 80 90 10 100 incio fim meio 2a. Iterao 20 30 40 50 60 70 80 90 10 100 incio fim meio 3a. Iterao Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 40 16/05/00
Exemplo
int buscaBin(int vet[],int tam,int chave,int *meio) { int fim=tam-1,ini=0;
A classificao ou ordenao de dados constitui uma das tarefas mais freqentes e importantes em processamento de dados, sendo, normalmente, auxiliar ou preparatria, visando tornar mais simples e eficiente as demais. A emisso de relatrios, por exemplo, usualmente feita usando como entrada um ou mais arquivos classificados. O processo de classificao de um conjunto de dados inteiramente contido na memria principal chamado de classificao interna, ao passo que a classificao de um conjunto de dados no inteiramente armazenado na memria principal chamada de classificao externa.
5.3.1 Classificao por Troca Mtodo da Bolha
No algoritmo que ser visto a seguir iremos classificar uma tabela composta por duas colunas. Cada uma das colunas ser implementada atravs de um vetor de um tipo de dados compatvel com a natureza dos dados armazenados nas colunas da tabela. A classificao ser feita em relao aos valores armazenados em uma nica coluna da tabela (chave de ordenao). Porm, quando trocarmos dois elementos de posio entre si no vetor de chaves de ordenao, a mesma troca ser efetuada nos outros vetores que implementam as demais colunas da tabela, para que o relacionamento entre os campos de uma mesma linha da tabela seja preservado. O Mtodo da Bolha um dos mtodos mais simples de classificao de dados. A cada passo, cada elemento do vetor de chaves comparado com o seu sucessor, sendo os dois trocados de posio caso estejam fora de ordem. Considere o seguinte vetor de chaves: Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 41 16/05/00
As seguintes comparaes so feitas ao percorremos o vetor pela primeira vez (1 a
passagem):
vet1[1] com vet1[2] (25 com 57) sem troca vet1[2] com vet1[3] (57 com 48) troca vet1[3] com vet1[4] (57 com 37) troca vet1[4] com vet1[5] (57 com 12) troca vet1[5] com vet1[6] (57 com 92) sem troca vet1[6] com vet1[7] (92 com 86) troca vet1[7] com vet1[8] (92 com 33) troca
Logo, aps a 1 a passagem o vetor ter a seguinte configurao:
Note que aps a 1 a passagem o maior elemento (nesse caso 92) ocupa a sua devida posio no vetor. Aps a 2 a passagem a configurao ser:
Note que o elemento 86 foi colocado na segunda maior posio dentro do vetor. Logo, desde que cada iterao coloca um novo elemento na sua devida posio, um vetor com n elementos ir requerer no mais que n-1 iteraes para estar completamente ordenado. A funo abaixo implementa o algoritmo acima descrito para uma tabela com duas colunas.
Exemplo
void ordena(int vet1[],float vet2[],int tam) { int aux1,i,j,trocou=1; float aux2;
Apesar de ser um mtodo de ordenao bastante simples, o Mtodo da Bolha muito ineficinte devido, principalmente, ao excessivo nmero de trocas executado. A complexidade deste algoritmos da ordem de n 2 e escreve-se O(n 2 ).
6 Matrizes
No Captulo 5 pudemos verificar a utilidade dos vetores na soluo de diversos problemas que envolviam arranjos homogneos unidimensionais. No entanto, podemos encontrar diversos problemas que envolvam na sua soluo arranjos homogneos multidimensionais, particularmente de duas dimenses. Por exemplo, considere o problema no qual se deseja calcular as mdias das turmas de uma escola e exibir no vdeo as notas dos alunos da turma com a maior mdia. Para calcular as mdias das turmas e armazen-las para posterior processamento necessrio apenas um vetor do tipo float. Porm, para exibir as notas dos alunos da turma que obteve a maior mdia necessrio armazenar as notas de todos os alunos de todas as turmas (agrupados por turma), pois a seleo da turma com a maior mdia s poder ser feita aps a leitura de todas as notas de todas as turmas da escola, e o clculo das mdias das turmas.
6.1 Usando Matrizes
Para representar os dados do problema descrito acima poderamos usar tantos vetores quanto fosse o nmero de turmas existentes no colgio. Porm, seria ideal uma estrutura de dados que armazenasse todas as notas de todas as turmas, e que pudesse ser referenciada pelo conjunto, pelo conjunto das notas de uma turma, ou por cada nota individualmente: Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 43 16/05/00
Exemplo:
notas
Uma matriz um tipo de dados usado para representar uma certa quantidade de valores homogneos de natureza bidimensional. Podemos imaginar uma matriz como sendo um vetor, onde cada elemento deste vetor tambm um vetor. Logo, para referenciarmos um elemento em uma matriz precisamos de dois ndices: o primeiro para selecionar o vetor (linha) e o segundo para selecionar o elemento do vetor previamente selecionado (coluna).
Exemplo:
notas[4][3]=7.5;
A declarao float notas[5][50] aloca memria para armazenar as notas de 5 turmas (linhas), cada uma das quais com 50 alunos (colunas). Neste momento importante diferenciarmos as tabelas, vistas no captulo anterior, das matrizes. Embora normalmente representadas em um forma tabular (linhas e colunas), as matrizes diferem das tabelas por serem estruturas de dados homogneas, tanto em relao aos tipos de dados quanto natureza da informao. Isto , as linhas e as colunas de uma matriz so formadas por elementos do mesmo tipo de dados, e armazenam informaes de mesmo contedo semntico (no exemplo acima cada elemento da matriz armazena uma nota do tipo float). As tabelas, por outro lado, so representadas tambm na forma tabular porm as suas colunas (implementadas atravs de vetores) so heterogneas entre si, armazenando informaes que podem ser at homogneas quanto ao tipo de dados, mas que so heterogneas quanto natureza da informao.
6.2 Percorrendo uma Matriz
Para percorrermos uma matriz so necessrias duas estruturas de repetio aninhadas. A estrutura de repetio mais externa percorrer as linhas da matriz, enquanto a estrutura de repetio mais interna percorrer os elementos (colunas) da linha corrente. O cdigo C a seguir permite que a matriz de notas do nosso problema seja "carregada" a partir do teclado:
Exemplo
for(i=0;i<5;i++) for(j=0;j<50;j++) { printf("Forneca a nota %d da turma %d\n",j+1,i+1); 5,0 3,0 8,0 .......... 9,5 7,3 2,2 5,6 .......... 8,0 5,7 1,8 6,4 ........... 5,9 2,4 4,9 6,0 .......... 3,8 10,0 9,5 3,0 .......... 4,0
1 2 3 ......... 50 Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 44 16/05/00
scanf("%f",¬as[i][j]); }
O cdigo C a seguir permite que a matriz de notas do nosso problema seja exibida no vdeo na forma matricial. Para isso, os elementos de uma mesma linha so exibidos uns aps os outros sem que seja dados um salto de linha (note que no existe \n no string de controle do printf). Aps a exibio de uma linha da matriz emitido um comando para saltar para a prxima linha do vdeo.
At aqui, quando desejamos indicar o nmero de elementos existentes em um vetor qualquer, usamos uma varivel inteira, digamos tam, para tal. Logo, quando necessrio verificar se um dado valor est presente em um vetor executamos um trecho de cdigo semelhante ao descrito abaixo:
Exemplo
int vet[10],i,tam,valor;
for(i=0;i<tam && vet[i]!=valor;i++); if(i==qtd) /* cdigo executado quando o valor */ /* foi encontrado no vetor */ else /* cdigo executado quando o valor */ /* no foi encontrado no vetor */
Uma outra abordagem possvel seria indicarmos o trmino do vetor lgico atravs da presena de um valor que no pertena ao domnio dos valores presentes no vetor. Seja, por exemplo, um vetor de inteiros que armazene as idades dos alunos de uma turma. A presena de um valor negativo poderia ser usada para indicar o fim do vetor de idades, j que no h sentido em uma idade negativa. Note que para esssa abordagem funcionar necessrio que definamos o vetor de idades com pelo menos um elemento a mais do que seria necessrio para armazenar todas as idades dos alunos da turma. Logo, se quisrmos verificar se uma dada idade existe em uma turma de no mximo 30 alunos, o trecho de cdigo a ser executado ser o seguinte:
Exemplo Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 45 16/05/00
int vetIdade[31],i,idade;
for(i=0;vetIdade[i]>=0 && vetIdade[i]!=idade;i++); if(vetIdade[i]>=0) /* cdigo executado quando o valor */ /* foi encontrado no vetor */ else /* cdigo executado quando o valor */ /* no foi encontrado no vetor */
7.1 Strings em C
A linguagem C no possui nenhum tipo primitivo que permita a manipulao de cadeias de caracteres (strings). Desta forma, um string em C implementado atravs do uso de um vetor de caracteres terminado pelo caracter nulo. O caracter nulo tem configurao binria 00000000 (o seu cdigo ASCII 0), e representado pela seqncia '\0' ou pela macro NULL. Para armazenarmos um string de at 10 caracteres escrevemos o comando
char str[11];
O vetor str pode ser inicializado de duas maneiras distintas:
1) listando os caracteres um a um, e acrescentando o caracter nulo no final.
char str[11]={'a','l','o', ' ','m','u','n','d','o','\0'}; 2) usando uma constante do tipo string. Neste caso, o compilador acrescenta o caracter nulo automaticamente.
char str[11]="alo mundo";
7.2 Lendo e Escrevendo Strings
A biblioteca padro de E/S, stdio.h, fornece duas funes para E/S com strings. A funo gets() l um string de caracteres do teclado e o coloca no endereo apontado pelo argumento passado (ponteiro para caracter). Os caracteres digitados so transferidos para a memria aps um Enter. O Enter no se torna parte do string; em seu lugar colocado o caracter nulo (\0).
char *gets(char *str);
Neste prottipo str um vetor de caracteres (ou um ponteiro qualquer para caracteres) que recebe os caracteres digitados pelo usurio. A funo gets() retorna um ponteiro para o string lido se a leitura for feita corretamente; ou retorna NULL caso algum erro tenha ocorrido, ou o fim-de-arquivo tenha sido atingido. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 46 16/05/00
A funo puts() escreve o seu argumento (um string) na tela, seguida por um caracter de nova linha.
int puts(char *str);
A funo puts() reconhece os mesmos caracteres de controle que a funo printf() (por exemplo, \n). Uma chamada puts() requer bem menos tempo do que a mesma chamada printf(), porque puts() pode escrever apenas strings de caracteres, no podendo escrever nmeros ou fazer converses de formato. Portanto, puts() ocupa menos espao na memria, e executada mais rapidamente do que printf().
A funo puts() devolve EOF se ocorrer algum erro. Caso contrrio, ela devolve um valor diferente de zero. No entanto, quando a escrita feita no console, pode-se normalmente assumir que no ocorrer nenhum erro.
O comando a seguir exibe a frase Alo Mundo! no monitor.
puts(Alo Mundo!);
A funo scanf() pode ser utilizada para ler um string da stream de entrada, usando o especificador de formato %s. O %s faz com que scanf() leia caracteres at que seja encontrado um caracter de espao em branco. Os caracteres lidos so colocados em um vetor de caracteres apontado pelo argumento correspondente, e o resultado tem terminao nula (\0). Para scanf(), um caracter de espao em branco um espao, um retorno de carro (Enter), ou uma tabulao. Logo, um string como Alo Mundo! no pode ser lido com scanf() (apenas o substring Alo ser carregado no vetor).
A funo printf() escreve uma string atravs do %s. Ao contrrio da funo puts(), no existe mudana automtica de linha na exibio de um string no vdeo.
Podemos tambm ler um string, caracter a caracter, atravs das funes getchar(), getch() ou getche(). Neste caso, funo do programador acrescentar um caracter \0 aps o string.
O cdigo a seguir exemplifica o que foi dito acima. Linguagem e Tcnica de Programao I
importante lembrar que as funes gets() e scanf() recebem como parmetro um ponteiro para uma rea de memria. Logo, no temos controle sobre a quantidade de caracteres que ser digitada pelos usurios, e sobre a rea de memria que ser utilizada para armazenar os caracteres lidos; j que o tamanho do vetor estabelecido estaticamente.
7.3 Algumas Funes de Strings
A biblioteca de funes de strings, string.h, fornece uma gama variada de funes para a manipulao de strings. As mais comuns so fornecidas pela maioria dos compiladores C. So elas:
7.3.1 strlen()
A funo strlen() retorna o nmero de caracteres em um string (o caracter NULL no contado). O cabealho da funo strlen() o seguinte:
int strlen(char *str);
Exemplo
#include<stdio.h> #include<string.h>
void main(void) { char nome[]="Joao da Silva"; Linguagem e Tcnica de Programao I
Ao ser executado, este programa exibir no monitor o texto
Joao da Silva contem 13 caracteres
7.3.2 strcpy()
A funo strcpy() usada para copiar o contedo de um string (fonte) para outro string (destino). O cabealho da funo strcpy() o seguinte:
char *strcpy(char *destino,char *fonte);
A funo strcpy() retorna um ponteiro para o string de destino.
Exemplo
#include<stdio.h> #include<string.h>
void main(void) { char nome[]="Joao da Silva",aluno[50]; strcpy(aluno,nome); printf("O nome do aluno e %s",aluno); }
Ao ser executado, este programa exibir no monitor o texto
O nome do aluno e Joao da Silva
7.3.3 strcmp()
A funo strcmp() usada para comparar lexicograficamente dois strings. O cabealho da funo strcmp() o seguinte:
int strcmp(char *str1,char *str2);
Se os strings forem iguais, isto , se str1 e str2 forem do mesmo comprimento e possurem caracteres iguais nos elementos de mesmo ndice, strcmp() retornar zero. Se str1 for lexicograficamente maior do que str2, a funo strcmp() retornar um valor maior do que zero. Se str2 for lexicograficamente maior do que str1, a funo strcmp() retornar um valor menor do que zero. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 49 16/05/00
Exemplo
#include<stdio.h> #include<string.h>
void main(void) { char nome1[]="Joao da Silva",nome2[]="Maria Fernanda";
if(!strcmp(nome1,nome2)) printf("%s e igual a %s",nome1,nome2); else if(strcmp(nome1,nome2)>0) printf("%s vem depois de %s",nome1,nome2); else printf("%s vem depois de %s",nome2,nome1); }
Ao ser executado, este programa exibir no monitor o texto
Maria Fernanda vem depois de Joao da Silva
7.3.4 strcat()
A funo strcat() anexa o contedo de um string (fonte) no final de outro string (destino). Este processo chamado de concatenao de strings. importante lembrar que o tamanho fsico do string de destino ter que ser suficiente para armazenar os caracteres dos dois strings. O primeiro caracter do string de origem copiado para a posio do string de destino que contm o caracter NULL. O processo repetido at que o ltimo caracter do string de origem seja copiado para o string de destino. O string de destino ter apenas um caracter NULL no final do processo. O cabealho da funo strcat() o seguinte:
char *strcat(char *destino,char *origem);
A funo strcat() retorna um ponteiro para o string de destino.
Exemplo
#include<stdio.h> #include<string.h>
void main(void) { char nome[30]="Joao",sobreNome[]=" da Silva";
printf("O nome do aluno e %s",strcat(nome,sobreNome)); Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 50 16/05/00
}
Ao ser executado, este programa exibir no monitor o texto
O nome do aluno e Joao da Silva
7.3.5 strstr()
A funo strstr() procura um substring de um string. Se o string contiver o substring em questo, a funo retornar um ponteiro para o primeiro caracter do substring no string. Caso contrrio, a funo retornar NULL. O cabealho da funo strstr() o seguinte:
char *strstr(char *string,char *substring);
Exemplo
#include<stdio.h> #include<string.h>
void main(void) { char nome[30]="Joao da Silva",sobreNome[]="da Silva";
Ao ser executado, este programa exibir no monitor o texto
Achou
8 Arquivos
Antes de comearmos a discutir o sistema de arquivos do C ANSI importante entendermos a diferena entre os termos streams e arquivos. O sistema de E/S do C fornece ao programador uma interface consistente, independente do dispositivo real sendo acessado. Isto , o sistema de E/S do C fornece um nvel de abstrao entre o programador e o dispositivo utilizado. Essa abstrao chamada de stream e o dispositivo real chamado de arquivo. O sistema de arquivos do C ANSI foi projetado para trabalhar com uma grande variedade de dispositivos de E/S, incluindo terminais, acionadores de discos e acionadores de fita. Embora cada um dos dispositivos seja muito diferente, o sistema de arquivos com buffer transforma-os em um dispositivo lgico chamado stream. Todos os streams se comportam de maneira semelhante. Pelo fato dos streams serem Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 51 16/05/00
independentes dos dispositivos reais, a mesma funo pode escrever dados em um arquivo em disco ou em algum outro dispositivo, como o console. Em C um arquivo pode ser qualquer coisa, desde um arquivo em disco at um terminal ou uma impressora. Um stream associado com um arquivo especfico atravs de uma operao de abertura. Uma vez aberto o arquivo, informaes podem ser trocadas entre ele e o programa. Quando no for mais necessrio trocar informaes com o arquivo, geralmente no final do processamento, devemos desassociar o stream do arquivo atravs de uma operao de fechamento. Nem todos os arquivos apresentam os mesmos recursos. Por exemplo, um arquivo em disco pode suportar acesso aleatrio enquanto um teclado no pode. Isso revela um ponto importante sobre o sistema de E/S do C: todos os streams so iguais, mas no todos os arquivos.
8.1 Fundamentos do Sistema de Arquivos
O sistema de arquivos ANSI composto de diversas funes para a manipulao de arquivos, algumas das quais sero vistas no decorrer deste captulo. Essas funes exigem que o cabealho STDIO.H seja includo nos programas que as utilizem. O arquivo STDIO.H fornece prottipos para as funes de E/S e define tambm os tipos size_t e FILE. O tipo size_t essencialmente o mesmo que um unsigned, enquanto o tipo FILE ser discutido na prxima seo. Em STDIO.H definida tambm a macro EOF (geralmente definida como 1), sendo tal valor devolvido quando uma funo de entrada tenta ler alm do final de um arquivo.
8.1.1 O Ponteiro de Arquivo
Um ponteiro de arquivo uma varivel do tipo FILE que aponta para informaes que definem vrios atributos de um arquivo, tais como o seu nome, status e posio atual do cursor. Basicamente, o ponteiro de arquivo identifica um arquivo especfico em disco, sendo usado pela stream associada para direcionar as operaes das funes de E/S. Para ler (escrever) dados de (em) um arquivo, um programa precisa definir um ponteiro de arquivo da seguinte maneira:
FILE *arq;
8.1.2 Abrindo um Arquivo
Usamos a funo fopen() para abrirmos uma stream para uso e associarmos um arquivo a ela. Esta funo retorna um ponteiro para uma estrutura do tipo FILE associada ao arquivo em questo. A funo fopen() tem o seguinte prottipo:
FILE *fopen(char *nomeArq,char *modo);
Neste prottipo, nomeArq um ponteiro para um string que forma um nome vlido de arquivo, podendo incluir uma especificao de caminho (path). O string Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 52 16/05/00
apontado por modo determina como o arquivo ser aberto. A tabela abaixo mostra os strings vlidos para modo.
Modo Significado
r abre um arquivo texto para leitura w cria um arquivo texto para escrita a anexa dados no final de um arquivo texto rb abre um arquivo binrio para leitura wb cria um arquivo binrio escrita ab anexa dados no final de um arquivo binrio r+ abre um arquivo texto para leitura/escrita w+ cria um arquivo texto para leitura/escrita a+ anexa ou cria um arquivo texto para leitura/escrita rb+ abre um arquivo binrio para leitura/escrita wb+ cria um arquivo binrio para leitura/escrita ab+ anexa ou cria um arquivo binrio para leitura/escrita
Se ocorrer um erro quando o arquivo estiver sendo aberto, fopen() devolve um ponteiro nulo. Logo, antes de prosseguirmos com a leitura (escrita) de (em) um arquivo devemos testar esta condio. Para isso comparamos o ponteiro de arquivo com a macro NULL, que definida em STDIO.H como '\0'. O seguinte trecho de programa ser normalmente visto nos programas que manipulam arquivos:
FILE *arq;
if((arq=fopen("c:\\dir\\arquivo.x","w"))==NULL) { printf("O arquivo nao pode ser aberto\n"); exit(1); }
8.1.3 Fechando um Arquivo
A funo fclose() fecha um stream que foi aberto atravs de uma chamada fopen(). Ela escreve qualquer dado que ainda esteja no buffer e, ento, fecha normalmente o arquivo em termos de sistema operacional. Uma falha no sistema antes que um stream seja fechado pode causar perda de dados ou destruio do arquivo. A funo fclose() tem o seguinte prottipo:
int *fopen(FILE *arq);
Onde arq um ponteiro devolvido pela chamada fopen().
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 53 16/05/00
Exemplo
fclose(arq);
8.2 Arquivos (Streams) de Texto
Um arquivo de texto armazenado como uma seqncia de caracteres ASCII. Isto permite que os arquivos de texto possuam grande portabilidade entre plataformas de hardware/software, j que o texto ter que ser convertido para a configurao de memria da plataforma alvo quando da sua manipulao por um programa. Esta converso, porm, impe um custo adicional aos usurios dos arquivos de texto atravs de uma sobrecarga nos sistemas que manipulam tais arquivos. Isto ocorre, principalmente, na converso de seqncias de dgitos decimais para dados dos tipos int e float. Logo, devemos sempre comparar os ganhos obtidos com a portabilidade dos arquivos de texto com a perda de performance imposta aos sistemas que manipulam dados numricos armazenados como texto. Os arquivos de texto podem ser classificados como sendo compostos por texto formatado e texto no-formatado. Os arquivos de texto no-formatado podem ser vistos como um seqncia de caracteres que no possui nem tamanho nem tipo pr- estabelecido. Os textos no-formatados so manipulados geralmente por editores de texto, e so lidos (ou gravados) caracter a caracter. Os textos formatados so compostos por seqncias de caracteres que possuem formato (tipo e tamanho) e semntica pr-estabelecidos. Por exemplo, os dados de um aluno podem ser armazenados em um arquivo texto onde a matrcula seria representada por uma seqncia de dgitos decimais, o nome por uma seqncia de caracteres alfabticos de comprimento menor ou igual a 40, o endereo por uma seqncia de caracteres alfa-numricos de comprimento menor ou igual a 50, e o seu CR por uma seqncia de dgitos decimais (alm no ponto decimal).
8.2.1 Escrevendo um Caracter
A funo fputc() escreve um caracter em um arquivo que foi previamente aberto para escrita. A funo fputc() tem o seguinte prottipo:
int fputc(char ch,FILE *arq);
Onde arq um ponteiro devolvido pela chamada fopen() e ch o caracter a ser escrito. Se a operao for bem sucedida, a funo devolve o caracter escrito. Caso contrrio, ela devolve EOF.
8.2.2 Lendo um Caracter
A funo fgetc() l um caracter de um arquivo que foi previamente aberto para leitura. A funo fgetc() tem o seguinte prottipo:
Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 54 16/05/00
char fgetc(FILE *arq);
Onde arq um ponteiro devolvido pela chamada fopen(). Se a operao for bem sucedida, a funo devolve o caracter lido. Quando o final do arquivo for alcanado a funo devolve EOF.
Exemplo
ch=fgetc(arq); while(ch!=EOF) { ch=fgetc(arq); }
8.2.3 Escrevendo Strings
A funo fputs() escreve um string em um arquivo que foi previamente aberto para escrita. A funo fputs() tem o seguinte prottipo:
int fputs(char *str,FILE *arq);
Onde arq um ponteiro devolvido pela chamada fopen() e str o string a ser escrito. Se a operao for bem sucedida, a funo devolve um inteiro positivo. Caso contrrio, ela devolve EOF.
8.2.4 Lendo Strings
A funo fgets() l um string de um arquivo que foi previamente aberto para leitura at encontrar um caracter de nova linha ('\n'), ou at que tam-1 caracteres sejam lidos. De maneira diferente de gets(), se um caracter de nova linha for lido ele far parte do string resultante. O caracter nulo ser inserido no final do string resultante. A funo fgets() tem o seguinte prottipo:
char *fgets(char *str, int tam, FILE *arq);
Onde arq um ponteiro devolvido pela chamada fopen() e str o string resultante. Se a operao for bem sucedida, a funo devolve um ponteiro para str. Se ocorrer algum tipo de erro um ponteiro nulo ser devolvido.
8.2.5 Usando fprintf() e fscanf()
As funes fprintf() e fscanf() so extenses das funes printf() e scanf() para operar com arquivos. Os seus prottipos so os seguintes:
int fprintf(FILE *arq,char *controle,<lista de parmetros>); int fscanf(FILE *arq,char *controle,<lista de parmetros>); Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 55 16/05/00
Onde arq um ponteiro devolvido pela chamada fopen(). Se a operao fprint() for bem sucedida, a funo devolve o nmero de caracteres escritos. Caso contrrio, um nmero negativo devolvido. Se a operao fscanf() for bem sucedida, a funo devolve o nmero de argumentos que receberam valores. Quando o final do arquivo for alcanado a funo devolve EOF.
arq=fopen("c:\\ltp1\\arquivos\\nomes.txt","w"); if(arq==NULL) { printf("Erro na abertura do arquivo"); exit(); }
clrscr(); printf("Forneca o nome do aluno\n"); gets(nome); tam=strlen(nome); while(tam) { printf("Forneca a nota do aluno\n"); scanf("%f",¬a); fprintf(arq,"%d %s %5.2f ",tam,nome,nota); getchar(); printf("Forneca o nome do aluno\n"); gets(nome); tam=strlen(nome); }
fclose(arq);
}
8.2.7 Exemplo de Programa de Leitura
#include<stdio.h> Linguagem e Tcnica de Programao I
arq=fopen("c:\\ltp1\\arquivos\\nomes.txt","r"); if(arq==NULL) { printf("Erro na abertura do arquivo"); exit(); }
clrscr(); while(fscanf(arq,"%d",&tam)!=EOF) { qtd++; fgetc(arq); fgets(nome,tam+1,arq); fscanf(arq,"%f",¬a); printf("%-40s %5.2f\n",nome,nota); soma+=nota; if(nota>notaMaior) { notaMaior=nota; strcpy(nomeMaior,nome); } } printf("A media da turma e %5.2f\n",soma/qtd); printf("A maior nota e %5.2f - %s\n",notaMaior,nomeMaior); fclose(arq); }
8.3 Arquivos (Streams) Binrios
Os dados armazenados em um arquivo binrio tm o mesmo formato das suas respectivas representaes na memria principal (RAM). Isto leva a um enorme aumento no desempenho das operaes de E/S quando comparado ao desempenho das operaes de E/S com arquivos texto, dado que nesse caso no necessrio converter a representao interna de um dado numrico (int ou float) para uma representao textual (e vice-versa). Dessa forma, os dados podem se lidos (escritos) diretamente para (da) a memria principal (RAM) sem que haja necessidade de converso. Alm disso, muito mais fcil tratar dados numricos armazenados na forma binria, j que os tipos numricos tm tamanhos fixos. Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 57 16/05/00
A grande vantagem dos arquivos binrios em comparao aos arquivos texto, tambm o seu principal ponto fraco quando se trata de portabilidade. Ou seja, como os arquivos binrios representam "imagens da memria principal", eles so dependentes do hardware para (de) onde sero lidos (escritos) e da forma como as diversas linguagens implementam os tipos numricos. Por exemplo, um dado do tipo ponto flutuante escrito por um programa compilado com o Turbo Pascal (real), que tem seis bytes de comprimento, no poder ser posteriormente lido para uma varivel de um programa compilado com Turbo C, j que no existe neste compilador nenhum tipo de dado que represente um ponto flutuante com seis bytes.
8.3.1 Funes de E/S para Arquivos Binrios
As funes fread() e fwrite() permitem, respectivamente, a leitura e a escrita de uma seqncia de bytes de qualquer tipo de dado. Os seus prottipos so os seguintes:
Na funo fread(), mem um ponteiro para uma regio de memria que armazenar os dados lidos do arquivo. Na funo fwrite(), mem um ponteiro para uma regio de memria que armazena os dados que sero escritos no arquivo. O nmero de bytes que sero lidos, ou escritos, dado por numBytes. O argumento cont determina quantos itens de dados (cada um dos quais de comprimento numBytes) sero lidos, ou escritos (o tipo size_t est definido em STDIO.H e geralmente o mesmo que unsigned int). Finalmente, arq um ponteiro devolvido pela chamada fopen(). A funo fread() devolve o nmero de itens lidos. Esse valor pode ser menor do que cont se o final do arquivo for atingido, ou se ocorrer um erro. A funo fwrite() devolve o nmero de itens escritos. Esse valor ser igual a cont, a no ser que ocorra um erro. Quando um arquivo for aberto no modo binrio, fread() e fwrite() podem ler e escrever qualquer tipo de informao. Desse modo, devemos usar o operador de tempo de compilao sizeof para determinarmos o comprimento de cada tipo de dado.
arq=fopen("c:\\ltp1\\arquivos\\nomes.dat","wb"); if(arq==NULL) { printf("Erro na abertura do arquivo"); exit(); }
clrscr(); printf("Forneca o nome do aluno\n"); gets(nome); tam=strlen(nome); while(tam) { printf("Forneca a nota do aluno\n"); scanf("%f",¬a); fwrite(&tam,sizeof(int),1,arq); fwrite(nome,sizeof(char),tam,arq); fwrite(¬a,sizeof(float),1,arq); getchar(); printf("Forneca o nome do aluno\n"); gets(nome); tam=strlen(nome); }
arq=fopen("c:\\ltp1\\arquivos\\nomes.dat","rb"); if(arq==NULL) { printf("Erro na abertura do arquivo"); exit(); }
clrscr(); while(fread(&tam,sizeof(int),1,arq)) { qtd++; fread(nome,sizeof(char),tam,arq); nome[tam]=NULL; fread(¬a,sizeof(float),1,arq); printf("%-40s %5.2f\n",nome,nota); soma+=nota; if(nota>notaMaior) { notaMaior=nota; strcpy(nomeMaior,nome); } } printf("A media da turma e %5.2f\n",soma/qtd); printf("A maior nota e %5.2f - %s\n",notaMaior,nomeMaior); fclose(arq); }
8.4 Usando a Funo feof()
Quando um arquivo aberto para entrada no modo binrio, um valor inteiro igual marca EOF pode ser lido. Isso poderia fazer com que a rotina de entrada indicasse o fim do arquivo apesar do final fsico do mesmo no ter sido alcanado. Para resolver esse problema foi introduzida uma funo, feof(), que determina quando o final do arquivo foi atingido em um processo de leitura. A funo feof() tem o seguinte prottipo (incluso em STDIO.H): Linguagem e Tcnica de Programao I
Ivan Mathias Filho Pgina 60 16/05/00
int feof(FILE *arq);
Essa funo devolve verdadeiro se o final do arquivo foi atingido; caso contrrio, ela devolve falso (0). O exemplo abaixo mostra a leitura de um arquivo texto at que o seu final seja atingido.