You are on page 1of 98

I

II Tpicos em C reviso 257 de 24/03/2004 23:16

ndice
INTRODUO ...........................................................................................................................................1 AS ORIGENS DE C ....................................................................................................................................1 C MDIO NVEL .......................................................................................................................................2 A FORMA GERAL DE UM PROGRAMA EM C ...................................................................................................2 TIPOS DE DADOS ......................................................................................................................................3 Tipos Bsicos.....................................................................................................................................3 Modificadores dos Tipos Bsicos ......................................................................................................3 CONSTANTES ...........................................................................................................................................4 Constantes Inteiras ............................................................................................................................4 Constantes Inteiras Longas ...............................................................................................................4 Constantes Octais..............................................................................................................................4 Constantes Hexadecimais .................................................................................................................4 Constantes em Ponto Flutuante ........................................................................................................5 Constantes Caracteres ......................................................................................................................5 Constantes String...............................................................................................................................5 VARIVEIS ................................................................................................................................................5 Nomenclatura de Variveis e Outros Identificadores .......................................................................5 Declarao de variveis:....................................................................................................................5 Atribuio de valores s variveis .....................................................................................................5 ENTRADA E SADA PELO CONSOLE.....................................................................................................7 INTRODUO ............................................................................................................................................7 BIBLIOTECA PADRO.................................................................................................................................7 SADA - A FUNO PRINTF ........................................................................................................................7 Cdigos de Converso ......................................................................................................................7
Justificao da sada ....................................................................................................................................7 Tamanho do Campo .....................................................................................................................................8 Especificador de Preciso ............................................................................................................................8 Modificadores de Comprimento ....................................................................................................................8

ENTRADA - A FUNO SCANF ....................................................................................................................8 LENDO E IMPRIMINDO CARACTERES ...........................................................................................................9 Funes getchar e putchar ................................................................................................................9 Funes getch e getche.....................................................................................................................9 LENDO E IMPRIMINDO STRINGS ..................................................................................................................9 Lendo e Imprimindo strings com scanf e printf ...........................................................................9 Lendo e Imprimindo strings com gets e puts ...............................................................................10 OPERADORES E EXPRESSES ...........................................................................................................11 OPERADORES .........................................................................................................................................11 Operador de Atribuio....................................................................................................................11 Operadores Aritmticos ...................................................................................................................11 Operadores Relacionais e Lgicos ..................................................................................................11
Operadores Relacionais .............................................................................................................................11 Operadores Lgicos ...................................................................................................................................11 && (AND lgico)..........................................................................................................................................11 || (OR lgico)...............................................................................................................................................12 ! (NOT lgico) .............................................................................................................................................12

Operadores Lgicos com Bits..........................................................................................................12 Operadores de Atribuio Composta...............................................................................................13 Operador sizeof() .............................................................................................................................13 EXPRESSES .........................................................................................................................................14 Converso de Tipos.........................................................................................................................14
Prof. Fabio Alexandre Spanhol, M.Sc. II

III Tpicos em C reviso 257 de 24/03/2004 23:16

Limites do intervalo do tipo de dado. ...............................................................................................14 Regras de Precedncia ...................................................................................................................14 COMANDOS DE CONTROLE .................................................................................................................15 BLOCOS DE COMANDOS ..........................................................................................................................15 COMANDOS DE TESTE .............................................................................................................................15 Comando if.......................................................................................................................................15 Instruo switch ...............................................................................................................................17 Comando Ternrio ...........................................................................................................................18 LAOS ...................................................................................................................................................19 Comando for ....................................................................................................................................19 Comando while ................................................................................................................................21 Comando do-while ...........................................................................................................................22 COMANDOS DE DESVIO ...........................................................................................................................22 Comando break................................................................................................................................22 Comando continue ...........................................................................................................................23 Comando goto..................................................................................................................................23 Funo exit() ....................................................................................................................................23 Comando return ...............................................................................................................................23 VETORES E CADEIAS DE CARACTERES............................................................................................24 INTRODUO ..........................................................................................................................................24 DECLARAO DE VETORES UNIDIMENSIONAIS ..........................................................................................24 CADEIAS DE CARACTERES ......................................................................................................................25 DECLARAO DE VETORES MULTIDIMENSIONAIS ......................................................................................26 VETORES DE CADEIAS DE CARACTERES ..................................................................................................26 INICIALIZAO DE VETORES.....................................................................................................................27 LIMITES DE VETORES E SUA REPRESENTAO EM MEMRIA .....................................................................28 FUNES ................................................................................................................................................29 INTRODUO ..........................................................................................................................................29 PROTTIPOS DE FUNES ......................................................................................................................29 ESCOPO DE VARIVEIS ...........................................................................................................................29 Variveis Locais ...............................................................................................................................29 Variveis Globais .............................................................................................................................30 Parmetros Formais.........................................................................................................................30
Passagem de Parmetros por Valor...........................................................................................................30 Passagem de Parmetros por Referncia..................................................................................................30

PASSAGEM DE VETORES E MATRIZES ......................................................................................................30 O COMANDO RETURN..............................................................................................................................31 RECURSO .............................................................................................................................................31 ARGUMENTOS - ARGC E ARGV .................................................................................................................32 PONTEIROS.............................................................................................................................................34 INTRODUO ..........................................................................................................................................34 OPERAES COM PONTEIROS .................................................................................................................34 Declarao de Ponteiros..................................................................................................................34 Os Operadores de Ponteiros ...........................................................................................................34 Aritmtica de Ponteiros....................................................................................................................34
Atribuio de Ponteiros...............................................................................................................................34 Acessando os Endereos ...........................................................................................................................35 Incrementando e Decrementando Ponteiros ..............................................................................................35 Comparao de Ponteiros ..........................................................................................................................35

Ponteiros e Vetores .........................................................................................................................35 Ponteiros e Strings...........................................................................................................................36


Prof. Fabio Alexandre Spanhol, M.Sc. III

IV Tpicos em C reviso 257 de 24/03/2004 23:16

Alocao Dinmica de Memria ......................................................................................................37 Ponteiros e Matrizes ........................................................................................................................37 Vetores de Ponteiros .......................................................................................................................38 Ponteiros para Ponteiros .................................................................................................................39 ESTRUTURAS .........................................................................................................................................42 INTRODUO ..........................................................................................................................................42 DEFINIES BSICAS..............................................................................................................................42 ATRIBUIO DE ESTRUTURAS ..................................................................................................................42 MATRIZES DE ESTRUTURAS .....................................................................................................................43 PASSANDO MEMBROS E ESTRUTURAS PARA FUNES .............................................................................43 PONTEIROS PARA ESTRUTURAS ..............................................................................................................46 ENTRADA E SADA DE DADOS POR ARQUIVOS ...............................................................................49 INTRODUO ..........................................................................................................................................49 FLUXOS DE DADOS .................................................................................................................................49 Fluxos de Texto................................................................................................................................49 Fluxo Binrio ....................................................................................................................................49 Arquivos ...........................................................................................................................................49 FUNES DE ENTRADA E SADA ..............................................................................................................49 INCIO E FIM ............................................................................................................................................50 Abrindo um Arquivo .........................................................................................................................50 Fechando um Arquivo......................................................................................................................50 Fim de Arquivo .................................................................................................................................51 Volta ao Incio ..................................................................................................................................51 LENDO E ESCREVENDO CARACTERES ......................................................................................................51 LENDO E ESCREVENDO CADEIAS DE CARACTERES ...................................................................................52 LENDO E ESCREVENDO ARQUIVOS BINRIOS ...........................................................................................53 ENTRADA E SADA FORMATADA ...............................................................................................................54 LISTAS .....................................................................................................................................................56 INTRODUO ..........................................................................................................................................56 LISTAS - DEFINIES BSICAS ................................................................................................................56 LISTAS COM ALOCAO SEQENCIAL ......................................................................................................56 PILHAS COM ALOCAO SEQENCIAL ......................................................................................................60 LISTAS COM ALOCAO ENCADEADA .......................................................................................................65 LISTAS SIMPLESMENTE ENCADEADAS ......................................................................................................65 LISTAS CIRCULARES ...............................................................................................................................66 RVORES ................................................................................................................................................69 DEFINIES BSICAS..............................................................................................................................69 RVORES BINRIAS ................................................................................................................................71 ARMAZENAMENTO DE RVORES BINRIAS ...............................................................................................72 UMA APLICAO DE RVORES BINRIAS ..................................................................................................72 PERCORRENDO RVORES BINRIAS ........................................................................................................74 ALGORITMO DE HUFFMAN .......................................................................................................................75 REMOVENDO NS DE RVORES BINRIAS ...............................................................................................76 RVORES RVORES BINRIAS BALANCEADAS ..........................................................................................79 GRFICOS EM BORLAND C..................................................................................................................82 INTRODUO ..........................................................................................................................................82 CONCEITOS PRINCIPAIS ..........................................................................................................................82 A Memria de Vdeo - Resoluo e Profundidade de Cores - o Pixel.............................................82 "Primitivas Grficas" - a Linha, o Retngulo, o Crculo ...................................................................82
Prof. Fabio Alexandre Spanhol, M.Sc. IV

V Tpicos em C reviso 257 de 24/03/2004 23:16

BITMAPs ..........................................................................................................................................82 A BIBLIOTECA GRFICA DA BORLAND ......................................................................................................83 DESCRIO DAS PRINCIPAIS FUNES DA BIBLIOTECA GRAPHICS.H.....................83
void far arc(int x, int y, int stangle, int endangle, int radius) ........................................................................83 void far bar(int left, int top, int right, int bottom);..........................................................................................84 void far bar3d(int left, int top, int right, int bottom, int depth, int topflag);.....................................................84 void far circle(int x, int y, int radius);............................................................................................................84 void far cleardevice(void); ...........................................................................................................................84 void far clearviewport(void); ........................................................................................................................84 void far closegraph(void); ...........................................................................................................................84 void far detectgraph(int far *graphdriver, int far *graphmode); ....................................................................84 void far floodfill(int x, int y, int border); ........................................................................................................85 int far getbkcolor(void); ...............................................................................................................................85 int far getcolor(void); ...................................................................................................................................85 char *far getdrivername(void); ....................................................................................................................85 int far getgraphmode(void);.........................................................................................................................85 char * far getmodename(int mode_number); ..............................................................................................85 int far getmaxx(void); ..................................................................................................................................85 int far getmaxy(void); ..................................................................................................................................85 int far getx(void) ..........................................................................................................................................85 int far gety(void) ..........................................................................................................................................85 unsigned far getpixel(int x, int y); ................................................................................................................85 char *far grapherrormsg(int errorcode) .......................................................................................................85 int far graphresult(void)...............................................................................................................................85 void far initgraph(int far *graphdriver, int far *graphmode, char far *pathtodriver).......................................85 void line(int x1, int y1, int x2, int y2) ............................................................................................................86 void far outtext(char *far textstring).............................................................................................................86 void far outtextxy(int x, int y, char *far textstring) ........................................................................................86 void far putpixel(int x, int y, int color)...........................................................................................................86 void far rectangle(int left, int top, int right, int bottom) .................................................................................86 void setbkcolor(int color) .............................................................................................................................86 void setcolor(int color).................................................................................................................................86 void far setgraphmode(int mode) ................................................................................................................86 void far setlinestyle(int linestyle, unsigned upattern, int thickness) .............................................................86 void far settextstyle(int font, int direction, int charsize)................................................................................86 void far setviewport(int left, int top, int right, int bottom, int clip)..................................................................86

OBSERVAES FINAIS ............................................................................................................................86 APNDICE : TABELA ASCII...................................................................................................................87 REFERNCIAS BIBLIOGRFICAS........................................................................................................93

Prof. Fabio Alexandre Spanhol, M.Sc. V

1 Tpicos em C reviso 257 de 24/03/2004 23:16

Introduo
Conhecer a Linguagem C ajuda a entender os motivos que levaram ao aumento da importncia da orientao a objetos e ao surgimento das linguagens C++ e Java. Alm disso, um conhecimento prvio de C, embora no seja indispensvel, sempre representa uma vantagem para que est interessado em programar em C++ e Java. O padro ANSI para Linguagem C existe desde 1988. Isso significa que o cdigo escrito dentro deste padro tem validade em qualquer parte do mundo. Programadores C de todo o mundo podem trocar figurinhas, e um programador C um profissional valorizado em todo o mundo. Em comparao, apenas recentemente foi definido um padro ANSI/ISO para a Linguagem C++. Como dito acima, um conhecimento de Linguagem C uma importante base para o aprendizado de C++ e Java. Isto porque a Linguagem C++ muito prxima de C; e a Linguagem Java herdou muito de C++. Alm disso, uma viso da evoluo histrica ajuda o programador a ter uma viso mais ampla de seu trabalho. O cdigo C existente em todo o mundo representa um patrimnio de muitos milhes de dlares. Ele no pode ser simplesmente abandonado ou convertido em outras linguagens. E de qualquer modo, essa converso exigir o trabalho de profissionais com excelente base em C. Por isso, programadores C tero trabalho ainda por muitos anos. A API Windows foi especificada originalmente em Linguagem C, simplesmente porque quando esse ambiente foi desenvolvido inicialmente, nos anos 80, a Linguagem C++ e a orientao a objetos ainda no tinham grande aceitao. Por isso, o programador que quiser realmente entender a fundo a programao Windows e extrair at a ltima gota de performance desse ambiente (por exemplo, no desenvolvimento de jogos) precisar ter bom domnio de Linguagem C. O sistema operacional Linux foi desenvolvido originalmente em C. Alm disso, a Linguagem C est estreitamente relacionada com tudo que diz respeito ao mundo Unix. Concluso: o conhecimento de C indispensvel para bons programadores Linux e Unix em geral. A Linguagem C continua sendo a preferida para o desenvolvimento de sistemas embutidos (embedded systems). Esses sistemas referem-se ao software que vai dentro de automveis, fornos de microondas, aparelhos de videocassete e outros dispositivos no-computacionais. Prev-se para os prximos anos uma exploso no uso de sistemas embutidos nos mais diversos tipos de aparelhos. O resultado ser um provvel aumento no mercado de trabalho para programadores C (embora C++ e Java tambm estejam nesta corrida). A Linguagem C permite a gerao de executveis muito pequenos. Isso muito importante quando se trata, por exemplo, de software distribudo por download na Internet. As Origens de C A linguagem de programao C foi projetada para permitir grande economia de expresso nos programas, isto , produzir programas fonte mais compactos. Foi primeiramente usada para escrever cerca de 90% do cdigo do sistema operacional UNIX, e com a popularizao deste sistema operacional em equipamentos de mdio porte, e at micros, a linguagem C tambm ganhou popularidade entre os programadores profissionais. Em 1969, os laboratrios Bell procuravam uma alternativa para o sistema operacional Multics para o computador PDP-7. Uma verso bsica do sistema operacional UNIX foi ento escrita em Assembly. Neste mesmo perodo, uma linguagem experimental estava sendo desenvolvida por Keneth Thompson. Esta linguagem se chamava B e foi influenciada por BCPL Essa linguagem foi projetada por Martin Richards no meio dos anos 60 enquanto ele visitava o MIT, e foi usada no comeo dos anos 70 para vrios projetos interessantes, dentre eles o sistema operacional OS6, em Oxford e partes do trabalho no Xerox PARC. A linguagem B subsidiou o projeto da linguagem C, em 1972. C foi criada e implementada primeiramente por Dennis M. Ritchie numa mquina DEC PDP-11. Ritchie trabalhava nos Laboratrios Bell AT&T (empresa da rea de Telecomunicaes), um dos maiores centros da Cincia (onde trabalham diversos detentores do Prmio Nobel). A AT&T foi uma das financiadoras do Multics. Logo depois, em 1973, o sistema operacional UNIX foi melhorado e cerca de 90% de seu cdigo foi escrito em C. Por causa desta libertao do Assembly, UNIX ( e conseqentemente C) adquiriu grande portabilidade, foi rapidamente adaptado a uma srie de computadores e seu uso no parou de crescer. No final da dcada de 70 e inicio da dcada de 80, a proliferao de UNIX e C foi muito grande e chegou at os micros. Alm do que, C ficou independente do sistema operacional UNIX e uma srie de compiladores C surgiram para muitos equipamentos.

Dennis M. Ritchie (em p) e Ken Thompson, no Bell Labs, incio dos anos 70 A AT&T colocou o compilador C (chamado "K&R") no pacote do UNIX e mandou para as universidades. "K&R" vem de Kernigham e Ritchie, autores do primeiro livro da Linguagem C, estilo atualmente conhecido como "C clssico". Quem recebeu o UNIX, recebeu o C gratuitamente e a maioria gostou (at para entender o UNIX melhor). O resultado foi que a linguagem C tornou-se um padro instantneo. C por muito tempo teve seu padro atrelado a verso 5 do UNIX, estando tal padro descrito em The C Programming Language, de Brian Kernighan e Dennis Ritchie, Englewood Cliffs, N.J.: Prentice Hall, 1978, o livro branco ou K&R. Finalmente, em 1988, a linguagem foi oficialmente padronizada pelo comit ANSI X3J11, o qual fez novas mudanas. O comit X3J11 produziu seu relatrio [ANSI 89] no final de 1989, e subseqentemente este padro foi aceito pela ISO como ISO/IEC 9899-1990.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 1 de 98

2 Tpicos em C reviso 257 de 24/03/2004 23:16 At o comeo dos anos 80, apesar de existirem compiladores para uma variedade de arquiteturas e mquinas e sistemas operacionais, a linguagem foi quase exclusivamente associada com o Unix; mais recentemente, ela tem difundido-se mais extensamente, e nos anos 90 posicionou-se dentre a linguagens mais comumente usada por toda a indstria de computao. No ambiente PC, em 1990, a Borland International Co, fabricante de compiladores profissionais escolhe o C e o Pascal como linguagens de trabalho para o seu Integrated Development Enviroment (Ambiente Integrado de Desenvolvimento): surge o Turbo C. Os mais recentes descendentes da C formal incluem Concurrent C, Objective C, C* e especialmente C++, de Stroustrup A linguagem tambm usada globalmente como uma representao intermediria (essencialmente, como uma linguagem assembler porttil) para uma larga variedade de compiladores, tanto para descendentes diretos como C++ ou linguagens independentes como Modula 3 e Eiffel.

Keneth Thompson C mdio nvel

Dennis M. Ritchie

Brian Kernighan

C foi criada para resolver um problema especfico: portar (transportar) um sistema operacional, o UNIX. Desta forma, a linguagem C foi desenvolvida por um programador para ele mesmo! C uma linguagem criada por programadores para programadores. Assim, C no geralmente a primeira linguagem a se estudar em Computao. A Linguagem C uma linguagem procedural tpica. Mais precisamente, trata-se de uma linguagem estruturada em blocos. Em C, inexistem procedimentos, somente funes. Para implementar um procedimento, usamos uma funo, dizendo que o valor retornado no nos interessa. C a linguagem cientfica mais utilizada, sucessora do FORTRAN. Obviamente, ainda existem pessoas usando FORTRAN, reforando o fato de que as mudanas so lentas... C++ uma expanso da Linguagem C, no sentido de capacitar o uso do estilo de "programao orientado a objetos". Muitos advogam o uso de C para fazer simuladores simples e o uso de C++ em projetos maiores, onde mais de um programador usado para implementar os algoritmos. C referenciada como linguagem mdio nvel, pois combina elementos de linguagens de alto nvel com a funcionalidade e o poder da linguagem assembly. C apesar de possuir tipos de dados internos, no uma linguagem rica em tipos como Pascal ou ADA. Alm disso, C no fortemente tipada, isto , permite quase todas as converses de tipo. Assim, por exemplo, inteiros e caracteres podem ser misturados livremente em expresses. Limites de matrizes tambm no verificadas em tempo de execuo. Essas verificaes so trabalho do programador. C manipula diretamente bits, bytes, words e ponteiros. C possui apenas 32 palavras-chave (o padro original de Kernighan e Ritchie possua 27, 5 foram adicionadas pelo comit ANSI). Essas palavras-chave so os comandos da linguagem. Para efeito de comparao: a linguagem BASIC (Beginners All-purpose Symbolic Instruction Code) possui 159 palavras-chave! break case char auto const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while A forma geral de um programa em C Um programa em C constitudo de: um cabealho contendo as diretivas de compilador onde se definem o valor de constantes simblicas, declarao de variveis, incluso de bibliotecas, declarao de rotinas, etc. um bloco de instrues principal e outros blocos de rotinas. documentao do programa: comentrios. O programa a seguir ilustra a estrutura bsica de um programa em C. /* /* Programa: primeiro.c Objetivo: Apresentar Formato Geral de um Programa em C */ /* Definicoes para o Pre-Processador (Ex: Inclusao de Bibliotecas) */ /* Esta eh uma biblioteca para funcoes de I/O padro */ #include <stdio.h> /* Declaracao de Variaveis Globais */ /* Neste programa nao ha variaveis */ /* Declaracao de Funcoes do Modulo */

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 2 de 98

3 Tpicos em C reviso 257 de 24/03/2004 23:16 /* * Se o modulo for o principal, entao a funcao "main" deve estar declarada. */ void main ( ) { /* Corpo da funo */ printf("Meu primeiro programa\n"); } /* Declaracao das demais funcoes do programa */ Um programa em C composto de funes. No exemplo temos apenas a funo main. Esta funo deve constar obrigatoriamente em todos os programas C porque ela sempre a primeira a ser executada. Dentro dos parnteses, logo aps o nome, so colocados os parmetros que a funo necessita para sua execuo. Por exemplo uma funo que calcule o seno de um ngulo dever receber como parmetro o valor deste ngulo. Neste caso nenhum parmetro foi entregue a funo. O que a funo vai executar, ou o chamado corpo da funo, aparece entre { e }. As intrues so executadas na ordem em que aparecem no texto, e so terminadas por um ;. Neste programa temos apenas uma chamada uma funo j prdefinida no sistema que imprime uma mensagem. Os delimitadores /* e */ identificam o incio e o fim de um comentrio. Comentrios so ignorados pelo compilador C, e portanto no so executados, servindo para, por exemplo, explicar o funcionamento do programa, documentando assim o cdigo-fonte. Tipos de Dados Tipos Bsicos Os dados podem assumir cinco tipos bsicos em C que so:

char: Caracter: O valor armazenado um caracter. Caracteres geralmente so armazenados em cdigos (usualmente o ASCII). Ocupa 1 byte, representando a faixa compreendida entre -128 a 127. int:
Nmero inteiro o tipo padro e o tamanho do conjunto que pode ser representado normalmente depende da mquina em que o programa est rodando. Em geral, em ambientes 16 bits, composto por 2 bytes, representando a faixa compreendida entre -32768 a 32767. J em ambientes 32 bits, composto por 4 bytes, representando a faixa compreendida entre 2.147.483.648 a 2.147.483.647.

float: Nmero em ponto flutuante de preciso simples. Dependente da mquina. Um ponto decimal em um nmero geralmente indica um ponto flutuante. Ocupa 4 bytes, representando a faixa compreendida entre 3.4e-38 a 3.4e38. double: Nmero em ponto flutuante de preciso dupla. Ocupa 8 bytes, representando a faixa compreendida entre 1.7e-308 a 1.7e308. void:
sem valor

Modificadores dos Tipos Bsicos Modificadores podem ser aplicados a estes tipos. Estes modificadores so palavras que alteram o tamanho do conjunto de valores que o tipo pode representar. Por exemplo, um modificador permite que possam ser armazenados nmeros inteiros maiores. Um outro modificador obriga que s nmeros sem sinal possam ser armazenados pela varivel. Deste modo no necessrio guardar o bit de sinal do nmero e somente nmeros positivos so armazenados. O resultado prtico que o conjunto praticamente dobra de tamanho. Os modificadores de tipos bsicos so: Modificador Descrio sumria tipo com sinal (default) tipo sem sinal tipo curto tipo longo Tipos afetados char, int char, int char, int, float, double char, int, float, double

signed unsigned short long

Para verificar como esses modificadores alteram os tamanhos em bytes dos tipos bsicos, execute o exemplo: /************************************************************************** * Linguagem C - 2004/1 ************************************************************************** * Programa: tipos.c * Autor: Fabio Alexandre Spanhol * * Objetivo: Apresentar os Tipos de Dados Primitivos e seus Modificadores **************************************************************************/ #include <stdio.h> int main (){ printf( "\n\nPrograma Tipos\n\n" ); printf( printf( printf( printf( printf( printf( "Tipos ANSI C: "sizeof(int) ................ "sizeof(unsigned int)........ "sizeof(short int) .......... "sizeof(unsigned short int) . "sizeof(long int) ........... Bytes\n" ); %2d\n", sizeof(int) ); %2d\n", sizeof(unsigned int) ); %2d\n", sizeof(int) ); %2d\n", sizeof(unsigned short int) ); %2d\n", sizeof(long int) );

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 3 de 98

4 Tpicos em C reviso 257 de 24/03/2004 23:16 printf( "sizeof(unsigned long int) .. %2d\n", sizeof(unsigned long int) ); printf( "sizeof(char) ............... %2d\n", sizeof(char) ); printf( "sizeof(unsigned char) ...... %2d\n", sizeof(unsigned char) ); printf( "sizeof(float) .............. %2d\n", sizeof(float) ); printf( "sizeof(double) ............. %2d\n", sizeof(double) ); printf( "sizeof(long double) ........ %2d\n\n", sizeof(long double) ); printf( printf( printf( printf( printf( "Outras Combinacoes: "sizeof(long char) .......... "sizeof(unsigned long char) . "sizeof(short char) ......... "sizeof(unsigned short char) Bytes\n" ); %2d\n", sizeof(long char) ); %2d\n", sizeof(unsigned long char) ); %2d\n", sizeof(short char) ); %2d\n", sizeof(unsigned short char) );

printf( "sizeof(long float) ......... %2d\n", sizeof(long float) ); printf( "sizeof(short float) ........ %2d\n", sizeof(short float) ); printf( "sizeof(short double) ....... %2d\n", sizeof(short double) ); }

Constantes Constantes Inteiras So valores numricos sem ponto decimal, precedidos ou no por um sinal. Por exemplo: 1997 -3 +5 0 -32000 Constantes Inteiras Longas So constantes armazenadas em um nmero maior de bits. Para diferenci-las de constantes inteiras comuns acrescenta-se um L ao final do nmero. Constantes inteiras escritas sem o L final mas que no podem ser armazenadas em um tipo int so armazenadas em tipo long. Por exemplo: 234L 320000L -120000L Constantes Octais So constantes representadas na base 8. Normalmente so representadas sempre sem sinal e com um 0 antecedendo o nmero. Na tabela abaixo mostramos exemplos de constantes octais e o seu valor na base 10. Base 8 Base 10 007 7 025 21 077 63 011 9 0175 125 Constantes Hexadecimais So constantes representadas na base 16. Normalmente so representadas com um 0x ou 0X antecedendo o nmero. Na tabela abaixo mostramos exemplos de constantes hexadecimais e o seu valor na base 10. Base 16 0xF 0X25 0XAB 0xBEEF Base 10 15 37 171 48879

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 4 de 98

5 Tpicos em C reviso 257 de 24/03/2004 23:16 Constantes em Ponto Flutuante So constantes com ponto decimal, precedidas ou no do sinal negativo. Observar que a notao cientfica tambm aceita. So exemplos de constantes em ponto flutuante. +23.45e-10 123.45 123.45e+10 123.45F Constantes Caracteres Uma constante caracter um nico caracter escrito entre ', como em 'a', podendo participar normalmente de expresses aritmticas. O valor que entra na expresso o do cdigo usado para representar o caracter. Exemplos de constantes do tipo caracter so: 'a' 'A' '\0141' '\n' caracter a caracter A Constante octal correspondente ao caracter 'a' return

Certos caracteres que no so visveis podem ser representados anteponde-se o caracter '\' , como no exemplo return da tabela acima. Outros exemplos so: \t (tabulao), \0 (null). Constantes String Uma constante string uma sequncia de caracteres entre " (aspas duplas), como no exemplo abaixo.

"alo mundo!!!"
Variveis Nomenclatura de Variveis e Outros Identificadores Existem algumas regras bsicas que limitam a criatividade do programador no batismo destas variveis. Estas regras bsicas so: Todo nome s pode conter letras e dgitos; Todo primeiro caracter deve ser sempre uma letra; Letras maisculas e minsculas so consideradas caracteres diferentes. A linguagem C case sensitive; O caracter "_" (underscore) pode ser contado como uma letra; Palavras reservadas no podem ser usadas como nome de variveis. boa poltica escolher nomes que significam alguma coisa e indiquem a funo da varivel. Por exemplo:

soma, total, nome, raio.


Declarao de variveis:

valor,

Para serem usadas, as variveis precisam ser declaradas de modo que o compilador possa reservar espao na memria para o valor a ser armazenado. A forma geral de uma declarao : tipo lista_de_variaveis ; Exemplos: int i; unsigned int a, b, c; unsigned short int dia, mes, ano; double salario; Atribuio de valores s variveis Aps ser declarada, a varivel pode receber valores. O operador de atribuio "=" indica que o valor direita ser atribudo varivel. O valor inicial pode ser atribudo de duas formas: Durante execuo da funo: int funcao(){ int i, j; float raio; char c;

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 5 de 98

6 Tpicos em C reviso 257 de 24/03/2004 23:16 i = 0; j = 10; raio = 2.54; c = 'd'; ... } Durante a declaracao da varivel int i = 0, j = 10; float raio = 2.54; char c = 'd';

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 6 de 98

7 Tpicos em C reviso 257 de 24/03/2004 23:16

Entrada e Sada pelo Console


Introduo Neste captulo vamos apresentar conceitos bsicos de entrada e sada de dados para que exemplos possam ser construdos. Um programa que no fornece resultados nem pede valores para operar no deve ter grande utilidade. A entrada de dados ser feita pelo teclado e a sada poder ser vista na tela do computador. Com este mecanismo poderemos resolver problemas bastante interessantes. Biblioteca Padro Para termos acesso biblioteca que contm as funes, macros e variveis que facilitam a entrada e sada de dados o programa deve conter a declarao

#include <stdio.h> #include "stdio.h"

no incio do programa. Normalmente os programadores usam os smbolos menor (<) e maior (>), mas possvel a alternativa

Quando o nome do header file delimitado por < e >, a pesquisa feita pelo header em locais padro (por exemplo, nos sistemas UNIX, tipicamente no diretrio /usr/include). A diretiva include instrui o compilador a ler outro arquivo adicionando-o quele que contm a diretiva. Se o nome do arquivo estiver entre os sinais de maior e menor ele ser procurado da forma definida pelo compilador, normalmente em um diretrio especfico onde esto os arquivos de incluso. Quando se usa aspas o arquivo procurado de maneira definida pela implementao, isso significa procurar no diretrio de trabalho atual. Normalmente os programadores usam maior e menor para incluir os arquivos de cabealho padro e aspas para a incluso de arquivos do projeto. Sada - A Funo printf A funo printf permite que dados sejam escritos na tela do computador. O formato ao mesmo tempo de uso simples e bastante flexvel, permitindo que os dados possam ser apresentados de diversas maneiras. A forma geral : onde os argumentos so impressos de acordo com a maneira indicada pelo controle. Um exemplo simples pode tornar a explicao mais clara. O programa abaixo imprime o valor da varivel ano. #include <stdio.h> /* Biblioteca de entrada e saida */ int main() { /* Aqui comeca o programa principal (tambem so existe ele). */ int ano = 2004; /* Declarei ano como inteiro e ja defini seu valor. */ printf("Estamos no ano %d", ano); /* Imprime o valor do ano */ } Na tela do computador ser impresso: Estamos no ano 2004 O controle, que deve aparecer sempre entre " ", define como sero impressos os argumentos. Neste controle podem existir dois tipos de informaes: caracteres comuns e cdigos de formatao. Os caracteres comuns, como no exemplo (Estamos no ano) so escritos na tela sem nenhuma modificao. Os cdigos de formatao, aparecem precedidos por um % e so aplicados aos argumentos na ordem em que aparecem. Deve haver um cdigo de formatao para cada argumento. O cdigo %d indica que o valor armazenado em ano deve ser impresso na notao inteiro decimal. Cdigos de Converso Os cdigos de converso so os seguintes: Cdigo Comentrio %c Caracter simples %d Inteiro decimal com sinal %i Inteiro decimal com sinal %E Real em notao cientfica com E %e Real em notao cientfica com e %f Real em ponto flutuante %G Use %E ou %f, o que for mais curto %g Use %e ou %f, o que for mais curto %o Inteiro em base octal %s Cadeia Caracteres (string) %u Inteiro decimal sem sinal %x Inteiro em base hexadecimal (letras minsculas) %X Inteiro em base hexadecimal (letras maisculas) %% Imprime o caracter % Entre o % e o caracter do cdigo podem ser inseridos os seguintes comandos: Justificao da sada Um sinal de menos para especificar que o argumento deve ser ajustado a esquerda no seu campo de impresso. #include <stdio.h>

printf(controle, arg1, arg2, ...);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 7 de 98

8 Tpicos em C reviso 257 de 24/03/2004 23:16 int main (void){ int ano = 2001; printf("Justificado para esquerda Ano = %-8d\n", ano); printf("Justificado para direita Ano = %8d\n", ano); } Tamanho do Campo Um nmero inteiro especificando um tamanho de campo onde o argumento ser impresso. No exemplo acima o nmero especifica que 8 espaos so reservados para imprimir o resultado. Especificador de Preciso O especificador de preciso consiste de um ponto que separa o nmero que define o tamanho do campo do prximo nmero. Este prximo nmero especifica o mximo nmero de caracateres de um string a ser impresso, ou o nmero de dgitos a serem impressos a direita do ponto em um nmero do tipo float ou double No exemplo abaixo o nmero calculado impresso com 3 casas decimais em um campo de tamanho 9 espaos. #include <stdio.h> main(){ float r = 1.0/3.0; printf("O resultado e = %9.3f\n", r); } Modificadores de Comprimento O modificador de comprimento (a letra "l") que pode ser aplicado aos comandos de tipo d, i, o, u, x e X indicando que o dado do tipo long e no int O modificador de comprimento h indica um dado do tipo short int. O programa mostra exemplos de uso destes cdigos. Ao executar o exemplo verifique que \n no impresso. A barra inclinada chamada de sequncia de escape, indicando que o prximo caracter no para ser impresso mas representa caracteres invisveis ou caracteres que no esto representados no teclado, por exemplo. Alguns destes caracteres so: Cdigo Comentrio \n Quebra a linha, passando para a prxima \t Tabulao horizontal \b Retorna um caracter \f Salta uma pgina \0 Caracter nulo \xhh Caracter representado pelo cdigo ASCII hh (hh em notao hexadecimal) \nnn Representao de um byte em base octal

Entrada - A Funo scanf A funo scanf pode ser utilizada para entrada de dados a partir do teclado. Esta funo equivalente funo printf e seu formato : Uma diferena fundamental existe entre as duas funes. Os argumentos so os endereos das variveis que iro receber os valores lidos e no, como em printf, as prprias variveis. A indicao que estamos referenciando um endereo e no a varivel se faz pelo operador &. Por exemplo, o comando espera que dois valores inteiros sejam digitados no teclado. O primeiro armazenado na varivel a e o segundo em b. Um outro exemplo incluindo variveis reais : int i; float x; scanf("%d %f", i, x) Assumindo que a linha de entrada no teclado fosse a execuo do exemplo iria terminar com o valor 34 sendo armazenado em i e 56,43 em x. Usualmente o campo de controle s contm especificaes de converso, que so utilizadas para interpretar os dados que sero lidos. No entanto outros caracteres podem aparecer. O campo de controle pode conter:

scanf(controle, arg1, arg2, ...);

scanf("%d %d", &a, &b)

34 56.43

Especificaes de converso, consistindo do caracter %, um caracter opcional de supresso da atribuio (caracter nmero opcional para especificar o tamanho mximo do campo, e um caracter de converso.; Espaos, caracteres de tabulao ou linefeeds (tecla enter) que so ignorados; Caracteres comuns (no %) que devem casar com o prximo caracter diferente de branco da entrada.

*), um

Para que os valores digitados sejam separados por vrgulas, o comando deveria ser escrito da seguinte maneira: scanf("%d, %f", &i, &x); Observar que deve haver uma correspondncia exata entre os caracteres no brancos do controle e os caracteres digitados. Neste caso a entrada deveria ser: 35, 46.3 O programa mostra exemplos de uso da funo scanf. #include <stdio.h> void main (){ char c; int num1, num2;

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 8 de 98

9 Tpicos em C reviso 257 de 24/03/2004 23:16 printf("Programa para ilustrar a funcao scanf.\n"); printf("Entre com um caracter qualquer.\n"); scanf("%c", &c); printf("O codigo ASCII do caracter %c vale %d.\n", c, c); printf("Agora entre com dois numeros inteiros separados por virgula.\n"); scanf("%d, %d", &num1, &num2); printf("A soma destes numeros vale %d.\n", num1+num2); } Lendo e Imprimindo Caracteres Funes getchar e putchar Para ler e escrever caracteres do teclado as funes de entrada e sada mais simples so getchar e putchar e que esto na biblioteca stdio.h so os seguintes: int getchar(void); int putchar(int c); Apesar da funo getchar retornar um parmetro inteiro possvel atribuir este valor a uma varivel do tipo char porque o cdigo do caracter est armazenado no byte ordem mais baixa. O mesmo acontece com a funo putchar que recebe um inteiro, mas somente o byte de ordem mais baixa passado para a tela do computador. O programa abaixo mostra exemplos de uso destas funes. int main (void) { char c; int i; printf("Entre com um caracter entre 0 e 9.\n"); c = getchar(); printf("O caracter lido foi o ", putchar(c)); } Funes getch e getche Na definio original da funo getchar a entrada armazenada at que a tecla ENTER seja apertada. Com isto caracteres ficam em um buffer esperando para serem tratados. Em ambientes onde necessrio tratar o caracter imediatamente esta funo pode no ser til. Muitos compiladores incluem funes para permitir entrada interativa de dados. As duas funes mais comuns so getch e getche e seus prottipos, que podem ser encontrados em conio.h, so os seguintes: int getch(void); int getche (void); A funo getch espera at que uma tecla seja apertada e retorna este valor imediatamente. O caracter lido no ecoado na tela. A funo getche opera da mesma maneira, mas ecoa o caracter lido. Lendo e Imprimindo Strings Um string em C um vetor de caracteres. Para usar strings preciso primeiro definir um espao para armazen-los. Para isto preciso declarar o tamanho e o tipo do vetor. Por exemplo: char vsNome[40]; /* reserva um espaco para armazenar caracteres. */ Quando definir o tamanho do vetor observar que todo string em C termina com o caracter null ('\0'), que automaticamente inserido pelo compilador. Portanto o vetor nome pode armazenar no mximo 39 caracteres. Aps este passo o vetor nome pode ser usado durante a execuo do programa. Lendo e Imprimindo strings com scanf e printf O exemplo mostrado abaixo mostra como ler e imprimir um string usando os comandos scanf e printf respectivamente. /* Definicoes */ #define DIM 40 /* Bibliotecas usadas pelo programa */ #include <stdio.h> void main ( ) { char vsNome[DIM]; /* linha de caracteres lidos do teclado */ /* Entrada de dados do vetor */ puts("Por favor, qual o seu nome."); scanf("%s", vsNome); printf("Eu sou um computador PC, em que posso ajuda-lo %s?\n", vsNome); } Caso o nome digitado tenha sido Carlos Drummond de Andrade, o programa imprimiria somente o seguinte: Eu sou um computador PC, em que posso ajuda-lo Carlos. O cdigo de converso para strings %s. Observar que o comando scanf no l o nome todo, mas encerra a leitura quando encontra um caracter branco. Mas como ler para um vetor um nome inteiro, ou um string que contenha brancos? Para isto deve-se usar a funo gets.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 9 de 98

10 Tpicos em C reviso 257 de 24/03/2004 23:16 Lendo e Imprimindo strings com gets e puts A funo gets l todo o string at que a tecla ENTER seja digitada. No vetor o cdigo da tecla ENTER no armazenado sendo substitudo pelo cdigo null ('\0'), Caso a funo scanf do exemplo anterior fosse substituda pela gets o programa imprimiria Eu sou um computador PC, em que posso ajuda-lo Carlos Drummond de Andrade. o comando que substitui o scanf gets(nome). O prottipo da funo gets o seguinte: #include<stdio.h> char *gets (char *str); A funo puts tem o seguinte prottipo: #include<stdio.h> int puts (const char *str); Ela imprime o string apontado por str. O programa exemplo, mostrado abaixo, semelhante ao exemplo anterior com as funes printf substitudas por puts. Observe que a impresso sempre termina passando para a prxima linha. #define DIM 40 #include <stdio.h> void main ( ) { char vsNome[DIM]; /* linha de caracteres lidos do teclado */ /* Entrada de dados do vetor */ puts("Entre com o seu nome, por favor."); gets( vsNome); puts("Alo "); puts(vsNome); puts("Eu sou um computador PC, em que posso ajuda-lo?"); }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 10 de 98

11 Tpicos em C reviso 257 de 24/03/2004 23:16

Operadores e Expresses
Operadores Operador de Atribuio Este o operador usado para transferir o resultado de uma expresso para uma varivel. Por exemplo: Soma = a + b; Pi = 3.1415; possvel fazer-se vrias atribuies mltiplas em uma nica linha, como no exemplo a seguir: a = b = c = 1.0; As trs vriaveis recebem o mesmo valor. Operadores Aritmticos Os operadores aritmticos so os apresentados na tabela abaixo: Operador Operao menos unrio; multiplicao; * diviso; / mdulo - resto da diviso inteira; % Pr ou ps incremento; ++ Pr ou ps decremento. -soma; + subtrao; Um ponto importante que deve ser sempre levado em considerao, quando uma expresso for calculada, so os tipos das variveis porque eles alteram radicalmente os resultados das expresses. Por exemplo a diviso entre operandos do tipo inteiro trunca qualquer parte decimal que ocorra. No possvel aplicar a operao de mdulo a operandos do tipo float e double. Algumas regras simples de converso existem e sero apresentadas mais adiante. Por exemplo a operao 1/3 em C fornece como resultado o valor 0, enquanto que 1 % 3 igual a 3. Outro ponto importante so as regras de precedncia que resolvem que operao deve ser executada primeiro e que esto detalhadas mais adiante. Para os operadores aritmticos a operao de mais alta precedncia o - unrio, vindo em seguida *, / e % com a mesma prioridade. Os operadores ++ e -- so os seguintes na lista e por ltimo aparecem + e -. Operadores do mesmo nvel so agrupados da esquerda para a direita. O operador menos unrio multiplica seu operador por -1. Operadores Relacionais e Lgicos Operadores Relacionais Os operadores relacionais so:

> >= < <= == !=

maior que maior ou igual menor que menor ou igual igual a diferente de

Os operadores >, >=, <, <= tm a mesma precedncia e esto acima de == e !=. Estes operadores tem precedncia menor que os aritmticos, portanto expresses como i < limite-1 e i < (lim -1) tm o mesmo significado. Os operadores lgicos definem as maneiras como as relaes acima podem ser conectadas. Para simplificar a apresentao destes operadores sero usadas variveis para substituir as relaes. Estas variveis podem assumir dois valores TRUE e FALSE. Em C qualquer valor diferente de zero considerado TRUE. Observar que, assim como em operaes aritmticas, podemos ter combinaes de mais de vrias variveis em uma nica expresso. Operadores Lgicos Os operadores lgicos so os seguintes: && (AND lgico) A tabela verdade do operador && a seguinte: p 0 0 1 1 q 0 1 0 1 p && q 0 0 0 1

O resultado da expresso verdade se, e somente se, todas as variveis forem iguais a TRUE. Por exemplo, considere o seguinte trecho de programa:

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 11 de 98

12 Tpicos em C reviso 257 de 24/03/2004 23:16 int i = 3, j = -5; float z = 3.0; int resultado; resultado = (10 > 5) && ( i > -5) && (z != 0); printf("O resultado e vale %d.", resultado); O resultado deste trecho a impresso do valor 1, ou seja o valor correspondente a TRUE porque 10 maior que 5 E (&&) i maior que (-5) E (&&) z diferente de 0. || (OR lgico) A tabela verdade do operador || a seguinte: p 0 0 1 1

q 0 1 0 1

p || q 0 1 1 1

Para que o resultado seja verdade basta que qualquer uma das variveis seja TRUE. Por exemplo considere o seguinte trecho de programa. float x = 3.0; int n = 55; i = 0; int resultado; resultado = (i != 0) || (x == 0) || (n < 100); printf("O resultado e %d", resultado); O resultado deste trecho a impresso do valor 1, i no diferente de 0, x no diferente de zero mas n menor que 100. ! (NOT lgico) A tabela verdade do operador ! a seguinte: p 0 1 Por exemplo, considere o seguinte trecho de programa: int dia = 25, ano = 1959; int resultado; resultado = ! ( (dia < 30) && (ano > 1950) ) printf ("O resultado vale %d.", resultado); Este trecho de programa imprime 0 (FALSE), porque dia menor que 30 E ano maior que 1950, portanto o resultado do parnteses vale 1 (TRUE). No entanto, o operador ! nega este valor que vira 0. A tabela abaixo mostra, em ordem decrescente, a precedncia dos operadores lgicos e relacionais. 1. 2. 3. 4. 5.

ou seja o valor correspondente a TRUE porque

!p 1 0

! --- mais prioritrio > >= <= < == != && || --- menos prioritrio
Operadores Lgicos com Bits

Para operaes com bits a linguagem C dispe de alguns operadores que podem ser usados em tipos char e int e no podem ser usados em float, double, long double e void. A diferena entre estes operadores e os lgicos e que estes operam em bits. Os operadores em bits so os seguintes: Operador & | ^ ~ >> << Operao AND OR OR Exclusive NOT desloca direita desloca esquerda

Os operadores &, ! e ~ tem a mesma tabela verdade que os operadores &&, || e ! respectivamente. J o operador ^ (OR Exclusive) tem a seguinte tabela verdade.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 12 de 98

13 Tpicos em C reviso 257 de 24/03/2004 23:16 p 0 0 1 1 q 0 1 0 1 p^q 0 1 1 0

O resultado da operao verdadeiro se e somente se os dois operandos so diferentes. Os operandos de deslocamento tem o seguinte modo de operao: >> (deslocamento varivel >> nmero de deslocamentos << (deslocamento varivel << nmero de deslocamentos direita) esquerda) cuja cuja forma forma geral geral

O exemplo abaixo ilustra o uso dos operandos de deslocamento: unsigned char c; c = 7; /* codigo binario 0000 0111 */ c = c << 1; /* codigo binario 0000 1110 = 14 */ c = c << 2; /* codigo binario 0011 1000 = 56 */ c = c << 3; /* codigo binario 1100 0000 = 192 */ c = c >> 1; /* codigo binario 0110 0000 = 96 */ Observar que ao deslocar para esquerda bits so perdidos e ao deslocar para direita novamente os bits no so recuperados.

Observaes:
1. 2. 3. Nos deslocamentos direita em variveis unsigned e nos deslocamentos esquerda os bits que entram so zeros; Nos deslocamentos direita em variveis signed, os bits que entram correspondem ao sinal do nmero (1= sinal negativo, 0 = sinal positivo); Um deslocamento para a direita equivalente a uma diviso por 2. Deslocamento para a esquerda equivalente a uma multiplicao por 2. Assim a = a * 2; e a = a << 1; so equivalentes. Operadores de Atribuio Composta Em C qualquer expresso da forma:

<variavel> = <variavel> <operador> <expressao>


pode ser escrita como:

<variavel> <operando>= <expressao>


Por exemplo: ano = ano + 10; equivalente a ano += 10; Outros exemplos so:

raiz = raiz * 4; raiz *= 4; soma = soma / ( a + b); soma /= (a + b); a = a >> 1; a >>= 1;
Operador sizeof() O operador sizeof() um operador unrio que retorna o tamanho em bytes da expresso ou tipo fornecido como parmetro. Por exemplo, suponha que o tipo float tenha quatro bytes ento o a funo sizeof(float) retorna o valor quatro. Para se calcular o tamanho de bytes de uma expresso no necessrio o uso de parnteses. No exemplo, ilustramos alguns exemplos de uso do operador sizeof(). #define DIM 10 #include <stdio.h> #include <conio.h> void main(){ int i=0; float f=3.0; char c='a'; int v[DIM]; clrscr(); printf("Tamanho em bytes de alguns tipos\n"); printf("Tamanho de int %d\n", sizeof i); printf("Tamanho do float %d\n", sizeof f); printf("Tamanho do double %d\n", sizeof (double)); printf("Tamanho do char %d\n", sizeof c); printf("Tamanho do vetor de %d inteiros e %d\n", DIM, sizeof v);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 13 de 98

14 Tpicos em C reviso 257 de 24/03/2004 23:16 getch(); } Expresses Converso de Tipos Se os dois operandos de uma atribuio no so do mesmo tipo, o valor da expresso ou operador da direita ser convertido para o tipo do identificador da esquerda. Exemplo: Algumas atribuies com converso de tipo: int i; float r; i = 5; /* valor de i: 5 */ r = i ; /* valor de r: 5.0 */ A varivel i foi inicializada com o valor 5. Ao final da terceira instruo, r recebe o valor 5.0. Nestas converses podem ocorrer alteraes dos valores convertidos se o operando da esquerda for de um tipo que utilize menor numero de bytes que o operando da direita. Exemplo: Algumas atribuies com converso de tipo e perda de informao: int i; float r = 654.321; i = r; /* truncamento! */ Aps a execuo deste trecho de programa o valor da varivel i ser 654 pois seu valor foi truncado durante a converso. Pode-se dizer que as converses potencialmente perigosas (onde h possibilidade de perda de informao) so: char int float double Observe que o compilador C ao encontrar esta operao no gera nenhum warning para o programador. Assim este detalhe pode gerar um erro de programao (bug) que passe desapercebido ao programador inexperiente. possvel dizer que a linguagem C possui tipos macios (soft types) pois a operao com variveis de tipos diferentes perfeitamente possvel. Esta caracterstica do C se contrape a algumas linguagens em que isto no possvel (Pascal, FORTRAN, por exemplo). Estas linguagens possuem tipos duros (hard types). Limites do intervalo do tipo de dado. Tambm importante observar que os tipos em C tem intervalos bem definidos e os resultados das operaes devem respeitar estes intervalos. Se a uma varivel for atribudo um valor que esteja fora dos seus limites ento este valor ser alterado. Exemplo: Observe as expresses abaixo, assuma que i seja uma varivel do tipo int. i = 4999; /* o valor de i e 4999 */ i = 4999 + 1; /* o valor de i e 5000 */ i = 5000 + 30000; /* o valor de i e 30536 */ O valor de 35000 ultrapassou o limite superior do tipo int (32767), em um ambiente 16 bits. importante observar que em C, ao contrrio de outras linguagens, a ultrapassagem do limite de um tipo no interpretado como erro. Isto pode acarretar resultados inesperados para o programador desatento. Regras de Precedncia A tabela abaixo mostra, em ordem decrescente de prioridade, as regras de precedncia dos operadores em C. Operadores

() [] -> ! ~ ++ -- * & (tipo) sizeof() * / % + >> << < <= >= > == != & ^ | && || ? () : () = += -= *= etc ,

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 14 de 98

15 Tpicos em C reviso 257 de 24/03/2004 23:16

Comandos de Controle
Blocos de Comandos Blocos de comando so grupos de comandos que devem ser tratados como uma unidade lgica. O incio de um bloco em C marcado por uma { e o trmino por uma }. O bloco de comando serve para criar um grupo de comandos que devem ser executados juntos. Normalmente usa-se bloco de comandos quando se usa comandos de teste em que deve-se escolher entre executar dois blocos de comandos. Um bloco de comandos pode ser utilizado em qualquer trecho de programa que se pode usar um comando C. Comandos de Teste Comando if O comando if utilizado quando for necessrio escolher entre dois caminhos, ou quando se deseja executar um comando sujeito ao resultado de um teste. A forma geral do comando if a seguinte: if (expresso) comando1; else comando2; Neste comando a expressp avaliada e caso o resultado seja VERDADE (qualquer resultado diferente de zero) o executado, caso contrrio o comando2 que executado. Pela definio do comando a expresso deve ter como resultado um valor diferente de zero para ser considerada verdade. Observar que somente um dos dois comandos ser executado. Como a clusula else opcional a forma abaixo do comando if perfeitamente vlida. if (expresso) comando; Nos dois casos acima os comandos sempre podem ser substitudos por um bloco de comandos. A seguir mostramos alguns trechos de programas com exemplos de uso de comando if: scanf("%d", &dia); if ( dia > 31 || dia < 1 ) printf("Dia invalido\n"); scanf("%d", &numero); if ( numero >= 0 ) printf("Numero positivo\n"); else printf("Numero negativo\n"); scanf("%f", salario); if (salario < 800.00){ printf("Aliquota de imposto = 0.1\n"); imposto = salario * 0.1; } else { printf("Aliquota de imposto = 0.25\n"); imposto = salario * 0.25; } Uma construo que pode aparecer so os comandos if's em escada, cuja forma geral a seguinte:

comando1

if (expresso)

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 15 de 98

16 Tpicos em C reviso 257 de 24/03/2004 23:16

comando; else if (expresso) comando; else if (expresso) comando; ... else comando; if's em escada e aninhados.

O programa seguinte mostra um exemplo com /* Programa: c5calc.c Autor: Xxxx Objetivo: Data: 19/02/2004 */ #include <stdio.h> #include <conio.h> void main ( ) { float num1, num2, res; char oper; /* /* /* /*

Exemplo de uso de if para selecao de varias opcoes usando como exemplo uma calculadora simples.

primeiro operando */ segundo operando */ resultado da operacao */ caracter que define a operacao */

printf("\nEste programa simula uma calculadora simples.\n"); printf("Por favor entre com os dois operandos.\n"); scanf("%f %f", &num1, &num2); printf("%f %f\n", num1, num2); printf("Qual a operacao \n"); oper = getch(); printf("A operacao e %c\n", oper); if (oper == '+') res = num1 + num2; else if (oper == '-') res = num1 - num2; else if (oper == '*') res = num1 * num2; else if (oper == '/') if (num2 == 0.0){ printf("Operacao de divisao por 0 invalida!\n"); exit(0); }; else res = num1 / num2; else{ printf("Operacao invalida!\n"); exit(0); } printf("O resultado da %c vale %f.\n", oper, res); } Para evitar que o recuo da escada seja muito profundo o comando if em escada pode ser escrito da seguinte maneira: if (expresso) comando; else if (expresso) comando; else if (expresso) comando; ...

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 16 de 98

17 Tpicos em C reviso 257 de 24/03/2004 23:16 else comando;

Instruo switch O comando if, em todas suas formas, suficiente resolver problemas de seleao de comandos. Porm em alguns casos, como no exemplo anterior, o programa se torna mais trabalhoso para ser escrito. O comando switch facilita a escrita de trechos de programa em que a seleo deve ser feita entre vrias alternativas. A forma geral do comando switch a seguinte: switch (expresso) { case constante1: sequncia de comandos; break; case constante2: sequncia de comandos; break; case constante3: sequncia de comandos; break; ... default: sequncia de comandos; } A execuo do comando segue os seguintes passos: 1. 2. 3. A expresso avaliada; O resultado da expresso comparado com os valores das constantes que aparecem nos comandos case; Quando o resultado da expresso for igual a uma das constantes, a execuo se inicia a partir do comando associado com esta constante. A execuo continua com a execuo de todos os comandos at o fim do comando switch, ou at que um comando break seja encontrado; Caso no ocorra nenhuma coincidncia o comando default executado. O comando default opcional e se ele no aparecer nenhum comando ser executado.

4.

O comando break um dos comandos de desvio da linguagem C. O break se usa dentro do comando interromper a execuo e pular para o comando seguinte ao comando switch. H alguns pontos importantes que devem ser mencionados sobre o comando switch.

switch

para

O resultado da expresso deve ser um tipo compatvel com um inteiro, isto , expresses com resultados tipo tambm podem ser usadas; Notar que caso no apareca um comando de desvio todas as instrues seguintes ao teste executadas, mesmo as que estejam relacionadas com outros testes case; O comando switch s pode testar igualdade; No podem aparecer duas constantes iguais em um case; O programa seguinte mostra um exemplo de uso de comandos switch. /* Programa: c5calsw.c Autor: Xxxx Objetivo: */ /* Bibliotecas usadas pelo programa */ #include <stdio.h> Exemplo de uso de switch para selecao de varias opcoes usando como exemplo uma calculadora simples. Data: 19/02/2004

char

case que teve sucesso sero

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 17 de 98

18 Tpicos em C reviso 257 de 24/03/2004 23:16 #include <conio.h> /* /* /* /* primeiro operando */ segundo operando */ resultado da operacao */ caracter que define a operacao */

void main ( ) { float num1, num2, res; char oper;

printf("\nEste programa simula uma calculadora simples.\n"); printf("Por favor entre com os dois operandos.\n"); scanf("%f %f", &num1, &num2); printf("num1= %f num2 = %f\n", num1, num2); printf("Qual a operacao \n"); oper = getch(); printf("A operacao e %c\n", oper); switch (oper) { case '+': res = num1 + num2; break; case '-': res = num1 - num2; break; case '*': res = num1 * num2; break; case '/': if (num2 == 0.0){ printf("Divisao por zero e uma opcao invalida.\n"); exit(0); } else { res = num1 / num2; break; } default:{ printf("Operacao invalida!\n"); exit(0); } } printf("O resultado da %c vale %f.\n", oper, res); }

Comando Ternrio O comando ternrio tem este nome porque necessita de trs operandos para ser avaliado. O comando ternrio tem a seguinte forma: expresso1 ? expresso2 : expresso3 Para avaliar o resultado da expresso primeiro expresso1 avaliada. Caso este resultado seja correspondente ao valor VERDADE ento resultado da expresso ser igual ao valor da expresso2, no caso contrrio expresso3 avaliada e se torna o resultado. O programa seguinte mostra um exemplo de uso de comando ternrio. /* Programa: c5calsw.c Autor: Xxxx Objetivo: Exemplo de uso de comando ternario. O programa le dois valores e imprime o maior deles. Data: 13/08/2000 */ /* Bibliotecas usadas pelo programa */ #include <stdio.h> #include <conio.h> void main ( ) { float num1, num2, max; /* primeiro operando */ /* segundo operando */ /* resultado da operacao */

printf("\nEste programa imprime o maior valor de dois numeros lidos do teclado.\n"); printf("Por favor entre com os dois mumeros.\n"); scanf("%f %f", &num1, &num2); max = (num1>num2)?num1:num2; printf("O maior dos numeros lidos e %f.\n", max);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 18 de 98

19 Tpicos em C reviso 257 de 24/03/2004 23:16 } Laos Estes comandos permitem que trechos de programa sejam repetidos um certo nmero de vezes controlado pelo programa. O nmero de vezes que um lao ser executado pode ser fixo ou depender de condies. Comando for Este comando aparece em vrias linguagens de programao, mas na linguagem C ele apresenta uma grau maior de flexibilidade. A idia bsica do comando for a seguinte. Uma varivel de controle, geralmente um contador, recebe um valor inicial. O trecho de programa que pertence ao lao executado e ao final a varivel de controle incrementada ou decrementada e comparada com o valor final que ela deve alcanar. Caso a condio de trmino tenha sido atingida o lao interrompido. A forma geral do comando for a seguinte: for (expresso1; expresso2; expresso3) comando; As trs expresses geralmente tem os seguintes significados: 1. 2. 3. A expresso1 utilizada para inicializar a varivel de controle do lao; A expresso2 um teste que controla o fim do lao; A expresso3 normalmente faz um incremento ou decremento da varivel de controle.

A execuo do comando for segue os seguintes passos: 1. 2. 3. 4. 5. A expresso1 avaliada; A expresso2 avaliada para determinar se o comando deve ser executado; Se o resultado da expresso2 for VERDADE o comando executado caso contrrio o lao terminado; A expresso3 avaliada; Voltar para o passo onde 2.

O trecho de programa abaixo imprime todos os nmeros entre 1 e 100. for (i = 0; i <= 100; i++) printf("Numero %d\n", i); possvel omitir qualquer uma das expresses. Por exemplo, se a expresso2 for omitida o programa assume que ela sempre verdade de modo que o lao s termina com um comando de desvio como o break for (i = 0; ; i++){ printf("numero %d\n", i); if (i == 5) break; } O programa seguinte mostra como se pode calcular o fatorial de um nmero usando-se o comando for. /* Programa: c5fat.c Autor: Xxxx Objetivo: Ilustrar o comando for para executar o calculo de um fatorial. Data: 11/10/2000 */ #include <stdio.h> #include <conio.h> unsigned int fat (unsigned int num){ unsigned int fato=1, i; for (i=num; i>1; i--) fato = fato * i; return fato; } void main(){ unsigned int numero; printf("\nEntre com um numero positivo."); scanf("%u", &numero);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 19 de 98

20 Tpicos em C reviso 257 de 24/03/2004 23:16 printf("O fatorial de %u vale %u.", numero, fat(numero)); getch(); } Laos for com mais de um comando por expresso Outra possibilidade que o comando for em C permite e a incluso de mais de um comando, separado-os por vrgulas, nas expresses. O programa seguinte mostra um exemplo de uso de comando for com vrios comandos nas expresses. /* Programa: c5for1.c Autor: Xxxx Objetivo: Ilustrar o comando for com varios comandos nas expressoes de controle. Data: 12/10/2000 */ #include <stdio.h> void main(){ int i,j; for ( i=1, j = 10; i <= 10; i++, j += 10){ printf ("i = %d, j = %d\n", i, j); } } Laos for com testes com outras variveis A expresso de controle no precisa necessriamente envolver somente um teste com a varivel que controla o lao. O teste de final do lao pode ser qualquer expresso relacional ou lgica. No exemplo seguinte o lao pode terminar porque a varivel de controle j chegou ao seu valor limite ou foi batida a tecla '*'. /* Programa: c5linf.c Autor: Xxxx Objetivo: Ilustrar um comando for controle de termino que envolve outras variaveis alem da que controla o laco. Data: 12/10/2000 */ #include <stdio.h> #include <conio.h> void main() { char c; int i; for (i=0 ;(i<5) && (c=getchar()) != '*';i++ ){ printf("%c\n", toupper(c)); c = getchar(); } } Laos for com expresses faltando Um outro ponto importante do for que nem todas as expresses precisam estar presentes. No exemplo, a varivel de controle no incrementada. A nica maneira do programa terminar o usurio bater o nmero -1. /* Programa: c5linf.c Autor: Xxxx Objetivo: Ilustrar um comando for que nao tem todas as expressoes de controle. Data: 12/10/2000 */ #include<stdio.h> #include<conio.h> void main() { int i; for (i=0 ; i != -1 ; ){ printf("%d\n",i ); scanf ("%d", &i); } }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 20 de 98

21 Tpicos em C reviso 257 de 24/03/2004 23:16

Lao infinito Uma construo muito utilizada o lao infinito. No lao infinito o programa para quando se usa o comando break. No exemplo seguinte o programa s para for digitada ou a tecla 's' ou 'S '. #include<stdio.h> #include<conio.h> void main(){ int c; for ( ; ; ){ printf("\nVoce quer parar?\n"); c = getche(); if ( toupper(c) == 'S') break; } } Laos aninhados Uma importante construo aparece quando colocamos como comando a ser repetido um outro comando for. Esta construo aparece quando estamos trabalhando com matrizes. O exemplo seguinte mostra um programa que imprime uma tabuada. /* Programa: c5finf.c Autor: Xxxx Objetivo: Ilustrar lacos for aninhados que imprimem uma tabuada. Data: 16/10/2000 */ #include<stdio.h> #include<conio.h> void main(){ int i, j; printf("Programa que imprime a tabuada de multiplicacao.\n"); for (i=1 ; i<10 ; i++){ printf("\n\nTabuada de %d", i); for (j=1; j<10; j++) printf("\n%d x %d = %d", i, j, i * j); } }

Comando while O comando while tem a seguinte forma geral: while (expresso) comando; A expresso pode assumir o valor FALSO (igual a 0) ou VERDADE (diferente de 0). Os passos para execuo do comando so os seguintes: 1. 2. 3. A expresso avaliada; Se a expresso for VERDADE ento o comando executado, caso contrrio a execuo do comando terminada; Voltar para o passo 1.

Uma caracterstica do comando while, como pode ser visto dos passos acima, que o comando pode no ser executado. O exemplo de comando for escrito com comando while fica da seguinte maneira: i = 1; while (i <= 100){ printf("Numero %d\n", i); i++; } O trecho acima escrito de forma mais compacta : i = 1;

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 21 de 98

22 Tpicos em C reviso 257 de 24/03/2004 23:16 while (i++ <= 100) printf("Numero %d\n", i); A expresso do comando pode incluir chamada de funo. Lembrar que qualquer atribuio entre parnteses considerada como uma expresso que tem o valor da atribuio sendo feita. Por exemplo, o trecho abaixo repete um bloco de comandos enquanto o usurio usar a tecla 'c' para continuar, qualquer outra tecla o bloco interrompido. #include <stdio.h> #include <conio.h> void main(){ int c; puts("Tecle c para continuar.\n"); while ((c=getche()) == 'c'){ puts("\nNo Acabou.\n"); } puts("\nAcabou.\n"); } Comando do-while A forma genrica do comando a seguinte: do { comando; } while (expresso); Observar que neste comando a expresso de teste est aps a execuo do comando, portanto o comando executado pelo menos uma vez. A execuo do comando segue os seguintes passos: 1. 2. 3. Executa o comando; Avalia a expresso; Se a expresso for VERDADE ento volta para o passo 1, caso contrrio interrompe o do-while

O exemplo de comando for escrito com comando do-while fica da seguinte maneira: i = 1; do printf("Numero %d\n", i); i++; while (i <= 100); Comandos de Desvio Comando break O comando break pode ser tanto usado para terminar um teste case dentro de um comando switch quanto interromper a execuo de um lao. Quando o comando utilizado dentro de um comando for o lao imediatamente interrompido e o programa continua a execuo no comando seguinte ao comando for. No trecho de programa abaixo o comando for deve ler 100 nmeros inteiros positivos. No entanto, se for digitado um nmero negativo o comando for interrompido imediatamente sem que o nmero seja impresso. for (i = 0; i <100; i++){ scanf("%d", &num); if (num < 0) break;

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 22 de 98

23 Tpicos em C reviso 257 de 24/03/2004 23:16 printf("%d\n", num); } Comando continue O comando continue parecido com o comando break. A diferena que o comando continue interrompe a execuo da iterao corrente passando para a prxima iterao do lao, se houver uma. No comando for o controle passa para o teste e o incremento do lao sejam executados, nos comandos while e do-while o controle passa para a fase de testes. No trecho de programa abaixo o lao l 100 nmeros inteiros, caso o nmero seja negativo ele um novo nmero lido. for (i = 0; i <100; i++){ scanf("%d", &num); if (num < 0) continue; printf("%d\n", num); } Comando goto O comando goto causa um desvio incondicional para um outro ponto da funo em que o comando est sendo usado. O comando para onde deve ser feito o desvio indicado por um rtulo, que um identificador vlido em C seguido por dois pontos. importante notar que o comando goto e o ponto para onde ser feito o desvio devem estar dentro da mesma funo. A forma geral deste comando : goto rtulo; . . . rtulo: Este comando durante muito tempo foi associado a programas ilegveis. O argumento para esta afirmao se baseia no fato de que programas com comandos goto perdem a organizao e estrutura porque o fluxo de execuo pode ficar saltando erraticamente de um ponto para outro. Atualmente as restries ao uso do comando tem diminudo e seu uso pode ser admitido em alguns casos. Funo exit() A funo exit provoca a terminao de um programa, retornando o controle ao sistema operacional. O prottipo da funo a seguinte: void exit( int codigo); Observar que esta funo interrompe o programa como um todo. O cdigo usado para indicar qual condio causou a interrupo do programa. Comando return O comando return usado para interromper a execuo de uma funo e retornar um valor ao programa que chamou esta funo. Caso haja algum valor associado ao comando return este devolvido para a funo, caso contrrio um valor qualquer retornado. A forma geral do comando : return expresso; Notar que a expresso opcional. A chave que termina uma funo equivalente a um comando return sem a expresso correspondente. possvel haver mais de um comando return dentro de uma funo. O primeiro que for encontrado durante a execuo causar o fim da execuo. Uma funo declarada como do tipo void no pode ter um comando return que retorne um valor. Isto no faz sentido, j que funes deste tipo no podem retornar valores.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 23 de 98

24 Tpicos em C reviso 257 de 24/03/2004 23:16

Vetores e Cadeias de Caracteres


Introduo Vetores so usados para tratamento de conjuntos de dados que possuem as mesmas caractersticas. Uma das vantagens de usar vetores que o conjunto recebe um nome comum e elementos deste conjunto so referenciados atravs de ndices. Pelo nome vetor estaremos referenciando estruturas que podem ter mais de uma dimenso, como por exemplo matrizes de duas dimenses. Declarao de Vetores Unidimensionais A forma geral da declarao de um vetor : tipo nome [tamanho]; onde tipo um tipo qualquer dados, nome o nome pelo qual o vetor vai ser referenciado e tamanho o nmero de elementos que o vetor vai conter. Observar que, em C, o primeiro elemento tem ndice 0 e o ltimo tamanho - 1. Exemplos de declaraes de vetores so: int numeros[1000]; /* conjunto de 1000 numeros inteiros */ float notas[65]; /* conjunto de 65 numeros reais */ char nome[40]; /* conjunto de 40 caracteres */ O espao de memria, em bytes, ocupado por um vetor igual a: espao = tamanho * (nmero de bytes ocupado por tipo) importante notar que em C no h verificao de limites em vetores. Isto significa que possvel ultrapassar o fim de um vetor e escrever em outras variveis, ou mesmo em trechos de cdigo. tarefa do programador fazer com que os ndices dos vetores estejam sempre dentro dos limites estabelecidos pela declarao do vetor. O exemplo, mostrado abaixo, ilustra como se declara um vetor, inicializa seus valores e imprime o contedo. Notar o uso da diretiva #define DIM 5 para definir uma constante, que posteriormente foi usada para estabelecer o tamanho do vetor. Esta constante passa a ser usada nas referncias ao vetor, observar o comando de gerao do conjunto. Caso seja necessrio trocar o tamanho do vetor basta alterar o valor da constante. #define DIM 5 #include <stdio.h> void main(){ int vetor[DIM]; unsigned int i, num; puts("\nEste programa gera um vetor contendo numeros inteiros.\n"); puts("Entre com o numero inicial do conjunto. "); scanf("%d", &num); /* Geracao do conjunto */ for (i=0 ; i<DIM; i++) vetor[i] = num++; /* Impressao do conjunto */ for (i=0; i <DIM; i++) printf("Elemento %d = %d\n", i, vetor[i]); } O exemplo, mostrado a seguir, calcula o produto escalar de dois vetores inteiros. Observar como na leitura dos elementos do vetor usa-se o operador de endereo & antes do nome de cada elemento. /* Definicoes */ #define DIM 5 /* Bibliotecas usadas pelo programa */ #include <stdio.h> #include <conio.h> void main ( ) { int vetor1[DIM], vetor2[DIM], i, prod=0; printf("Primeiro, entre com um vetor de %d elementos\n", DIM); for (i = 0; i < DIM; i++){ printf("Elemento %d ", i); scanf("%d", &vetor1[i]); } printf("Agora, entre com um outro vetor de %d elementos\n", DIM); for (i = 0; i < DIM; i++){ printf("Elemento %d ", i); scanf("%d", &vetor2[i]); } for (i = 0; i < DIM; i++) prod += vetor1[i] * vetor2[i]; printf("O produto vale %d", prod); } O exemplo, mostrado a seguir ilustra o mtodo da bolha para ordenao em ordem crescente de um vetor de inteiros. Neste mtodo a cada etapa o maior elemento movido para a sua posio. /* Definicoes */

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 24 de 98

25 Tpicos em C reviso 257 de 24/03/2004 23:16 #define DIM 5 #define FALSO 0 #define VERDADE 1 /* Bibliotecas usadas pelo programa */ #include <stdio.h> #include <conio.h> void main ( ) { int vetor[DIM], i, trocou = FALSO, fim=DIM, temp; printf("Primeiro, entre com um vetor de %d elementos\n", DIM); for (i = 0; i<DIM; i++){ printf("Elemento %d ", i); scanf("%d", &vetor[i]); } do { trocou = FALSO; for (i=0; i<fim-1; i++){ if (vetor[i]>vetor[i+1]){ temp = vetor[i]; vetor[i] = vetor[i+1]; vetor[i+1] = temp; trocou = VERDADE; } } fim--; } while (trocou); for (i=0; i<DIM; i++) printf("%d\n", vetor[i]); getch(); } Cadeias de Caracteres Um cadeia um conjunto de caracteres terminado por um caractere nulo, que geralmente especificado como '\0'. Para especificar um vetor para armazenar um cadeia deve-se sempre reservar um espao para este caracter. Por exemplo, para armazenar um cadeia de 40 caracteres deve-se reservar um vetor de 41 de caracteres. Em C possvel haver constantes cadeia, que so definidas como uma lista de caracteres entre aspas. Por exemplo, "programando em C" No necessrio a colocao do caracter nulo ao final da cadeia. Em C no h tipo cadeia e portanto conjuntos de caracteres teriam de ser tratados como conjuntos de nmeros inteiros, por exemplo. Para facilitar a programao foram criadas algumas funes para manipular cadeias. As funes mais comuns so as seguintes: Funes de cadeia strcat(dest,orig) strncat n) (dest, orig, Descrio Concatena cadeia origem ao final de destino Concatena cadeia orig ao final de dest, usando no mximo n caracteres de orig. Compara os dois cadeias. Retorna zero se iguais, menor que 0 se str1 < str2, maior que 0 se str1 > str2 Compara os dois cadeias sem levar em conta maisculas e minsculas Calcula o comprimento da cadeia sem o caracater nulo. Converte cadeia para minsculas Converte cadeia para maisculas Copia cadeia origem para destino

strcmp(str1,str2) strcmpi (str1, str2) strlen(str) strlwr(str) strupr(str) strcpy(dest,orig) cadeia.

Estas funes esto na biblioteca

string.h. O exemplo seguinte mostra exemplos de uso de algumas das funoes de

#include<string.h> #include<stdio.h> #include<conio.h> void main(){ char c, nome[40]; char sobrenome[10]; int i; clrscr(); printf("Entre com um nome "); gets(nome); puts(nome); printf("Entre com um sobrenome ");

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 25 de 98

26 Tpicos em C reviso 257 de 24/03/2004 23:16 gets(sobrenome); puts(nome); strcat(nome, " "); strcat(nome, sobrenome); puts(nome); printf("Qual caracter? "); c = getch(); for (i=0; i<strlen(nome); i++) if (c == nome[i]) printf("\n%d", i); getch(); } Declarao de Vetores Multidimensionais Em C existe a possibilidade de declarmos vetores de mais de uma dimenso. A forma geral da declarao a seguinte: tipo nome [dim1][dim2][dim3]...[dimN]; onde dimI o tamanho da dimenso I. Deve-se tomar cuidado com armazenamento de matrizes multidimensionais, por que a memria necessria para guardar estes dados igual a sizeof(tipo)*dim1*dim2*dim3*...*dimN Por exemplo a declarao define uma matriz quadrada de 10 linhas por 20 colunas, enquanto o comando armazena o dobro do elemento que est na terceira linha e oitava coluna na varivel c. Observar que o primeiro ndice indica a linha e o segundo a coluna. O exemplo abaixo mostra um programa que l uma matriz de trs linhas e cinco colunas e imprime os valores. #define DIML 3 #define DIMC 5 #include<stdio.h> #include<conio.h> void main(){ int i, j; int matriz[DIML][DIMC]; for (i=0; i<DIML; i++) for (j=0; j<DIMC; j++) scanf("%d", &matriz[i][j]); for (i=0; i<DIML; i++){ for (j=0; j<DIMC; j++) printf("%4d", matriz[i][j]); printf("\n"); } getch(); } A matriz armazenada na memria linha a linha e a figura abaixo ilustra esta idia com uma matriz de nmeros inteiros de trs por trs. Estamos assumindo que cada nmero inteiro ocupa dois bytes, o endereo aponta um byte e a matriz est armazenada a partir do endereo 1000. End 1000 1002 1004 1006 1008 1010 1012 1016 1018 Vetores de Cadeias de Caracteres A declarao abaixo mostra uma matriz de cadeias de caracteres com 30 linhas de 80 caracteres. Elemento m[0][0] m[0][1] m[0][2] m[1][0] m[1][1] m[1][2] m[2][0] m[2][1] m[2][2]

int matriz[10][20];

c = 2 * matriz[3][8];

char nome_turma[30][80];
O exemplo abaixo mostra um programa que l uma matriz de nomes imprime os seus contedos. #define DIML 5 #define DIMC 40

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 26 de 98

27 Tpicos em C reviso 257 de 24/03/2004 23:16 #include<stdio.h> #include<conio.h> void main(){ int i, j; int nomes[DIML][DIMC]; for (i=0; i<DIML; i++){ printf("Entre com a linha %d", i); gets(nomes[i]); } for (i=0; i<DIML; i++){ printf("O nome %d e\n", i); puts(nomes[i]); } getch(); } Inicializao de Vetores Em C possvel inicializar vetores no momento em que so declarados da mesma forma que variveis. A forma de fazer isto a seguinte: tipo nome[dim1][dim2]...[dimN] = {Lista de valores}; A lista de valores um conjunto de valores separados por vrgulas. Por exemplo, a declarao abaixo iniciliza um vetor inteiro de cinco posies. int vetor[5] = { 10, 15, 20, 25, 30 }; Observe que nesta declarao necessrio que o tamanho do conjunto seja conhecido antecipadamente. No entanto, tambm possvel inicializar vetores em que no se conhece o seu tamanho. Observar que neste caso importante que o programador preveja um modo de indicar o fim do vetor. O exemplo a seguir mostra os dois casos ilustrados acima. Para descobrir o como parar de processar o vetor apresentamos duas solues possveis. No primeiro caso a condio de fim do vetor o nmero negativo -1, sendo assim uma posio do vetor perdida para armazenar esta condio. No segundo caso usado o operador sizeof para descobir o tamanho do vetor. Observe que sizeof calcula o tamanho do vetor em bytes e por esta razo necessrio uma diviso pelo tamanho em bytes do tipo de cada elemento. #define DIM 5 #include <stdio.h> #include <conio.h> void main(){ int vetor[DIM] = {10, 15, 20, 25, 30}; int vetor1[] = {10, 20, 30, 40, 50, 60, -1}; int vetor2[] = {3, 6, 9, 12, 15, 18, 21, 24}; unsigned int i, tam; clrscr(); printf("\nEste programa imprime um vetor contendo numeros inteiros e\n"); printf("que foi inicializado durante a sua declaracao.\n"); /* Impressao dos conjuntos */ printf("\nVetor com tamanho pre-definido\n"); for (i=0; i <DIM; i++) printf("Elemento %d = %d\n", i, vetor[i]); printf("\nVetor terminando por -1\n"); for (i=0; vetor1[i]>0; i++) printf("Elemento %d = %d\n", i, vetor1[i]); tam = sizeof vetor2 / sizeof (int); printf("\nDescobrindo o tamanho do Vetor\n"); for (i=0; i < tam ; i++) printf("Elemento %d = %d\n", i, vetor2[i]); getch(); } possvel inicializar matrizes multidimensionais e neste caso necessrio especificar todas as dimenses menos a primeira para que o compilador possa reservar memria de maneira adequada. A primeira dimenso somente especifica quantos elementos o vetor ir armazenar e isto lendo a inicializao o compilador pode descobrir. O exemplo abaixo ilustra a definio de um vetor de cadeia de caracteres, que nada mais do que uma matriz de caracteres. #define DIM 5 #include <stdio.h> #include <conio.h> void main() { char disciplinas [][40] = { "disc 0: Computacao para Informatica", "disc 1: Banco de Dados I", "disc 2: Banco de Dados II",

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 27 de 98

28 Tpicos em C reviso 257 de 24/03/2004 23:16 "disc 3: Arquitetura de Computadores I" }; int i; clrscr(); printf("Qual a disciplina? "); scanf("%d", &i); puts(disciplinas[i]); getch(); } A declarao abaixo ilustra como declarar e inicializar uma matriz de trs linhas por quatro colunas de nmeros reais. float mat[][4] = { 1.0, 2.0, 3.0, 4.0, // linha 1 8.0, 9.0, 7.5, 6.0, // linha 2 0.0, 0.1, 0.5, 0.4 }; // linha 3

Limites de Vetores e sua Representao em Memria Na linguagem C, devemos ter cuidado com os limites de um vetor. Embora na sua declarao, tenhamos definido o tamanho de um vetor, o C no faz nenhum teste de verificao de acesso a um elemento dentro do vetor ou no. Por exemplo se declaramos um vetor como int valor[5], teoricamente s tem sentido usarmos os elementos valor[0], ..., valor[4]. Porm, o C no acusa erro se usarmos valor[12] em algum lugar do programa. Estes testes de limite devem ser feitos logicamente dentro do programa. Este fato se deve a maneira como o C trata vetores. A memria do microcomputador um espao (fsico) particionado em pores de 1 byte. Se declaramos um vetor como int vet[3], estamos reservando 6 bytes (3 segmentos de 2 bytes) de memria para armazenar os seus elementos. O primeiro segmento ser reservado para vet[0], o segundo segmento para vet[1] e o terceiro segmento para vet[2]. O segmento inicial chamado de segmento base, de modo que vet[0] ser localizado no segmento base. Quando acessamos o elemento vet[i], o processador acessa o segmento localizado em base+i. Se i for igual a 2, estamos acessando o segmento base+2 ou vet[2](o ultimo segmento reservado para o vetor). Porm, se i for igual a 7, estamos a acessando segmento base+7 que no foi reservado para os elementos do vetor e que provavelmente est sendo usado por uma outra varivel ou contm informao espria (lixo). Observe que acessar um segmento fora do espao destinado a um vetor pode destruir informaes reservadas de outras variveis. Estes erros so difceis de detectar pois o compilador no gera nenhuma mensagem de erro... A soluo mais adequada sempre avaliar os limites de um vetor antes de manipul-lo. A princpio este fato poderia parecer um defeito da linguagem, mas na verdade trata-se de um recurso muito poderoso do C. Poder manipular sem restries todos os segmentos de memria uma flexibilidade apreciada pelos programadores.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 28 de 98

29 Tpicos em C reviso 257 de 24/03/2004 23:16

Funes
Introduo Em C, diferentemente de outras linguagens como Pascal, todas as aes ocorrem dentro de funes. Na linguagem C no h conceito de um programa principal, o que existe uma funo chamada main que sempre a primeira a ser executada. A forma geral de uma funo em C a seguinte: tipo nome (tipo nome1, tipo nome2, ..., tipo nomeN ){ corpo do funo } O tipo na definio da funo especifica o tipo que ser devolvido quando o comando return for executado. Caso nenhum tipo seja especificado o compilador assume que a um tipo inteiro retornado. O tipo void pode ser usado para declarar funes que no retornam valor algum. A lista de parmetros uma lista, separada por vrgulas, de variveis com seus tipos associados . possvel que existam funes que no tenham lista de parmetros, mas ainda assim necessrio que os parnteses sejam usados. importante notar que diferentemente de declaraes de variveis onde podemos associar vrios nomes de variveis a uma declarao como em int a, dia, mes, i; na lista de parmetros necessrio associar um tipo a cada varivel como no exemplo abaixo: float media (float n1, float n2, float n3) Os parmetros so valores que a funo recebe para realizar as tarefas para as quais foi programada. Por exemplo, uma funo que calcule a raiz quadrada de um nmero real, deve ter como parmetro uma varivel real para receber este valor. A funo que vai usar uma outra funo em seu corpo de programa deve colocar o nome da funo chamada e a lista de valores que deseja passar para a funo chamada. importante notar que os tipos e o nmero de parmetros que aparecem na declarao da funo e na sua chamada correspondam exatamente. Se os tipos so incompatveis, o compilador no gera um erro, mas podem ser gerados avisos na compilao e resultados estranhos. Prottipos de Funes O padro ANSI estendeu a declarao da funo para permitir que o compilador faa uma verificao mais rgida da compatibilidade entre os tipos que a funo espera receber e queles que so fornecidos. Prottipos de funes ajudam a detectar erros antes que eles ocorram, impedindo que funes sejam chamadas com argumentos inconsistentes. A forma geral de definio de um prottipo : tipo nome (tipo nome1, tipo nome2, ..., tipo nomeN); O exemplo mostra a declarao de uma funo e seu prottipo. #include<stdio.h> #include<conio.h> /* Prototipo da funcao */ int soma (int, int); /* Definicao da funcao */ int soma(int a, int b){ return a+b; } /* Funcao Principal */ void main(){ int a=5, b=9; clrscr(); printf("\n%d", soma(a,b)); getch(); } Escopo de Variveis Variveis podem ser usadas dentro de uma funo particular ou pode ocorrer que uma ou mais variveis precisem ser acessveis por diversas funes diferentes. Por esta razo temos que definir onde as variveis de um programa podem ser definidas e onde elas esto disponveis. As variveis podem ser declaradas basicamente em trs lugares: dentro de funes, fora de todas as funes e na lista de parmetros das funes. As variveis dentros das funes so chamadas de variveis locais, as que aparecem fora de todas as funes chamamos de variveis globais e aquelas que aparecem na lista de parmetros so os parmetros formais. importante notar que em C todas as funes esto no mesmo nvel, isto no possvel definir uma funo dentro de outra funo. Variveis Locais As variveis locais so aquelas declaradas dentro de uma funo. Elas passam a existir quando do incio da execuo do bloco de comandos ou funo onde foram definidas e so destrudas ao final da execuo do bloco. Uma varivel local s pode ser referenciada, ou seja usada, dentro das funes onde foram declaradas. Outro ponto muito importante que como as variveis locais

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 29 de 98

30 Tpicos em C reviso 257 de 24/03/2004 23:16 deixam de existir ao final da execuo da funo, elas so invisveis para outras funes do mesmo programa. O cdigo que define uma funo e os seus dados so particulares a funo. Alguns autores usam o termo variveis automticas para se referir as variveis locais. Em C existe a palavra chave auto que pode ser usada para declarar variveis locais. No entanto, como todas as variveis locais so por definio automticas raramente se usa esta palavra chave. Observe que um bloco de comandos se inicia em um "{" e termina em um "}". O bloco de comandos mais usado para definir uma varivel a funo. Todas as variveis que sero usadas dentro de um bloco de comandos precisam ser declaradas antes do primeiro comando do bloco. Declaraes de variveis, incluindo sua inicializao, podem vir logo aps o abre chaves que incia um bloco de comandos, no somente o que comea uma funo. O exemplo abaixo ilustra este tipo de declarao: #include <stdio.h> void main(){ int i; for (i=0; i<10; i++){ int t; scanf("%d", &t); printf("%d\n", i*t); } } Existem algumas vantagens em se declarar variveis dentro de blocos. Como as variveis somente passam a existir quando o bloco passa a ser executado, o programa ocupa menos espao de memria. Isto porque se a execuo do bloco for condicional a varivel pode nem ser alocada. Outra vantagem que como a varivel somente existe dentro do bloco pode-se controlar melhor o uso da varivel, evitando erros de uso indevido da varivel. Variveis Globais As variveis globais so definidas fora de qualquer funo e so portanto disponveis para qualquer funo. Este tipo de varivel pode servir como um canal de comunicao entre funes, uma maneira de transferir valores entre elas. Por exemplo, se duas funes tem de partilhar dados mais uma no chama a outra, uma varivel global tem de ser usada. Parmetros Formais As variveis que aparecem na lista de parmetros da funo so chamadas de parmetros formais da funo. Eles so criados no incio da execuo da funo e destrudos no final. Parmetros so valores que as funes recebem da funo que a chamou. Portanto, os parmetros permitem que uma funo passe valores para outra. Normalmente os parmetros so inicializados durante a chamada da funo, pois para isto que foram criadas. No entanto, as variveis que atuam como parmetros so iguais a todas as outras e podem ser modificadas, operadas, etc, sem nenhuma restrio. Passagem de Parmetros por Valor Parmetros podem ser passados para funes de duas maneiras: passagem por valor ou passagem por referncia. Na passagem por valor uma cpia do valor do argumento passado para a funo. Neste caso a funo que recebe este valor ao fazer modificaes no parmetro no estar alterando o valor original que somente existe na funo que chamou. Passagem de Parmetros por Referncia Na passagem por referncia o que passado para a funo o endereo do parmetro e portanto a funo que recebe pode atravs do endereo modificar o valor do argumento na funo que chamou, Para a passagem de parmetros por referncia necessrio o uso de ponteiros. Este assunto ser discutido no prximo captulo e portanto neste captulo estaremos usando somente funes com passagem por valor. Passagem de Vetores e Matrizes Matrizes so um caso especial e exceo a regra que parmetros so passados sempre por valor. Como veremos mais adiante, o nome de um vetor corresponde ao endereo do primeiro elemento do array, Quando um vetor passado como parmetro, apenas o endereo do primeiro elemento passado. Existem basicamente trs maneiras de declarar um vetor como um parmetro de uma funo. Na primeira ele declarado como tem sido apresentado em todos os exemplos at agora. O exemplo mostrado abaixo mostra um programa que usa uma funo para descobrir quantas vezes um caracter ocorre em um vetor. Observe que a dimenso do vetor foi declarada explicitamente. #include<stdio.h> #include<conio.h> #define DIM 80 char conta (char v[], char c); char conta (char v[DIM], char c){ int i=0, vezes=0; while (v[i] != '\0') if (v[i++] == c) vezes++; return vezes; } void main(){ char linha[DIM]; char c; int maiusculas[26], minusculas[26]; puts("Entre com uma linha"); gets (linha); for (c='a'; c<='z'; c++) minusculas[c-'a'] = conta(linha, c); for (c='A'; c<='Z'; c++) maiusculas[c-'A'] = conta(linha, c);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 30 de 98

31 Tpicos em C reviso 257 de 24/03/2004 23:16 for (c='a'; c<='z'; c++) if (minusculas[c-'a']) printf("%c apareceu %d vezes\n", c, minusculas[c-'a']); for (c='A'; c<='Z'; c++) if (maiusculas[c-'A']) printf("%c apareceu %d vezes\n", c, maiusculas[c-'A']); getch(); } Uma outra maneira, leva em conta que apenas o endereo do vetor passado. Neste modo o parmetro declarado como um vetor sem dimenso. Isto perfeitamente possvel porque a funo somente precisa receber o endereo onde se encontra o vetor. Alm disso C no confere limites de vetores e portanto a funo precisa do endereo inicial do vetor e uma maneira de descobrir o final do vetor. Esta maneira pode ser, por exemplo, uma constante, ou o caracter '\0' em um vetor de caracteres. Para ilustrar este modo de passar vetores O exemplo mostra este modo de passar vetores com um programa que inverte o contedo de um vetor. #include<stdio.h> #include<conio.h> #define DIM 6 void Le_vetor (int v[], int tam){ int i; for (i=0; i<tam; i++){ printf("%d = ? ", i); scanf("%d", &v[i]); } } void Imprime_vetor (int v[], int tam){ int i; for (i=0; i<tam; i++) printf("%d = %d\n", i, v[i]); } void Inverte_vetor (int v[], int tam){ int i, temp; for (i=0; i<tam/2; i++){ temp = v[i]; v[i] = v[tam-i-1]; v[tam-i-1] = temp; } } void main(){ int v[DIM], i; Le_vetor(v, DIM); Imprime_vetor (v, DIM); Inverte_vetor (v, DIM); Imprime_vetor (v, DIM); getch(); } O Comando return O comando return usado para retornar o valor calculado para a funo que chamou. Qualquer expresso pode aparecer no comando, que tem a a seguinte forma geral: return expresso A funo que chamou livre para ignorar o valor retornado. Alm disso a funo pode no conter o comando e portanto nenhum valor retornado e neste caso a funo termina quando o ltimo comando da funo executado. Quando o comando return no existe o valor de retorno considerado indefinido. As funes que no retornam valores devem ser declaradas como do tipo void. importante observar que funes que so declaradas com um tipo vlido podem ser includas em qualquer expresso vlidas em C. Recurso Funes em C podem ser usadas recursivamente, isto uma funo pode chamar a si mesmo. como se procurssemos no dicionrio a definio da palavra recurso e encontrssemos o seguinte texto: recurso: s.f. Veja a definio em recurso Um exemplo simples funo que pode ser usada com chamadas recursivas o fatorial de um nmero inteiro. O fatorial de um nmero pode ser defindo como o produto deste nmero pelo fatorial de seu predecessor, ou seja n! = n * (n-1)! A funo fatorial implementada sem recurso foi ilustrada anteriormente.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 31 de 98

32 Tpicos em C reviso 257 de 24/03/2004 23:16 A alternativa uma funo recursiva em que cada chamada da funo que calcula o fatorial chama a prpria funo fatorial. O exemplo ilustrado abaixo mostra como a funo pode ser escrita recursivamente. #include <stdio.h> #include <conio.h> unsigned int fat (unsigned int num){ unsigned int fato; if (num == 1) return (1); else fato = num * fat (num-1); return fato; } void main(){ unsigned int numero; printf("\nEntre com um numero positivo."); scanf("%u", &numero); printf("O fatorial de %u vale %u.", numero, fat(numero)); getch(); } Quando a funo fatorial recursiva chamada primeiro verificado se o nmero recebido como parmetro vale 1. Neste caso a funo retorna o valor 1, caso contrrio ela devolve o valor da expresso num * fat(num-1), ou seja o produto do nmero pelo valor do fatorial do nmero predecessor. Portanto, quando se incia o processo a funo chamada com o valor do nmero, e a funo sendo chamada sempre com este nmero sendo decrementado at que ele seja um. Quando o processo se reverte e as chamadas comeam a ser respondidas. Um ponto importante que toda funo recursiva deve prever cuidadosamente como o processo de recurso deve ser interrompido. No caso da funo fat o processo interrompido quando o valor do nmero vale 1. Quando uma funo chama a si mesmo recursivamente ela recebe um conjunto novo de variveis na pilha que usada para transferncia de valores entre funes. importante notar que recurso no trs obrigatoriamente economia de memria porque os valores sendo processados tem de ser mantidos na pilha. Nem ser mais rpido, e as vezes pode ser at mais lento porque temos o custo de chamada as funes. As principais vantagens da recurso so cdigos mais compactos e provalvemente mais fceis de serem lidos. Argumentos - argc e argv A funo main como todas as funes podem ter parmetros. Como a funo main sempre a primeira a ser executada, os parmetros que ela recebe so fornecidos pela linha de comando. No caso dos compiladores da Borland no menu Run uma das opes so os argumentos a serem passados para a funo main. No caso da funo main so usados dois argumentos especiais argc e argv. O primeiro argumento, argc, uma varivel inteira que indica quantos argumentos foram fornecidos para a funo. Observar que argc vale sempre pelo menos 1, porque o nome do programa sempre o primeiro argumento fornecido ao programa. A partir do segundo argumento em diante que aparecem os outros argumentos. O outro parmetro um vetor de cadeias de caracteres. Todos os argumentos so fornecidos em forma de cadeias de caracteres e portanto, caso sejam fornecidos nmeros, estes devem ser convertidos para o formato requerido. Cada um dos argumentos do programa um elemento deste vetor. A primeira linha da funo main pode ter a seguinte forma. void main (int argc, char *argv[]) O programa abaixo calcula o fatorial dos nmeros fornecidos como argumentos. #include <stdio.h> #include <stdlib.h> #include <conio.h> unsigned long int fat (unsigned long int num){ unsigned long int fato; if (num == 1) return (1); else fato = num * fat (num-1); return fato; } void main(int argc, char *argv[]){ unsigned long int numero, fatorial; unsigned int i; clrscr(); if (argc<2){ printf("Para rodar: %s num1 num2 ... .\n", argv[0]); exit(0);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 32 de 98

33 Tpicos em C reviso 257 de 24/03/2004 23:16 } i = argc; for ( i=1; i<argc; i++){ numero = (unsigned long int) (atoi(argv[i])); fatorial = fat(numero); printf("O fatorial de %lu vale %lu.\n", numero, fatorial); } } Os nomes argc e argv so comumente usados mas o programador livre para escolher os nomes mais apropriados.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 33 de 98

34 Tpicos em C reviso 257 de 24/03/2004 23:16

Ponteiros
Introduo Ponteiros so usados em situaes em que necessrio conhecer o endereo onde est armazenada a varivel e no o seu contedo. Um ponteiro uma varivel que contm um endereo de memria e no o contedo da posio. A memria de um computador pode ser vista como uma sequncia de bytes cada um com seu prprio endereo. No h dois bytes com o mesmo endereo. O primeiro endereo sempre 0 e o ltimo geralmente uma potncia de 2. Por exemplo um computador com memria igual a 16 Mbytes tem 16x1024x1024 bytes. A figura abaixo mostra um mapa de um trecho de memria que contm duas variveis (num, res) inteiras de tipo longo (4 bytes cada uma). Observar que os endereos esto pulando de quatro em quatro j que as variveis so inteiras de tipo longo. Uma possvel declarao destas variveis dentro de um programa C poderia ser: long int num=10, res=120; Endereos 996 1000 1004 Contedo --10 120 Varivel --num res

Mapa de Memria Ponteiros so importantes por exemplo quando se deseja que uma funo retorne mais de um valor. Por exemplo, uma funo pode receber no os valores dos parmetros mas sim ponteiros que apontem para seus endereos. Assim esta funo pode modificar diretamente os contedos destas variveis. Uma outra aplicao importante de ponteiros apontar para reas de memria que so admnistradas durante a execuo do programa. Com ponteiros possvel alocar as posies de memria necessrias para armazenamento de vetores somente quando o programa estiver rodando. O programador pode reservar o nmero exato de posies que o programa requer. Operaes com Ponteiros Declarao de Ponteiros Antes de serem usados os ponteiros, como as variveis, precisam ser declarados. A forma geral da declarao de um ponteiro a seguinte: tipo *nome; Onde tipo qualquer tipo vlido em C e nome o nome da varivel ponteiro. Por exemplo:

int *res; float *div;

/* ponteiro para uma variavel inteira */ /* ponteiro para uma variavel de ponto flutuante */

Os Operadores de Ponteiros Existem dois operadores especiais para ponteiros: * e &. Os dois operadores so unrios, isto requerem somente um operando. O operador & devolve o endereo de memria do seu operando. Por exemplo, pint = &soma; /* o endereco de soma e carregado em pint */ No exemplo seguinte considere a Tabela 8.1. Aps a execuo do trecho de programa abaixo a varivel ponteiro p termina com o valor 1000. p = &num; O operador * o complemento de &. O operador * devolve o valor da varivel localizada no endereo que o segue. Por exemplo, o comando num = *p; significa que a varivel num recebe o valor apontado por p. Estes operadores no devem ser confundidos com os j estudados em captulos anteriores. O operador * para ponteiros no tem nada a ver com o operador multiplicao. O operador ponteiro * unrio e, como o operador &, tem precedncia maior que do que todos os operadores aritmticos. Aritmtica de Ponteiros Atribuio de Ponteiros Do mesmo modo que uma varivel comum o contedo de um ponteiro pode ser passado para outro ponteiro do mesmo tipo. As variveis ponteiro devem sempre apontar para os tipos de dados corretos. Uma varivel ponteiro declarada como apontador de dados inteiros deve sempre apontar para dados deste tipo. Observar que em C possvel atribuir qualquer endereo a uma varivel ponteiro. Deste modo possvel atribuir o endereo de uma varivel do tipo float a um ponteiro inteiro. No entanto, o programa no ir funcionar da maneira correta. Por exemplo, no trecho de programa abaixo o endereo do terceiro elemento do vetor v carregado em p1 e o endereo da varivel i carregado em p2. Alm disso no final o endereo apontado por p1 carregado em p2. Os comandos printf imprimem os valores apontados pelos ponteiros respectivos. void main(void) { int vetor[] = { 10, 20, 30, 40, 50 }; int *p1, *p2; int i = 100;

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 34 de 98

35 Tpicos em C reviso 257 de 24/03/2004 23:16 p1 = &vetor[2]; printf("%d\n", *p1); p2 = &i; printf("%d\n", *p2); p2 = p1; printf("%d\n", *p2); } Acessando os Endereos O trecho de programa abaixo faz com que o endereo da varivel imprime o que est apontado por p. void main(void) { float x=3.14; float *p; p = &x; printf("*p = %f", *p); } Incrementando e Decrementando Ponteiros O exemploabaixo mostra que operaes de incremento e decremento podem ser aplicadas em operandos. O primeiro printf imprime 30 o segundo 40 e o terceiro 50. void main(void) { int vetor[] = { 10, 20, 30, 40, 50 }; int *p1; p1 = &vetor[2]; printf("%d\n", *p1); p1++; printf("%d\n", *p1); p1 = p1 + 1; printf("%d\n", *p1); } Pode parecer estranho que um endereo que aponte para um nmero inteiro que armazenado em dois bytes seja incrementado por um e passe para apontar para o prximo nmero inteiro. A resposta para isto que sempre que um ponteiro incrementado (decrementado) ele passa a apontar para a posio do elemento seguinte (anterior). Do mesmo modo somar trs a um ponteiro faz com que ele passe apontar para o terceiro elemento aps o atual. Portanto, um incremento em um ponteiro que aponta para um valor que armazenado em n bytes faz que n seja somado ao endereo. possvel se usar o seguinte comando *(p+1)=10; Este comando armazena o valor 10 na posio seguinte quela apontada por p. possvel somar-se e subtrair-se inteiros de ponteiros. A operao abaixo faz com que o ponteiro p passe a apontar para o terceiro elemento aps o atual. p = p + 3; A diferena entre ponteiros fornece quantos elementos do tipo do ponteiro existem entre os dois ponteiros. No exemplo abaixo impresso o valor 3. void main(void) { float vetor[] = { 1.0, 2.0, 3.0, 4.0, 5.0 }; float *p1, *p2; p1 = &vetor[2]; /* endereco do terceiro elemento */ p2 = &vetor; /* endereco do primeiro elemento */ printf("Diferenca entre ponteiros %d\n", p1-p2); } No possvel multiplicar ou dividir ponteiros, e no se pode adicionar ou subtrair o tipo ponteiros.

x seja carregado no ponteiro p. Em seguida o programa

float

ou o tipo

double

Comparao de Ponteiros possvel comparar ponteiros em uma expresso relacional. S possvel comparar ponteiros de mesmo tipo. O trecho de programa abaixo ilustra um exemplo deste tipo de operaes. char *c, *v; scanf("%c %c", c, v); if (c == v) printf("As variveis estao na mesma posicao.\n"); else printf("As variaveis nao estao na mesma posicao.\n"); Ponteiros e Vetores Ponteiros e Vetores esto fortemente relacionados na linguagem C. O nome de um vetor um ponteiro que aponta para a primeira posio do vetor e todas as operaes j mencionadas para ponteiros podem ser executadas com um nome de vetor. Por exemplo, a declarao int v[100]; declara um vetor de inteiros de 100 posies e a partir dela temos que v um ponteiro equivalente ao da declarao abaixo int *v; Por esta razo as seguintes declaraes so idnticas e podem ser intercambiadas independemente do modo como v foi declarado: v[i] == *(v+i) &v[i] == v+i O exemplo ilustrado abaixo mostra as duas notaes sendo usadas para imprimir o mesmo vetor. void main(void) {

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 35 de 98

36 Tpicos em C reviso 257 de 24/03/2004 23:16 float v[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; int i; for (i=0; i<9; i++) printf("%.1f ", v[i]); printf("\n"); for (i=0; i<9; i++) printf("%.1f ", *(v+i)); } Existe uma diferena fundamental entre declarar um conjunto de dados como um vetor ou atravs de um ponteiro. Na declarao de vetor a compilador automaticamente reserva um bloco de memria para que o vetor seja armazenado. Quando apenas um ponteiro declarado a nica coisa que o compilador faz alocar um ponteiro para apontar para a memria, sem que espao seja reservado. O nome de um vetor chamado de ponteiro constante e, portanto, no pode ter o seu valor alterado. Assim, os comandos abaixo no so vlidos: void main(){ int list[5], i; /* O ponteiro list nao pode ser modificado recebendo o endereco de i */ list = &i /* O ponteiro list nao pode ser incrementado */ list++; } Para percorrer um vetor alm da maneira mostrada no exemplo anterior possvel usar um ponteiro varivel como ilustrado no exemplo abaixo. void main(void) { float v[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; int i; float *p; for (i=0; i<9; i++) printf("%.1f ", v[i]); printf("\n"); for (i=0; i<9; i++) printf("%.1f ", *(v+i)); printf("\n"); for (i=0, p=v; i<9; i++, p++) printf("%.1f ", *p); } Observe como o ponteiro p recebe seu valor inicial e a maneira que ele incrementado. Ponteiros e Strings Um string constante escrito como no exemplo: "Este e um string". At agora um dos usos mais comuns de strings constantes tem sido na funo printf, como no exemplo abaixo printf("Acabou o programa.\n"); Quando um string como este enviado para a funo o que passado o ponteiro para o string. possvel ento carregar o endereo do string em um ponteiro do tipo char. void main(void) { char *lista; lista = "Ola como vai?"; printf("%s", lista); } No exemplo aparece uma funo que conta o nmero de caracteres de um string. Observe o ponteiro para o string constante e na funo o ponteiro *(s+tam++) apontando caracter a caracter. int strtam(char *s); void main(void) { char *lista="1234567890"; printf("O tamanho do string \"%s\" e %d caracteres.\n", lista, strtam(lista)); printf("Acabou."); } int strtam(char *s){ int tam=0; while(*(s + tam++) != '\0'); return tam-1; } Um outro exemplo ilustrado abaixo mostra uma funo que copia um string para outro. int strcop(char *d, char *o); void main(void) { char destino[20]; char *origem="string de origem"; strcop(destino, origem); /* copia o string origem para o destino */ printf("%s\n", origem);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 36 de 98

37 Tpicos em C reviso 257 de 24/03/2004 23:16 printf("%s\n", destino); printf("Acabou.\n"); } int strcop(char *d, char *o){ while ((*d++ = *o++) != '\0'); return 1; } Alocao Dinmica de Memria As funes bsicas de alocao de memria so malloc(), calloc() e free(). Estas funes so encontradas na biblioteca

stdlib.h.

O exemplo abaixo ilustra o uso das funes calloc e free. #include <stdio.h> #include<alloc.h> #include <stdlib.h> void main(void){ float *v; int i, tam; printf("Qual o tamanho do vetor? "); scanf("%d", &tam); v = calloc(tam, sizeof(float)); if (v){ for (i=0; i<tam; i++){ printf("Elemento %d ?", i); scanf("%f", v+i); printf("Li valor %f \n", *(v+i)); } free(v); } else printf("Nao consegui alocar memoria."); }

Ponteiros e Matrizes Um ponteiro aponta para uma rea de memria que endereada de maneira linear. Deste modo, necessrio mapear o endereo de cada elemento na matriz, que dado por linha coluna, em um endereo linear. Considere uma matriz chamada matriz de tamanho LIN,COL que poderia ser declarada e ter um de seus elementos lidos da seguinte maneira: #define LIN 3 #define COL 4 void main(){ int matriz[LIN][COL]; for(i=0; i<LIN; i++){ for (j=0; j<COL; j++){ printf("Elemento %d %d = ", i, j); scanf("%d", matriz[i][j]); } } } Caso o programa utilizasse ponteiros ao invs de notao de matrizes o trecho de programa ficaria da seguinte maneira: #define LIN 3 #define COL 4 void main(void) { int *matriz; int i, j; matriz = malloc(LIN*COL*sizeof(int)); if (!matriz){ printf("Nao consegui alocar a memoria suficiente.\n"); exit(1); } for(i=0; i<LIN; i++){ for (j=0; j<COL; j++){ printf("Elemento %d %d = ", i, j); scanf("%d", matriz+(i*COL+j)); } } Observe que o endereo de cada elemento da matriz teve de ser calculado explicitamente. O programa completo que mostra como a matriz poderia ser impressa est indicada no exemplo. Uma outra possibilidade utilizar vetores de ponteiros, onde cada linha corresponde a um vetor que apontado por um ponteiro. No item seguinte (Vetores de ponteiros) detalhamos este exemplo.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 37 de 98

38 Tpicos em C reviso 257 de 24/03/2004 23:16 Vetores de Ponteiros um Como ponteiros tambm so variveis possvel ento criar vetores de ponteiros e utiliz-los. O programa exemplo mostra programa onde utilizado um vetor de ponteiros para linhas de caracteres. #define LINHAS 10 #define COLUNAS 60 /* prototipos das funcoes */ void alocaespaco(char *linha[]); void lelinhas (char *linha[]); void imprimelinhas (char *linha[]); void ordenalinhas (char *linha[]); void main(void) { char *linha[LINHAS]; alocaespaco(linha); lelinhas(linha); imprimelinhas(linha); ordenalinhas(linha); imprimelinhas(linha); } /* Esta rotina aloca espaco para as linhas */ void alocaespaco(char *linha[]){ int i; for (i=0; i<LINHAS; i++){ if (!(linha[i] = malloc(COLUNAS*sizeof(int)))){ printf("Nao consegui alocar o vetor %d.\n", i); exit(i); } } } /* Esta rotina le linhas */ void lelinhas (char *linha[]){ int i; for (i=0; i<LINHAS; i++){ printf("Entre com a linha %d.\n", i); gets(linha[i]); } } /* Esta rotina imprime as linhas */ void imprimelinhas (char *linha[]){ int i; for (i=0; i<LINHAS; i++){ printf("Linha %d %s.\n", i, linha[i]); } } void ordenalinhas( char *linha[]){ char trocou; char *p; int i; do { trocou = 0; for (i=0; i<LINHAS-1; i++){ if (strcmp(linha[i], linha[i+1]) >0){ p = linha[i]; linha[i] = linha[i+1]; linha[i+1] = p; trocou = 1; } } } while (trocou); } Observe na rotina main a declarao char *linha[LINHAS]; que define um vetor de tamanho LINHAS. Este vetor contem ponteiros e no valores. At este momento temos apenas posies reservadas para armazenar ponteiros. A alocao de espao e a inicializao dos ponteiros feita na rotina

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 38 de 98

39 Tpicos em C reviso 257 de 24/03/2004 23:16

alocaespaco. Como cada elemento do vetor um ponteiro o ponteiro retornado pela funo malloc armazenado em cada um dos elementos do vetor. Portanto linha[i] um ponteiro pode ser usado como parmetro das funes gets e printf. Um ponto importante da ordenao das linhas que a troca de posio das linhas involve somente a troca dos ponteiros das linhas a serem trocadas e no o seu contedo, o que muito mais rpido.
Ponteiros para Ponteiros No exemplo anterior podemos observar que o nmero de linhas da matriz fixa, e portanto, h uma mistura de notao de ponteiros com matrizes. Vamos considerar um exemplo onde tanto o nmero de linhas como o de colunas desconhecido. Neste exemplo iremos criar um vetor de ponteiros que ir armazenar o endereo inicial de cada linha. Portanto, para obter um elemento da matriz primeiro devemos descobrir onde est a linha no vetor que armazena os endereos das linhas, em seguida procuramos na linha o elemento. A figura ilustra como ser feito o armazenamento desta matriz.
Vetor de Ponteiros **matriz *(matriz + 0) *(matriz +1) *(matriz + 2) *(matriz + 3) ... *(matriz + n) ... Linha n Linha 3 Linha 2

Linha 0 Linha 1

O programa exemplo, listado a seguir, ir pedir ao usurio que digite o nmero de linhas e colunas da matriz. Em seguida ler todos os elementos da matriz e por ltimo ir trocar duas linhas da matriz de posio. Observe que agora foi criado um ponteiro para ponteiro chamado de **matriz. O programa primeiro pergunta o nmero de linhas da matriz para poder alocar espao para armazenar os ponteiros para cada uma das linhas. Em seguida alocado espao para armazenar cada uma das linhas. Notar que a troca de linhas da matriz involve simplesmente a troca de dois ponteiros e no a troca de todos os elementos das linhas. Esta soluo muito mais rpida do que a anterior, especialmente para matrizes grandes. #include<stdio.h> #include<stdlib.h> void main () { int **matriz; /* ponteiro para os ponteiros de cada uma das linhas */ int lin, col; /* nmero de linhas e colunas */ int i, j; int linha1, linha2; /* linhas da matriz que serao trocadas */ char linha[80]; /* linha de caracteres com os dados */ int *temp; do { puts("Qual o numero de linhas?"); gets(linha); lin = atoi(linha); } while (lin<=0); printf("Numero de linhas = %d\n", lin); printf("Alocando espao para armazenar os ponteiros para as linhas.\n"); matriz = (int **) malloc (lin * sizeof(int *)); if (!matriz) { puts("Nao h espao para alocar memria"); exit(1); } do { puts("Qual o numero de colunas?"); gets(linha); col = atoi(linha); } while (col<=0); printf("Numero de colunas = %d\n", lin); printf("Alocando espao para armazenar os vetores de linhas.\n"); for (i=0; i<lin; i++) { *(matriz +i) = (int *) malloc(col * sizeof (int)); if (! *(matriz+i) ){ printf("No h espao para alocar a linha %d", i); exit(1); } } puts("Entre com os dados"); for (i=0; i<lin; i++) { printf("Entre com a linha %d\n", i); for (j=0; j<col; j++) {

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 39 de 98

40 Tpicos em C reviso 257 de 24/03/2004 23:16 printf("Elemento %d %d\n", i, j); scanf("%d", *(matriz +i) +j); } } puts("Dados lidos"); for (i=0; i<lin; i++) { for (j=0; j<col; j++) { printf("%7d ", *(*(matriz +i) +j)); } printf("\n"); } getchar(); do { puts("Qual a primeira linha a ser trocada?"); gets(linha); linha1=atoi(linha); } while (linha1 >=lin); do { puts("Qual a segunda linha a ser trocada?"); gets(linha); linha2=atoi(linha); } while (linha2 >=lin); temp = *(matriz + linha1); *(matriz + linha1) = *(matriz + linha2); *(matriz + linha2) = temp; puts("Dados trocados."); for (i=0; i<lin; i++) { for (j=0; j<col; j++) { printf("%7d ", *(*(matriz +i) +j)); } printf("\n"); } } A seguir mostramos o exemplo anterior modificado para utilizar funes. O propsito mostrar como ficam as chamadas e as definies das funes que utilizam ponteiros para ponteiros. #include<stdio.h> #include<stdlib.h> int **aloca_linhas(int ); void aloca_colunas (int **, int, int); void le_dados(int **, int, int ); void imprime_matriz(int **, int, int ); void troca_linhas (int **, int, int); void main () { int **matriz; /* ponteiro para os ponteiros de cada uma das linhas */ int lin, col; /* nmero de linhas e colunas */ int linha1, linha2; /* linhas da matriz que serao trocadas */ char linha[80]; /* linha de caracteres com os dados */ do { puts("Qual o numero de linhas?"); gets(linha); lin = atoi(linha); } while (lin<=0); printf("Numero de linhas = %d\n", lin); printf("Alocando espao para armazenar os ponteiros para as linhas.\n"); matriz = aloca_linhas(lin); do { puts("Qual o numero de colunas?"); gets(linha); col = atoi(linha); } while (col<=0); printf("Numero de colunas = %d\n", lin); printf("Alocando espao para armazenar os vetores de linhas.\n"); aloca_colunas(matriz, lin, col); le_dados(matriz, lin, col); puts("Imprimindo dados lidos"); imprime_matriz(matriz, lin, col); getchar(); do { puts("Qual a primeira linha a ser trocada?"); gets(linha); linha1=atoi(linha); } while (linha1 >=lin); do { puts("Qual a segunda linha a ser trocada?");

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 40 de 98

41 Tpicos em C reviso 257 de 24/03/2004 23:16 gets(linha); linha2=atoi(linha); } while (linha2 >=lin); troca_linhas(matriz, linha1, linha2); puts("Imprimindo dados trocados."); imprime_matriz(matriz, lin, col); } int **aloca_linhas(int lin) { int **m; m = (int **) malloc (lin * sizeof(int *)); if (!m) { puts("Nao h espao para alocar memria"); exit(1); } return m; } void aloca_colunas(int **matriz, int lin, int col) { int i; for (i=0; i<lin; i++) { *(matriz +i) = (int *) malloc(col * sizeof (int)); if (! *(matriz+i) ){ printf("No h espao para alocar a linha %d", i); exit(1); } } } void le_dados (int **matriz, int lin, int col) { int i, j; puts("Entre com os dados"); for (i=0; i<lin; i++) { printf("Entre com a linha %d\n", i); for (j=0; j<col; j++) { printf("Elemento %d %d\n", i, j); scanf("%d", *(matriz +i) +j); } } } void imprime_matriz (int **matriz, int lin, int col) { int i, j; for (i=0; i<lin; i++) { for (j=0; j<col; j++) { printf("%7d ", *(*(matriz +i) +j)); } printf("\n"); } } void troca_linhas ( int **matriz, int linha1, int linha2) { int *temp; temp = *(matriz + linha1); *(matriz + linha1) = *(matriz + linha2); *(matriz + linha2) = temp; }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 41 de 98

42 Tpicos em C reviso 257 de 24/03/2004 23:16

Estruturas
Introduo Uma estrutura um conjunto de uma ou mais variveis, que podem ser de tipos diferentes, agrupadas sob um nico nome. O fato de variveis agrupadas em uma estrutura poderem ser referenciadas por um nico nome facilita a manipulao dos dados armazenados nestas estruturas. Um exemplo poderia ser uma estrutura que armazenasse as diversas informaes sobre os alunos de uma Universidade. Nesta estrutura estariam armazenadas, sob o mesmo nome, informaes do tipo: nome, registro, data de nascimento, data de ingresso, CPF, etc. Uma estrutura pode incluir outras estruturas alm de variveis simples. As estruturas facilitam manipular estes agrupamentos complexos de dados. Por exemplo, considere o problema de ordenar as informaes sobre os alunos da Universidade exemplo. A ordenao pode ser efetuada como se todos os dados que compem a estrutura fossem uma entidade nica. Definies Bsicas Uma estrutura, ento, uma coleo de variveis, de tipos diversos ou no, agrupadas sob um nico nome. As variveis que compem a estrutura so os seus membros, elementos ou campos. Normalmente os elementos da estrutura tem alguma relao semntica. Por exemplo: alunos de uma universidade, discos de uma coleo, elementos de uma figura geomtrica, etc. Vamos considerar o exemplo do aluno e assumir que estaremos armazenando o seu nome, registro, ano de entrada e curso. Para este fim podemos criar uma estrutura como a descrita abaixo: struct aluno { char nome[40]; int registro; int ano_entrada; char curso[20]; }; A palavra chave struct inicia a declarao da estrutura, em seguida pode aparecer um identificador (aluno), que subsequentemente pode ser usado como abreviao da definio da estrutura. A declarao continua com a lista de declaraes entre chaves e termina com um ;. Um membro da estrutura e uma vriavel no membro da estrutura podem ter o mesmo nome, j que possvel distingui-las por contexto. A declarao acima ainda no alocou espao de memria j que nenhuma varivel foi realmente definida. Esta declarao apenas um modelo de como estruturas do tipo aluno devem ser construdas. Para definir estruturas do tipo aluno podemos usar a seguinte declarao. Nesta declarao trs estruturas do tipo aluno foram criadas. Esta declarao alocou espao para armazenar os dados dos trs alunos. A declarao acima idntica, na forma, a declarao de variveis de um tipo pr-definido, como por exemplo: possvel declarar ao mesmo tempo o modelo da estrutura e as variveis do programa. Por exemplo, struct aluno { char nome[40]; int registro; int ano_entrada; char curso[20]; } paulo, carlos, ana; Para referenciar um elemento da estrutura usa-se o nome da varivel do tipo da estrutura seguida de um ponto e do nome do elemento. Por exemplo, paulo.ano_entrada = 1999; armazena o ano em que aluno paulo entrou na universidade. Para ler o nome do curso que paulo cursa pode-se usar o comando gets(paulo.curso); Estruturas podem conter outras estruturas como membros. Por exemplo, vamos definir uma estrutura para armazenar uma data com a seguinte definio: struct data { int dia, mes, ano; } Agora vamos modificar a estrutura aluno de modo que ela inclua a data de nascimento do aluno. A estrutura fica com a seguinte definio: struct aluno { char nome[40]; int registro; int ano_entrada; char curso[20]; struct data data_nascimento; }; Para se referir ao ms de nascimento de uma varivel paulo do tipo estrutura aluno usamos a declarao paulo.data_nascimento.mes o operador . associa da esquerda para a direita. Atribuio de Estruturas possvel atribuir o contedo de uma estrutura a outra estrutura do mesmo tipo, no sendo necessrio atribuir elemento por elemento da estrutura. O programa a seguir ilustra como podemos atribuir uma estrutura a outra.

struct aluno paulo, carlos, ana; int a, b, c;

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 42 de 98

43 Tpicos em C reviso 257 de 24/03/2004 23:16 #include<stdio.h> void main () { struct empregado { char nome[40]; float salario; } temp, emp1; puts("Entre com nome."); gets(emp1.nome); puts("Qual o salario?"); scanf("%f", &emp1.salario); temp=emp1; printf("O salario de %s e %.2f\n", temp.nome, temp.salario); } Matrizes de Estruturas Estruturas aparecem freqentemente na forma de matrizes. A declarao abaixo define uma matriz de 100 estruturas do tipo aluno.

struct aluno turma[100];

Para se imprimir ao nome do terceiro aluno (incio em 0) usamos o comando

O exemplo abaixo mostra atribuies entre estruturas e operaes aritmticas envolvendo membros de estruturas. O programa coloca um vetor de estruturas em ordem crescente usando como chave de ordenao um dos membros. #define MAX 4 #include <stdio.h> #include <string.h> void main () { struct aluno{ char nome[40]; float n1, n2, media; } turma[MAX], turma2[MAX]; int i, j, pos; puts("Lendo dados da turma"); for (i=0; i<MAX; i++) { printf("Dados do aluno %d\n", i); puts("Nome?"); gets(turma[i].nome); puts("Primeira nota?"); scanf("%f", &turma[i].n1); puts("Segunda nota?"); scanf("%f", &turma[i].n2); getchar(); turma[i].media=(turma[i].n1+turma[i].n2)/2.0; } puts("Imprimindo dados lidos da turma."); puts("Digite qualquer coisa para continuar."); getchar(); for (i=0; i<MAX; i++) { printf("\nDados do aluno %d\n", i); printf("Nome: %s\n",turma[i].nome); printf("Primeira nota: %.1f\n", turma[i].n1); printf("Segunda nota: %.1f\n", turma[i].n2); printf("Media: %.1f\n", turma[i].media); } for (i=0; i<MAX; i++) { pos = 0; for (j=0; j<MAX; j++) if (turma[i].media > turma[j].media) pos++; turma2[pos] = turma[i]; } for (i=0; i<MAX; i++) turma[i]=turma2[i]; puts("Imprimindo dados ordenados da turma."); puts("Digite qualquer coisa para continuar."); getchar(); for (i=0; i<MAX; i++) { printf("\nDados do aluno %d\n", i); printf("Nome: %s\n",turma[i].nome); printf("Primeira nota: %.1f\n", turma[i].n1); printf("Segunda nota: %.1f\n", turma[i].n2); printf("Media: %.1f\n", turma[i].media); } getchar(); } Passando Membros e Estruturas para Funes Primeiro vamos considerar o caso de passar elementos da estrutura para funes. Caso os elementos da estrutura sejam variveis de um dos tipos j vistos, a passagem efetuada da maneira normal. Por exemplo, considere a estrutura struct ponto { float x, y; } p1, p2; Para passar a coordenda x do ponto p1 para uma funo chamada positivo poderamos a seguinte declarao:

printf("%s\n", turma[2].nome);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 43 de 98

44 Tpicos em C reviso 257 de 24/03/2004 23:16 if ( positivo(p1.x) == 0 ) puts("Eixo y); else if ( positivo(p1.x>0 ) puts("Eixo positivo dos x"); else puts("Eixo negativo dos x"); A funo que recebe este parmetro est preparada para receber uma varivel de ponto flutuante simples. Caso seja necessrio passar o endereo de um dos membros ou elementos da estrutura basta colocar o operador & antes do nome da estrutura. Por exemplo, para trocar os valores das coordenadas x dos pontos p1 e p2 usaramos chamadas da seguinte forma. troca_x (&p1.x, &p2.x); Para trabalhar com endereos necessrio usar ponteiros dentro da funo troca_x, mas isto veremos no prximo item. Antes vamos verificar como possvel passar uma estrutura inteira para uma funo. Estruturas, quando passadas para funes, se comportam da mesma maneira que as variveis dos tipos que j estudamos. Ao passar uma estrutura para uma funo estaremos passando os valores armazenados nos membros da estrutura. Como este tipo de passagem feito por valor, alteraes nos membros da estrutura no modificam os valores da estrutura na funo que chamou. A passagem de estruturas para funes ilustrada no exemplo abaixo onde o comprimento da reta que liga dois pontos P1 e P2 calculado e impresso. #include<stdio.h> #include<math.h> struct ponto { float x, y; } P1, P2; float comp (struct ponto, struct ponto); void main () { puts ("Coordenadas do ponto 1"); printf("x1 = ? "); scanf("%f", &P1.x); printf("y1 = ? "); scanf("%f", &P1.y); puts ("Coordenadas do ponto 2"); printf("x1 = ? "); scanf("%f", &P2.x); printf("y1 = ? "); scanf("%f", &P2.y); printf("\nComprimento da reta = %.2f", comp(P1, P2)); } float comp(struct ponto p1, struct ponto p2){ return sqrt (pow(p2.x-p1.x,2)+pow(p2.y-p1.y,2)); } O exemplo mostra um programa que implementa um banco de dados simples sobre uma turma de alunos. Neste exemplo mostramos a passagem de matrizes de estruturas para funes. #include <stdio.h> #include <string.h> #include <conio.h> #include <ctype.h> #define MAXNOME 40 #define MAXALUNOS 4 #define VERDADE 1 #define FALSO 0 struct RegAluno { char nome[MAXNOME]; float n1, n2, m1; }; void void void void char void { insere(struct RegAluno turma[]); remover(struct RegAluno turma[]); lista (struct RegAluno turma[]); inicia(struct RegAluno turma[]); escolhe(); main(void) struct RegAluno turma[MAXALUNOS]; char c, continua; clrscr(); inicia(turma); do { continua = VERDADE; c = escolhe(); switch (c) { case 'i': insere(turma); break; case 'r': remover(turma); break; case 'l':

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 44 de 98

45 Tpicos em C reviso 257 de 24/03/2004 23:16 lista(turma); break; default: continua = !continua; }; } while (continua); printf("Acabou.\n"); } char escolhe(){ char c; printf("\nEscolha uma operacao.\n"); printf("[I]nserir um aluno.\n"); printf("[R]emover um aluno.\n"); printf("[L]istar a turma.\n"); printf("Qualquer outra tecla para terminar.\n"); c = tolower(getch()); return c; } void insere (struct RegAluno turma[]){ int i=0; char achou=FALSO; while (i<MAXALUNOS && !achou){ if (*turma[i].nome == ' '){ achou = !achou; printf("\nForneca os dados sobre o aluno.\n"); fflush(stdin); printf("Nome. "); gets(turma[i].nome); printf("Nota 1. "); scanf("%f", &turma[i].n1); printf("Nota 2. "); scanf("%f", &(turma[i].n2)); turma[i].m1 = (turma[i].n1+turma[i].n2)/2.0; } i++; } if (!achou) printf("\nDesculpe, nao ha espaco para inserir um novo aluno.\n"); } void remover (struct RegAluno turma[]){ int i=0; char nome[MAXNOME]; char achou = FALSO; fflush(stdin); printf("Qual o nome a remover?"); gets(nome); while (i<MAXALUNOS && !achou){ if (!strcmp(turma[i].nome, nome)){ achou = !achou; *turma[i].nome = ' '; printf("\nAluno %s removido.\n", nome); } i++; } if (!achou) printf("\nDesculpe, mas nao ha este aluno.\n"); } void lista (struct RegAluno turma[]){ int i, aluno=0; for (i=0; i<MAXALUNOS; i++){ if (*turma[i].nome != ' '){ printf("\nDados printf("Nome. "); printf("Nota 1. "); printf("Nota 2. "); printf("Media. } }; } void inicia (struct RegAluno turma[]){ int i;

sobre o aluno %d.\n", aluno++); printf("%s\n", turma[i].nome); printf("%.1f\n", turma[i].n1); printf("%.1f\n", turma[i].n2); "); printf("%.1f\n", turma[i].m1);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 45 de 98

46 Tpicos em C reviso 257 de 24/03/2004 23:16 for (i=0; i<MAXALUNOS; i++) *turma[i].nome=' '; } Ponteiros para Estruturas Para definir ponteiros para estruturas a declarao similar a declarao de um ponteiro normal. O exemplo abaixo mostra a definio de um ponteiro chamado maria para uma estrutura chamada aluno. struct aluno { char nome[40]; int ano_entrada; float n1, n2, media; } *maria; Ponteiros so uteis quando passamos estruturas para funes. Ao passar apenas o ponteiro para estrutura economizamos tempo e memria. O espao de memria, economizado por que se evita passar os dados que compem a estrutura um por um. O tempo economizado porque no necessrio gastar o tempo de empilhar e desempilhar todos os elementos da estrutura no processo de passagem para a funo. Empilhar e desempilhar se referem a pilha de dados usada para transferir os dados entre funes. Para acessar elementos da estrutura apontada por um ponteiro usa-se o chamado operador seta (->). Por exemplo para imprimir a mdia da aluna maria usaramos o comando printf ("A media vale %.1f", maria->media); Para alocar espao para estruturas apontadas por ponteiros necessrio usar o operador unrio sizeof, isto porque o tamanho de uma estrutura sempre igual ou maior que a soma dos tamanhos dos seu componentes. Para explicar esta fato devemos considerar como os dados so armazenados na memria dos computadores. Algumas arquiteturas de computadores endeream os dados na mmoria por bytes, isto cada endereo de memria se refere a um byte. No entanto, estas arquiteturas lem sempre uma palavra inteira da memria. Usualmente, palavras podem ser compostas de dois bytes e comeam em endereos pares, como est mostrado na figura abaixo. Sabemos que existem variveis que ocupam mais de um byte, por exemplo inteiros que so compostos de dois bytes. Imagine ento uma estrutura composta de um caracter (1 byte) e um nmero de inteiro (2 bytes). Caso a memria do computador seja organizada em palavras de 16 bits ou 2 bytes a estrutura acima ocuparia 3 bytes ou uma palavra e meia. Para ler o nmero inteiro o programa deveria ler duas palavras. Lembrar que se os dados fossem sempre armazenados sequencialmente, metade do nmero inteiro estaria em uma palavra e a metade restante na outra, como est indicado na figura abaixo (parte a). Para facilitar o acesso s variveis, alguns compiladores armazenam as variveis de acordo com o que est indicado na figura (parte b). Observar que agora a estrutura ocupa quatro bytes. Neste caso o acesso ao nmero inteiro ser sempre feito em um passo e portanto ganhou-se em tempo de acesso ao custo de gasto de memria. Este uma troca constante em computao.

Endereo 1000 1002 1004 1006 1008 1010 (a) Endereo 1000 1002 1004 1006 1008 1010

Byte 0 Char Int (segunda metade)

Byte 1 Int (primeira metade)

Armazenamento de estruturas economizando espao Byte 0 Char Int (primeira metade) Byte 1 vazio Int (segunda metade)

(b) Armazenamento de estruturas economizando espao Armazenamento de estruturas Vimos ento que embora o total de bytes dos elementos da estrutura fosse trs o compilador pode armazenar a estrutura em quatro bytes, da a necessidade de sempre usar o operador sizeof quando alocar espao. O programa abaixo mostra como utilizar ponteiros para estruturas e a forma mais segura de alocar espao para os dados. #include<stdio.h> #include<stdlib.h> #define TAMNOME 40

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 46 de 98

47 Tpicos em C reviso 257 de 24/03/2004 23:16 typedef struct _func { char nome[TAMNOME]; float salario; float imposto; } Tfunc ; void le (Tfunc *, int ); void imprime (Tfunc *, int ); float imposto (Tfunc *, int); int main () { Tfunc *cadastro; int funcionarios; puts("Quantos funcionarios tem a empresa?"); scanf ("%d", &funcionarios); cadastro = (Tfunc *) malloc(funcionarios * sizeof (Tfunc)); if (!cadastro) { puts("Nao ha espaco para alocar memoria."); exit (1); } le(cadastro, funcionarios); printf("O imposto total a ser recolhido vale %.2f\n", imposto(cadastro, funcionarios)); imprime(cadastro, funcionarios); exit(0); } void le (Tfunc *cadastro, int funcionarios) { int i; puts("Lendo dados dos funcionarios."); for (i=0; i<funcionarios; i++) { printf ("Dados dos funcionario %d\n", i); puts ("Nome ?"); getchar(); fgets((cadastro+i)->nome, TAMNOME, stdin); puts ("Salario ?"); scanf("%f", &((cadastro+i)->salario)); } } void imprime (Tfunc *cadastro, int funcionarios) { int i; puts("Imprimindo dados dos funcionarios."); for (i=0; i<funcionarios; i++) { printf ("\nDados dos funcionario %d\n", i); printf("Nome = %s\n", (cadastro+i)->nome); printf("Salario = %.2f\n", (cadastro+i)->salario); printf("Imposto = %.2f\n", (cadastro+i)->imposto); } } float imposto(Tfunc *cadastro, int funcionarios) { float impostoTotal=0.0; int i; for (i=0; i<funcionarios; i++) { if (cadastro[i].salario > 1000.00) cadastro[i].imposto = cadastro[i].salario * 0.25; else cadastro[i].imposto = cadastro[i].salario * 0.10; impostoTotal += cadastro[i].imposto; } return impostoTotal; } Observar que neste exemplo usamos as duas notaes possveis para representar elementos de uma estrutura. Nas funes le e imprime usamos a notao de ponteiros e na funo imposto usamos o fato de que aps a alocao de espao para o vetor de estruturas podemos usar a notao de vetores. Reproduzimos abaixo as funes com as duas notaes para ficar mais claro o que estamos procurando destacar. Funo le (notao ponteiros) gets((cadastro+i)->nome);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 47 de 98

48 Tpicos em C reviso 257 de 24/03/2004 23:16 scanf("%f", &((cadastro+i)->salario)); Funo

imprime (notao ponteiros) printf("Nome = %s\n", (cadastro+i)->nome); printf("Salario = %.2f\n", (cadastro+i)->salario); imposto (notao vetorial) if (cadastro[i].salario > 1000.00) func += cadastro[i].salario * 0.25; else func += cadastro[i].salario * 0.10;

Funo

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 48 de 98

49 Tpicos em C reviso 257 de 24/03/2004 23:16

Entrada e Sada de Dados por Arquivos


Introduo Em C no existem comandos de Entrada e Sada, sendo estas tarefas executadas por funes especialmente criadas para esta finalidade e armazenadas em bibliotecas especficas. Fluxos de Dados Para isolar os programadores dos problemas de manipular os vrios tipos de dispositivos de armazenamento e seus diferentes formatos a linguagem C utiliza o conceito de fluxo (stream) de dados. Todos os diferentes sistemas de arquivos se comportam da mesma maneira quando manipulados como um fluxo contnuo de dados. Dados podem ser manipulados em dois diferentes tipos de fluxos: fluxos de texto e fluxos binrios. Fluxos de Texto Um fluxo de texto composto por uma seqncia de caracteres, que pode ou no ser dividida em linhas terminadas por um caracter de final de linha. Um detalhe que deve ser considerado que na ltima linha no obrigatrio o caracter de fim de linha. Nem sempre a traduo entre a representao do caracter no fluxo de texto e no sistema de arquivos do computador hospedeiro um para um. Por exemplo, entre UNIX e DOS h uma diferena na representao de final de linha que causa problemas na impresso de arquivos. Em UNIX um final de linha representado pelo caracter de alimentao de linha. Em DOS um final de linha representado pelo par retorno de carro/alimentao de linha. Deste modo quando um arquivo gerado em UNIX vai para uma impressora que espera final de linha no modo DOS surge o que comumente chamado de efeito escada. A impresso continua na linha seguinte mas sem voltar para o incio da linha porque em UNIX o caracter de retorno de carro no inserido no fluxo de texto. At agora temos trabalhado com os fluxos de dados padro: stdin, para entrada de dados e stdout para sada de dados. Fluxo Binrio Um fluxo binrio composto por uma seqncia de bytes lidos, sem traduo, diretamente do dispositivo externo. No ocorre nenhuma traduo e existe uma correspondncia um para um entre os dados do dispositivo e os que esto no fluxo. Arquivos Arquivo pode ser qualquer dispositivo de Entrada e Sada como por exemplo: impressora, teclado, disquete, disco rgido, etc. Programas vem os arquivos atravs de fluxos. Para que um determinado arquivo seja associado a um determinado fluxo necessrio que o arquivo seja aberto. Aps esta operao o programa pode utilizar os dados armazenados no arquivo. Operaes comuns em arquivos so: abertura e fechamento de arquivos; apaga um arquivo; leitura e escrita de um caracter; indicao de que o fim do arquivo foi atingido; posicionar o arquivo em um ponto determinado Obviamente algumas dessas funes no se aplicam a todos os tipos de dispositivos. Por exemplo, para uma impressora no possvel usar a funo que reposiciona o arquivo no nicio. Um arquivo em disco permite acesso aleatrio enquanto um teclado no. Ao final das operaes necessrias o programa deve fechar o arquivo. Ao final da execuo de um programa todos os arquivos associados so fechados automaticamente e os contedos dos buffers so descarregados para o dispositivo externo. Caso o arquivo seja de entrada o contedo do buffer esvaziado. Funes de Entrada e Sada As funes de Entrada e Sada normalmente utilizadas pelos programadores esto armazenadas na biblioteca As funes mais comuns esto mostradas na tabela a seguir. Funo Descrio Abre um arquivo Escreve um caracter em um arquivo L um caracter de um arquivo Equivalente a printf(), usando stream Equivalente a scanf(), usando string Equivalente a scanf(), usando stream

stdio.h.

Fopen() Putc(),fputc() getc(), fgetc() fprintf() sscanf() fscanf()

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 49 de 98

50 Tpicos em C reviso 257 de 24/03/2004 23:16

fseek() rewind() feof() ferror() fflush() fread() Fwrite()

Posiciona o arquivo em um ponto especfico Posiciona o arquivo no incio Retorna VERDADE se chegou ao fim do arquivo Verifica a ocorrncia de erro Descarrega o buffer associado com o arquivo Leitura de dados no modo binrio Escrita de dados no modo binrio

Tabela - Funes de Entrada e Sada Para ter acesso aos dados em um arquivo necessrio a definio de um ponteiro do tipo especial FILE. Este tipo tambm est definido na biblioteca stdio.h. Um ponteiro deste tipo permite que o programa tenha acesso a uma estrutura que armazena informaes importantes sobre o arquivo. Para definir uma varivel deste tipo o programa deve conter a seguinte declarao FILE *arq; onde arq o ponteiro que ser usado para executar as operaes no arquivo. Incio e Fim As operaes mostradas a seguir mostram operaes que devem ser realizadas antes e depois de usar um arquivo (fopen() e fclose()). As outras duas funes server para que o usurio possa detectar o fim de um arquivo ou voltar para seu incio. Abrindo um Arquivo Antes de qualquer operao ser executada com o arquivo, ele deve ser aberto. Esta operao associa um fluxo de dados a um arquivo. Um arquivo pode ser aberto de diversas maneiras: leitura, escrita, leitura/escrita, adico de texto, etc. A funo utilizada para abrir o arquivo chamada fopen() e tem o seguinte prottipo: FILE *fopen (const char *parq, const char *modo) onde parq um ponteiro de arquivo para o arquivo a ser manipulado e modo um ponteiro para uma cadeia de caracteres que define a maneira como o arquivo vai ser aberto. Este ponteiro no deve ser modificado e a funo retorna um ponteiro nulo (NULL) se o arquivo no puder ser aberto. A tabela abaixo lista os diversos modos que podem ser usados para abrir um arquivo. Modo Descrio Abre um arquivo texto para leitura Cria um arquivo texto para escrita Adiciona texto ao fim de um arquivo texto

r w a rb wb ab r+ w+ a+ r+b r+b a+b

Abre um arquivo binrio para leitura Abre um arquivo binrio para escrita Anexa a um arquivo binrio Abre um arquivo texto para leitura/escrita Cria um arquivo texto para leitura/escrita Cria ou anexa a um arquivo texto para leitura/escrita Abre um arquivo binrio para leitura/escrita Cria um arquivo binrio para leitura/escrita Anexa a um arquivo binrio para leitura/escrita

Tabela - Modos de abertura de arquivos Observar que se um arquivo for aberto com permisso de escrita todo o sue contedo anterior ser apagado. Caso o arquivo no exista ele ser criado. A maneira mais comum de abrir um arquivo est ilustrado no exemplo abaixo: FILE *pa; if (( pa = fopen ("arquivo.txt", "w")) == NULL ) { printf("Desculpe, o arquivo nao pode ser aberto."); exit(1); } Abrir um arquivo que j existe para escrita, implica em apagar todo o contedo anterior do arquivo e a preparao do arquivo para receber dados a partir de seu ponto inicial. Se o programador deseja acrescentar dados ao final de um arquivo j existente o modo de abertura deve ser a. Fechando um Arquivo Um arquivo aberto por meio da funo fopen() deve ser fechado com a funo fclose() cujo prottipo int fclose (FILE *parq); onde parq um ponteiro de arquivo para o arquivo que deve ser fechado. Um valor zero de retorno significa que a operao foi executada com xito, qualquer outro valor implica em erro.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 50 de 98

51 Tpicos em C reviso 257 de 24/03/2004 23:16 A operao fclose() implica em escrita no arquivo de qualquer dado que ainda no tenha sido escrito. Este ponto importante de ser considerado porque em UNIX uma operao de escrita em um arquivo no ocorre imediatamente a emisso da ordem de escrita. O sistema operacional pode executar a ordem no momento que achar mais conveniente. Fim de Arquivo A funo feof() indica que um arquivo chegou ao seu final. A pergunta que pode surgir a seguinte - Se j existe o valor EOF para indicar o final de arquivo por que precisamos de uma funo extra do tipo feof()? O problema que EOF um valor inteiro e ao ler arquivos binrios este valor pode ser lido como parte do arquivo e no por ser o final do arquivo. A funo feof() serve para indicar que o final de um arquivo binrio foi encontrado. Naturalmente esta funo pode ser aplicada tambm a arquivos texto. O prottipo da funo o seguinte: int feof(FILE *parq) Volta ao Incio A funo rewind() recoloca o indicador de posio de arquivo no inicio do arquivo. Uma operao semelhante ao que fazemos em uma fita cassete de msica ou vdeo. O prottipo da funo o seguinte: void rewind(FILE *parq) importante observar que o arquivo deve estar aberto em um modo que permita a execuo das operaes desejadas. Por exemplo, um arquivo aberto somente para escrita e em seguida rebobinado para o incio, no ir permitir outra operao que no seja escrita. Lendo e Escrevendo Caracteres As operaes mais simples em arquivos so a leitura e escrita de caracteres. Para ler um caracter de um arquivo que foi previamente aberto pode se usar as funes getc() e fgetc(), que so equivalentes. O prottipo de fgetc : int fgetc (FILE *parq); As funes getc() e fgetc() so equivalentes e muitos compiladores implementam getc() como uma macro do seguinte modo: #define getc(parq) fgetc(parq) A funo l o caracter como um unsigned char mas retorna o valor como um inteiro, onde o byte mais significativo vale zero. A funo devolve o cdigo EOF ao chegar ao final do arquivo. O valor EOF tambm um inteiro vlido e portanto ao usar arquivos binrios necessrio que a funo feof() seja utilizada para verificar o final do arquivo. Para escrever caracteres h duas funes definidas putc() e fputc(). O prottipo da funo fputc o seguinte: int fputc(int ch, FILE *parq) onde parq um ponteiro de arquivo para o arquivo que foi previamente aberto por meio da funo fopen() e ch e o caracter a ser escrito. O programa mostrado abaixo ilustra um exemplo onde um arquivo criado para leitura e escrita. Em seguida um conjunto de caracteres lido do teclado escrito no arquivo. Neste exemplo a leitura termina quando o usurio digita o caracter <ctl>D, que indica final de arquivo pelo teclado em Unix (naquele outro sistema acho que <ctl-Z>). O prximo passo a leitura do arquivo que iniciada aps uma chamada a funo rewind(), fazendo com que o indicador de posio do arquivo volte a apontar para seu incio. /* Programa: Descricao: Escreve e em seguida le caracteres de um arquivo. */ #include <stdio.h> #include void main (void ) { int c; FILE *pa; char *nome = "texto.txt"; /* Abre o arquivo para leitura e escrita */ if (( pa = fopen(nome, "w+")) == NULL) { printf("\n\nNao foi possivel abrir o arquivo.\n"); exit(1); } /* Cada caracter digitado ser gravado no arquivo */ c = getchar(); while (!feof(stdin)) { fputc(c, pa); c = getchar(); } rewind(pa); /* volta ao inicio do arquivo */ printf("\nTerminei de escrever, agora vou ler.\n"); c = fgetc(pa); while (!feof(pa)) { putchar(c); c = fgetc(pa); } fclose(pa); getchar(); /* Espera o usuario digitar alguma coisa */ }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 51 de 98

52 Tpicos em C reviso 257 de 24/03/2004 23:16 Uma outra alternativa mostrada em mostra um exemplo onde o arquivo criado para escrita em seguida fechado e reaberto para leitura ficando automaticamente posicionado no incio para a leitura. /* Programa: c10lec.c Descricao: Escreve e em seguida le caracteres de um arquivo. Nao usa rewind. */ #include <stdio.h> #include <conio.h> void main (void ) { int c; FILE *pa; char *nome = "texto.txt"; if (( pa = fopen(nome, "w+")) == NULL) { printf("\n\nNao foi possivel abrir o arquivo para escrita.\n"); exit(1); } /* Cada caracter digitado ser gravado no arquivo */ c = getchar(); while (!feof(stdin)) { fputc(c, pa); c = getchar(); } fclose(pa); printf("\nTerminei de escrever, agora vou ler.\n"); if (( pa = fopen(nome, "r")) == NULL) { printf("\n\nNao foi possivel abrir o arquivo para leitura.\n"); exit(1); } c = fgetc(pa); while (!feof(pa)) { putchar(c); c = fgetc(pa); } fclose(pa); getchar(); } Lendo e Escrevendo Cadeias de Caracteres As funes fgets() e fputs() servem para ler e escrever cadeias de caracteres em arquivos. i Os prottipos das funes so: int fputs(char *str, FILE *parq); int fgets(char *str, int comp, FILE *parq); A funo fputs() escreve a cadeia de caracteres apontada por str no fluxo apontado por parq e o cdigo correspondente EOF ser retornado se ocorrer um erro. A funo fgets() l uma cadeia de caracteres do fluxo especificado at que um caracter de nova linha seja encontrado ou comp-1 caracteres sejam lidos. Observar que diferentemente de gets() o caracter de nova linha encontrado passa a fazer parte da cadeia que recebe um caracter nulo ao seu final. Caso ocorra um erro na leitura da cadeia o ponteiro str recebe o valor NULL O exemplo, listado abaixo, mostra um exemplo de uso das funes para ler e escrever cadeias de caracteres em um arquivo. /* Programa: Descricao: Escreve e em seguida le cadeias de um arquivo. */ #include <stdio.h> #include <conio.h> #define MAX 80 void main (void ) { char linha[MAX]; FILE *pa; char *nome = "texto.txt"; /* Abre o arquivo para leitura e escrita */ if (( pa = fopen(nome, "w+")) == NULL) { printf("\n\nNao foi possivel abrir o arquivo.\n"); exit(1); } /* Cada linha digitada sera gravada no arquivo */ gets(linha); while (!feof(stdin)) { strcat(linha, "\n"); fputs(linha, pa); gets(linha); } rewind(pa); /* volta ao inicio do arquivo */ printf("\nTerminei de escrever, agora vou ler.\n"); fgets(linha, MAX, pa); while (!feof(pa)) {

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 52 de 98

53 Tpicos em C reviso 257 de 24/03/2004 23:16 printf("%s",linha); fgets(linha, MAX, pa); } fclose(pa); getchar(); /* Espera o usuario digitar alguma coisa */ } Lendo e Escrevendo Arquivos Binrios As funes fread e fwrite so empregadas para leitura e escrita de dados em modo binrio. Os prottipos das funes so: size_t fread (void *str, size_t size, size_t nmemb, FILE *parq); size_t fwrite(const void *str, size_t size, size_t nmemb, FILE *parq); A funo fread l nmemb objetos, cada um com size bytes de comprimento do fluxo apontado por stream e os coloca na localizao apontada por ptr. fread retorna o nmero de itens que foram lidos com sucesso. Caso ocorra um erro, ou o fim do arquivo foi atingido o valor de retorno menor do que nmemb ou zero. Esta funo no distingue entre um fim de arquivo e erro, portanto aconselhavel o uso de feof() ou ferror() para determinar que erro ocorreu. A funo fwrite escreve nmemb elementos de dados, cada um com size bytes de comprimento, para o fluxo apontado por stream obtendo-os da localizao apontada por ptr. fwrite retorna o nmero de itens que foram lidos com sucesso. Caso ocorra um erro, ou o fim do arquivo foi atingido o valor de retorno menor do que nmemb ou zero. O programa mostrado abaixo ilustra como podemos escrever e ler dados de diferentes tipos em arquivos. Como um dos parmetros da fun o nmero de bytes do dado a ser lido, recomendado o uso de sizeof. int main (void ) { int inum=10; float fnum=2.5; double pi=3.141516; char c='Z'; FILE *pa; char *nome = "texto.txt"; if (( pa = fopen(nome, "w+b")) == NULL) { printf("\n\nNao foi possivel abrir o arquivo para escrita.\n"); exit(1); } fwrite(&inum, sizeof(int), 1, pa); fwrite(&fnum, sizeof(float), 1, pa); fwrite(&pi, sizeof(double), 1, pa); fwrite(&c, sizeof(char), 1, pa); rewind(pa); fread(&inum, sizeof(int), 1, pa); fread(&fnum, sizeof(float), 1, pa); fread(&pi, sizeof(double), 1, pa); fread(&c, sizeof(char), 1, pa); printf("%d, %f, %lf, %c\n", inum, fnum, pi, c); fclose(pa); exit(0); } Uma das principais aplicaes destas funes a leitura e escrita de estruturas criadas pelos usurios. A gravao em binrio da estrutura permite que o programador ao escrever ou ler do arquivo se preocupe somente com a estrutura como um todo e no com cada elemento que a compe. O programa mostrado abaixo ilustra como podemos escrever e ler estruturas. #include <stdio.h> #include <conio.h> #define MAX 4 int main () { FILE *pa; char nome[40]; char linha[80]; struct pessoa { char nome[40]; int ano; } turma [MAX], back[MAX]; int i;

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 53 de 98

54 Tpicos em C reviso 257 de 24/03/2004 23:16 for (i=0; i<MAX; i++) { puts("Nome ? "); fgets(turma[i].nome, 40, stdin); puts("Ano ? "); fgets(linha, 80, stdin); sscanf(linha, "%d", &turma[i].ano); } puts ("\nImprimindo\n"); for (i=0; i<MAX; i++) { printf("Nome = %s\n", turma[i].nome); printf("Ano = %d\n\n", turma[i].ano); } puts("\nGravando\n"); puts("Qual o nome do arquivo?"); fgets(nome, 40, stdin); if (( pa = fopen(nome, "w+b")) == NULL ) { puts("Arquivo nao pode ser aberto"); exit(1); } for (i=0; i<MAX; i++) { if (fwrite( &turma[i], sizeof (struct pessoa), 1, pa) != 1) puts("Erro na escrita."); } rewind(pa); for (i=0; i<MAX; i++) { if (fread( &back[i], sizeof (struct pessoa), 1, pa) != 1) { puts("Erro na escrita."); if (feof(pa)) break; puts("Erro na leitura."); } } puts("Imprimindo o vetor lido."); for (i=0; i<MAX; i++) { printf("Nome = %s\n", back[i].nome); printf("Ano = %d\n\n", back[i].ano); } exit(0); } Entrada e Sada Formatada As funes fprintf() e fscanf() so equivalentes as funes printf() e scanf() usadas at agora, sendo a nica modificao o fato de que elas trabalham com fluxos de dados (arquivos). Os prottipos das duas funes so os seguintes: int fprintf(FILE *parq, const char *formatacao, ...); int fscanf(FILE *parq, const char *formatacao, ...); onde parq um ponteiro de arquivo recebido aps uma chamada a fopen(). Embora estas duas funes, por sua semelhana com printf() e scanf(), sejam maneiras convenientes de escrecer e ler dados de arquivos, elas tm a desvantagem de serem mais lentas do que uso de arquivos binrios, os quais sero mostradas mais a frente. A perda de tempo devido ao fato dos dados serem gravados em ASCII, o que obriga a uma converso dos dados a cada operao realizada. Em alguns casos o fato dos dados serem gravados em ASCII pode ser considerado um vantagem que se sobrepe a desvantagem da reduo de velocidade. Dados gravados em ASCII podem ser facilmente verificados pelos usurios, o que no acontece com dados em binrio. O exemplo, listado abaixo, mostra um exemplo de uso destas funes para ler e escrever vrios tipos de dados em um arquivo. /* Descricao: Escreve e em seguida le dados formatados. */ #include <stdio.h> #include <conio.h> #define MAX 20 void main (void ) { char palavra[MAX]; int i; float f; FILE *pa; char *nome = "format.txt"; /* Abre o arquivo para leitura e escrita */

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 54 de 98

55 Tpicos em C reviso 257 de 24/03/2004 23:16 if (( pa = fopen(nome, "w+")) == NULL) { printf("\n\nNao foi possivel abrir o arquivo.\n"); exit(1); } puts ("Entre com uma palavra."); scanf ("%s", palavra); puts ("Entre com um numero inteiro."); scanf("%d", &i); puts ("Entre com um numero flutuante."); scanf("%f", &f); /* Escreve os dados no arquivo */ fprintf(pa, "%s %d %f", palavra, i, f); rewind(pa); /* volta ao inicio do arquivo */ printf("\nTerminei de escrever, agora vou ler.\n"); fscanf(pa, "%s %d %f", palavra, &i, &f); printf("Palavra lida: %s\n", palavra); printf("Inteiro lido: %d\n", i); printf("Float lido: %f\n", f); fclose(pa); getchar(); /* Espera o usuario digitar alguma coisa */ }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 55 de 98

56 Tpicos em C reviso 257 de 24/03/2004 23:16

Listas
Introduo

Neste captulo iremos discutir alguns aspectos de listas e como podemos implementar esta estrutura de dados em C. No iremos abordar extensivamente este assunto, mas apenas discutir os algoritmos mais importantes e mostrar como eles podem ser implementados em C. Listas - Definies Bsicas Uma lista linear de informaes uma estrutura de dados simples que armazena informaes sobre dados que apresentam uma relao entre seus elementos. As operaes mais frequentes em listas so a busca, insero e remoo de um determinado elemento. Outras operaes que costumam ocorrer so a alterao de um elemento particular da lista, ordenao dos elementos da lista segundo uma determinada chave, procura do ltimo ou do primeiro elemento da lista, etc. Os elementos da lista podem ser armazenados em posies contguas da memria e neste caso temos uma lista seqencial. Podemos tambm armazenar os elementos em posies quaisquer da memria e neste caso temos de armazenar em cada elemento um indicador de onde est o prximo elemento da lista. Este ltimo mtodo conhecido como alocao encadeada. Listas com Alocao Seqencial A maneira mais simples de se manter uma lista na memria do computador colocar seus ns em posies contguas. Neste caso o elemento j+c estar c bytes (ou palavras, depende do sistema) frente do elemento j da lista. A constante c corresponde ao nmero de bytes (ou palavras) necessrio para armazenar cada elemento da lista. Cada elemento da lista comumente chamado de n. Um n da lista pode conter vrios tipos de informaes que so armazenados em campos. Cada n contm um (ou mais) elemento(s) que identificam unicamente o n, a chave. Uma lista pode estar ordenada ou no de acordo com a chave. O programa mostra um exemplo de busca em uma lista seqencial. O algoritmo, contido na funo busca1, simples e percorre a lista toda at encontrar o elemento. A funo recebe a lista e o elemento a ser procurado. Note que o nmero de elementos na lista n conhecido pelo algoritmo. #include <stdio.h> #define MAX 4 struct Taluno { char nome[40]; unsigned long int dre; }; int busca1 ( struct Taluno t[], unsigned long int ); void le ( struct Taluno t[]); void imprime ( struct Taluno t[]); void main () { int aluno; unsigned long dre; char linha[80]; struct Taluno turma[MAX]; le (turma); /* le dados da turma de MAX alunos */ imprime (turma); /* imprime dados para verificar se correto */ /* uso sscanf, mais seguro que scanf */ puts("Qual o DRE a procurar? "); gets(linha); sscanf(linha, "%lu", &dre); aluno = busca1 (turma, dre); if (aluno == -1) puts("Nao ha aluno com este dre."); else printf("O aluno com dre %lu chama-se %s\n", turma[aluno].dre, turma[aluno].nome); exit(0); } void le (struct Taluno t[]) { int i; char linha[80]; for (i=0; i<MAX; i++) { puts("Nome ? "); gets (t[i].nome); puts("DRE ? "); gets(linha);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 56 de 98

57 Tpicos em C reviso 257 de 24/03/2004 23:16 sscanf(linha, "%lu", &t[i].dre); } } void imprime (struct Taluno t[]) { int i; puts("Dados da turma"); for (i=0; i<MAX; i++) { printf("\nAluno %d\n", i); printf("Nome = %s\n", t[i].nome); printf("Nome = %lu\n", t[i].dre); } } /* Busca um elemento na lista L */ int busca1 (struct Taluno turma[], unsigned long int dre) { int i=0; int aluno=-1; /* vou assumir que a lista contem MAX alunos */ while (i < MAX) { if ( turma[i].dre == dre) { aluno = i; i=MAX; } else i++; } return aluno; /* se -1 nao achei, senao indice do aluno */ } No exemplo anterior so feitas duas comparaes a cada interao ( i < MAX e turma[i].dre == dre). Para diminuir o nmero de comparaes vamos usar um artifcio simples: colocar uma posio extra no final da lista e sempre que for necessrio buscar um elemento na lista, o seu valor ser inserido nesta posio extra. Deste modo sempre acharemos o elemento na lista. No entanto, se o elemento estiver no final da lista significa que o elemento no foi encontrado. Observar que a lista passa ter agora uma posio a mais, que reservada para o elemento a ser procurado. O programa seguinte mostra um exemplo de busca em uma lista seqencial com uma posio a mais. #include <stdio.h> /* A lista cabem 4 elementos o elemento 5 e para a elemento a ser procurado */ #define MAX 5 struct Taluno { char nome[40]; unsigned long int dre; }; int busca1 ( struct Taluno t[], unsigned long int ); void le ( struct Taluno t[]); void imprime ( struct Taluno t[]); void main () { int aluno; unsigned long dre; char linha[80]; struct Taluno turma[MAX]; le (turma); imprime (turma); puts("Qual o DRE a procurar? "); gets(linha); sscanf(linha, "%lu", &dre); aluno = busca1 (turma, dre); if (aluno == -1) puts("Nao ha aluno com este dre."); else printf("O aluno com dre %lu chama-se %s\n", turma[aluno].dre, turma[aluno].nome); exit(0); } void le (struct Taluno t[]) { int i; char linha[80]; for (i=0; i<MAX-1; i++) {

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 57 de 98

58 Tpicos em C reviso 257 de 24/03/2004 23:16 puts("Nome ? "); gets (t[i].nome); puts("DRE ? "); gets(linha); sscanf(linha, "%lu", &t[i].dre); } } void imprime (struct Taluno t[]) { int i; puts("Dados da turma"); for (i=0; i<MAX-1; i++) { printf("\nAluno %d\n", i); printf("Nome = %s\n", t[i].nome); printf("Nome = %lu\n", t[i].dre); } } /* Busca um elemento na lista L */ int busca1 (struct Taluno turma[], unsigned long int dre) { int i=0; turma[MAX-1].dre=dre; while (turma[i].dre != dre) i++; if (i != MAX-1 ) return i; else return -1; } Os exemplos anteriores assumiam dois fatos a cerca da lista: a lista est cheia e desordenada. Quando a lista est ordenada a procura pode ser interrompida antes de chegar ao fim da lista. Neste algoritmo ao invs de procurar o elemento, o teste se o elemento da lista menor que o procurado. O prximo programa mostra um exemplo de busca em uma lista ordenada. Para facilitar vamos mostrar apenas a rotina busca_ord que executa este algoritmo, j que todo o restante do programa similar ao anterior. No arquivo indicado na ligao mostramos o arquivo todo. /* Busca um elemento na lista L */ int busca_ord ( Taluno turma[], unsigned long int dre) { int i=0; turma[MAX-1].dre=dre; while (turma[i].dre < dre) i++; if (i == (MAX-1) || turma[i].dre != dre ) return -1; /* nao achei. */ else return i; } Um algoritmo mais eficiente para busca em uma lista ordenada o algoritmo de busca binria. Primeiro o algoritmo procura o elemento no elemento do meio da tabela. Caso no esteja ele descarta a metade onde o elemento no est e passa a procurar no meio da metade que sobrou. O algoritmo vai dividindo a lista em duas metades, sempre descartando a metade onde o elemento no est. O programa seguinte mostra um exemplo de busca binria em uma lista ordenada. Para facilitar vamos mostrar somente a rotina busca_bin que implementa este algoritmo. /* Busca um elemento na lista L */ int busca_bin ( Taluno t[], unsigned long int dre) { int inf=0, sup=MAX-1, achou=-1, meio; while (inf <= sup) { meio = 0.5*(inf+sup); if (t[meio].dre == dre) { achou=meio; inf = sup+1; } else if (t[meio].dre<dre) inf = meio+1; else sup = meio - 1 ; } return achou; } Agora passaremos a apresentar algoritmos para inserir e remover elementos de uma lista. Ambos os algoritmos utilizam a rotina de busca para inserir e remover. Primeiro vamos apresentar um algoritmo de insero que no precisa que a lista esteja ordenada. Neste algoritmo o elemento, caso ele no j esteja na lista, inserido aps o ltimo elemento. O algoritmo de remoo move, a partir do ltimo elemento at o elemento seguinte ao que ser removido, todos os elementos uma posio para esquerda. O exemplo abaixo mostra um programa que inclui estes dois algoritmos. Como a lista no estar sempre cheia, j que estaremos removendo e inserindo elementos a todo instante, iremos modificar as rotinas de busca. A modificao adiciona uma varivel n que contm o nmero de elementos na lista no instante em questo. O programa pede ao usurio que indique atravs de uma letra qual operao deseja executar. As operaes possveis so as seguintes: [I]nserir, [R]emover, [L]istar e [S]air. Para facilitar o entendimento a lista utilizada contm somente nmeros inteiros. O exerccio 1, pede a construo de um programa similar a este, apenas modificando a lista de modo que ela contenha uma estrutura mais complexa. /* Busca um elemento na lista L */

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 58 de 98

59 Tpicos em C reviso 257 de 24/03/2004 23:16 #define MAX 5 #include <stdio.h> #include <ctype.h> char meu_menu(); void insere(int item[], int *n); void meu_remove(int item[], int *n); void listar(int item[], int n); void ordena(int item[], int n); int busca_bin (int item[], int n, int x); void main() { int item[MAX]; int n=0, sair=0; int a, b, t; char opcao; do { opcao = meu_menu(); switch (opcao) { case 'i': insere(item, &n); break; case 'r': meu_remove(item, &n); break; case 'l': listar(item, n); break; case 's': sair = 1; break; default: puts("Opcao invalida."); break; } } while (!sair); } void ordena(int item[], int n){ int a, b, t; for (a=1; a<n; ++a) { t = item[a]; for (b=a-1; b>=0 && t < item[b]; b--) { item[b+1]=item[b]; } item[b+1]=t; } } int busca_bin (int item[], int n, int x) { int inf=0, sup, meio, achou=-1; sup = n-1; while (inf <= sup) { meio = 0.5*(inf+sup); if (item[meio]==x) { achou=meio; inf = sup + 1; } else if (item[meio]<x) inf = meio+1; else sup = meio-1; } return achou; } void listar (int item[], int n) { int i; for (i=0; i<n; i++) printf("elemento %d = %d \n", i, item[i]); }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 59 de 98

60 Tpicos em C reviso 257 de 24/03/2004 23:16 char meu_menu () { char opcao, linha[80]; // limpa(); puts("Qual a sua opcao?"); puts("[L]istar, [I]nserir, [R]emover, [S]air"); gets(linha); sscanf(linha, "%c", &opcao); return tolower(opcao); } void imprime (int item[]){ int i; for (i=0; i<5; i++) { printf("%d ", item[i]); } printf("\n"); } void insere (int item[], int *n) { int t; char linha[80]; printf("Valor a inserir? "); gets(linha); sscanf(linha, "%d", &t); if (*n < MAX-1) { if ( busca_bin(item, *n, t) == -1) { item[*n] = t; *n=*n+1; ordena (item, *n); } else puts("Elemento ja existe na tabela."); } else puts("Lista ja cheia."); } void meu_remove (int item[], int *n) { int x, indice, i; char linha[80]; if (*n != 0) { printf("Valor a remover? "); gets(linha); sscanf(linha, "%d", &x); indice = busca_bin(item, *n, x); printf("Indice = %d\n", indice); if (indice != -1) { for (i=indice; i<*n; i++) item[i] = item[i+1]; *n = *n - 1; } else puts("Nao existe este elemento."); } else puts("Lista vazia"); } Pilhas com Alocao Seqencial Uma pilha uma lista em que os elementos so inseridos e removidos em uma posio especial, o topo da pilha. A figura apresentada abaixo mostra como se processam operaes que envolvem acesso a uma pilha.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 60 de 98

61 Tpicos em C reviso 257 de 24/03/2004 23:16


Pilha Vazia Topo = -1 Insere A Topo = 0 Insere B Topo = 1 Retira B Topo = 0 Insere C Topo = 1 Insere D Topo = 2

7 6 5 4 3 2 1 0 A

D B A A C A C A

Uma imagem simples pode ilustrar o funcionamento das pilhas. Considere um restaurante onde os clientes do tipo selfservice. Neste restaurante, para se servirem, os clientes retiram pratos e os empregados colocam pratos limpos em uma pilha. Observe que os empregados colocam pratos no topo da pilha, o mesmo local de onde os clientes retiram os pratos. O exemplo listado abaixo, mostra um programa que permite inserir e retirar elementos de uma pilha. /* Exemplo de pilha */ #define MAX 10 #include <stdio.h> #include <ctype.h> char void void void meu_menu(); insere(int item[], int *topo); meu_remove(int item[], int *topo); listar(int item[], int topo);

void main() { int item[MAX]; int topo=-1, sair=0; char opcao; do { opcao = meu_menu(); switch (opcao) { case 'i': insere(item, &topo); break; case 'r': meu_remove(item, &topo); break; case 'l': listar(item, topo); break; case 's': sair = 1; break; default: puts("Opcao invalida."); break; } } while (!sair); } void listar (int item[], int topo) { /* Esta rotina esta aqui somente para depuracao */ int i; for (i=0; i<topo; i++) printf("elemento %d = %d \n", i, item[i]); }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 61 de 98

62 Tpicos em C reviso 257 de 24/03/2004 23:16 char meu_menu () { char opcao, linha[80]; // limpa(); puts("Qual a sua opcao?"); puts("[L]istar, [I]nserir, [R]emover, [S]air"); gets(linha); sscanf(linha, "%c", &opcao); return tolower(opcao); } void insere (int item[], int *topo) { int t; char linha[80]; printf("Valor a inserir? "); gets(linha); sscanf(linha, "%d", &t); if (*topo < MAX) { *topo=*topo+1; item[*topo] = t; printf("Topo = %d\n", *topo); } else puts("Pilha cheia."); } void meu_remove (int item[], int *topo) { int x; if (*topo != -1) { x = item[*topo]; *topo = *topo-1; printf("Retirei %d da pilha.\n", x); } else puts("Lista vazia"); } Uma pilha pode ser usada para avaliar expresses em notao polonesa. Nesta notao no so necessrios parnteses para indicar a ordem em que as operaes sero avaliadas. Abaixo mostramos exemplos de expresses na notao parentizada e em notao polonesa reversa. Parnteses a+b (a + b) - c (a + b) * (c -d) (a + ( b * c )) (a + b) * c ) Polonesa Reversa ab+ ab+cab+cd-* abc*+ ab+c*

Observar que cada operando atua sobre os dois elementos que o precedem na expresso. Caso fossemos colocando estes operandos em uma pilha, poderamos sempre que encontrarmos um operador tirar dois operandos da pilha, executarmos a operao e em seguida guardarmos o resultado na pilha novamente. A figura mostrada abaixo ilustra o que acontece na pilha caso tenhamos que avaliar a expresso 3 4 5 * + .

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 62 de 98

63 Tpicos em C reviso 257 de 24/03/2004 23:16


Pilha Vazia Topo = -1 Insere 3 Topo = 0 Insere 4 Topo = 1 Insere 5 Topo = 2 Opera=* Topo = 1 Opera = + Topo = 0

7 6 5 4 3 2 1 0 3

5 4 3 4 3 20 3 23

O exemplo, listado abaixo, mostra um programa que avalia uma expresso em notao polonesa reversa. Notar que o programa assume que a expresso est gramaticalmente correta. /* Calculo de expressao em notacao polonesa reversa */ #include<stdio.h> #include<string.h> #include<stdlib.h> #define MAX 80 #define VERDADE 1 #define FALSO 0 typedef struct { float item[MAX]; int top; } pilha; void imprime(pilha ); float avalia (const char *); int eOperando (char ); int eOperador (char ); float pop (pilha *); void push (pilha *, float); float oper (char , float ,float); void main(){ char exp[MAX]; puts("Entre com a expresso ?"); gets(exp); puts ("Expresso original "); puts(exp); printf("O resultado da expressao e %f\n", avalia(exp)); } float avalia (const char *expr) { int position; char symb; pilha opndstk; float opnd1, opnd2, value; opndstk.top = -1; /* pilha vazia */ position = 0; /* primeiro simbolo da expressao */ symb = expr[position]; while (symb != '\0') { if (eOperando(symb)) { value = atof(&symb); push(&opndstk, value);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 63 de 98

64 Tpicos em C reviso 257 de 24/03/2004 23:16 // imprime(opndstk); } else if (eOperador(symb)) { opnd2 = pop(&opndstk); opnd1 = pop(&opndstk); value = oper(symb, opnd1, opnd2); push(&opndstk, value); // imprime(opndstk); } if (position < MAX) { position++; symb = expr[position]; } } /* fim do while e da avaliacao */ return pop(&opndstk); } float oper (char symb, float op1, float op2) { switch(symb) { case '+': return op1 + op2; case '-': return op1 - op2; case '*': return op1 * op2; case '/': if (op2) return op1/op2; else { puts("Divisao por zero"); exit(1); } } exit(1); } float pop (pilha *p) { if (p->top >= 0) { p->top--; return p->item[p->top+1]; } else { puts("Underflow"); exit(1); } } void push (pilha *p, float op) { if (p->top < MAX - 1){ p->top++; p->item[p->top] = op; } else { puts("Overflow"); exit(1); } } /* Esta funcao serve so para depuracao do programa */ void imprime (pilha p){ int i; printf("\nPilha = "); for (i=0; i<=p.top; i++) printf("%f ", p.item[i]); printf("\n"); } int eOperador (char c) { if (c == '*' || c == '/' || c == '+' || c == '-') return VERDADE; else return FALSO; } int eOperando (char c) { if (!eOperador(c) && (c != ' ')) return VERDADE; else return FALSO; }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 64 de 98

65 Tpicos em C reviso 257 de 24/03/2004 23:16 Listas com Alocao Encadeada

Listas Simplesmente Encadeadas A figura abaixo mostra como seria uma lista usando ponteiros para encadear os elementos da lista. O ponteiro pt aponta para o n inicial da lista. Cada n est representado por um retngulo dividido em duas partes. Uma das partes contm a informao e a outra o ponteiro para o prximo n. Observar que o no ltimo n a seta aponta para a terra, que indica fim da lista.

O algoritmo mostra como percorrer uma lista encadeada simples. void listar (struct tElemento *ptlista) { int i=0; struct tElemento *pont; pont = ptlista; while (pont) { printf("Elemento %d = %d\n", i++, pont->info); pont = pont->prox; } } Outros algoritmos relevantes em listas so: busca, insero e remoo. Para facilitar a busca e localizao dos ns a serem removidos e das posies de insero modificaremos a lista para incluir o que costuma ser chamado de n cabea. Neste tipo de lista o primeiro n no ir conter informao, ele apenas faz com que o algoritmo de busca no necessite diferenciar o primeiro n dos demais. A figura lista encadeada com n cabea ilustra esta lista.

A rotina de que iremos mostrar um algoritmo simples que tem como prottipo ponteiros para ponteiros. O primeiro aponta para o n anterior ao n procurado e **ponte aponta para o n procurado. void busca ( tElemento *ptlista, int x, tElemento **ant, tElemento **ponte) { /* *ptlista ponteiro para inicio da lista x elemento a ser procurado **ant ponteiro para ponteiro do elemento anterior **pont ponteiro para ponteiro do elemento procurado */ tElemento *ptr; *ant = ptlista; /* aponta no anterior */ *ponte = NULL; /* aponta no procurado, se nao achar retorna nulo */ ptr = ptlista->prox; /* aponta no procurado */ while (ptr) { /* procura enquanto houver chance */ if (ptr->info < x) { /* ainda nao chegou no no */ *ant = ptr; ptr = ptr->prox; } else { /* pode ser aqui */ if (ptr->info == x) *ponte = ptr; /* achou */ ptr = NULL; /* nao estava na lista */ } }

**ant

} Esta rotina parte do programa que inclui rotinas para remoo e insero de elementos em uma lista encadeada simples. A figura remoo de lista encadeada com n cabea ilustra o algoritmo de remoo, que tem o cdigo mostrado abaixo.

void remover ( tElemento *ptlista) { int x; char linha[80]; tElemento **ant, **pont; printf("Valor a remover? "); fgets(linha, 80, stdin); sscanf(linha, "%d", &x); ant = ( tElemento **) malloc(sizeof ( tElemento *));

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 65 de 98

66 Tpicos em C reviso 257 de 24/03/2004 23:16 pont = ( tElemento **) malloc(sizeof ( tElemento *)); busca (ptlista, x, ant, pont); if (*pont) { (*ant)->prox = (*pont)->prox; printf("Retirei %d\n", (*pont)->info); free(*pont); } else puts("Nao achei na lista."); } O algoritmo de insero tem o seguinte cdigo: void insere ( tElemento *ptlista) { tElemento *pt, **pont; int x; char linha[80]; /* ponteiro para o novo no a inserir */ **ant, /* ponteiro para ponteiro na lista */ /* ponteiro para ponteiro do no anterior */

printf("Valor a inserir? "); fgets(linha, 80, stdin); sscanf(linha, "%d", &x); ant = ( tElemento **) malloc(sizeof ( tElemento *)); pont = ( tElemento **) malloc(sizeof ( tElemento *)); busca (ptlista, x, ant, pont); if (!*pont) { pt = ( tElemento *) malloc(sizeof( tElemento)); pt->info = x; pt->prox = (*ant)->prox; (*ant)->prox = pt; } else puts("Elemento ja existe na tabela."); } Listas Circulares O algoritmo de busca em uma lista encadeada pode ser melhorado se modificarmos a lista de modo que ela passe a ser circular como est mostrado na figura abaixo.

pt

N 1

N 2

N 3

Neste caso o algoritmo no tem como testar o final da lista. A soluo armazenar o dado que se est procurando no n cabea. Ao trmino do algoritmo a chave sempre encontrada, e pode-se descobrir se ela pertencia ou no a lista pelo ponteiro que foi dado como resposta. Caso o ponteiro termine apontando para o n cabea, podemos afirmar que o elemento no se encontra na lista. Os algoritmos de procura, insero e remoo em uma lista circular encadeada esto mostrados no programa seguinte. O algoritmo de busca aparece como parte das rotinas de insero e remoo. Neste algoritmos primeiro se procura o elemento depois se decide o que fazer. No algoritmo de busca, caso o elemento j exista na lista, no h nada a fazer, caso contrrio o ponteiro ant aponta para o elemento aps o qual o novo elemento ser inserido. No algoritmo de remoo, a busca termina apontando para o elemento a ser removido (ponteiro pont) ou com a indicao que o elemento no se encontra na lista (pont apontando para o n cabea). #include <stdio.h> #include <ctype.h> #include <stdlib.h> struct tElemento { int info; struct tElemento *prox; }; char tela(); struct tElemento *cria_no(); void insere (struct tElemento *, int ); void meu_remove (struct tElemento *, int); void listar(struct tElemento *); void main() { struct tElemento *ptlista; char linha[80]; char opcao; int sair = 0, valor; ptlista = cria_no(); ptlista->prox=ptlista; do {

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 66 de 98

67 Tpicos em C reviso 257 de 24/03/2004 23:16 opcao = tela(); switch (opcao) { case 'i': puts("Qual dado a inserir?"); gets(linha); sscanf(linha, "%d", &valor); insere(ptlista, valor); break; case 'r': puts("Qual dado a remover?"); gets(linha); sscanf(linha, "%d", &valor); meu_remove(ptlista, valor); break; case 'l': listar(ptlista); break; case 's': sair = 1; break; default: puts("Opcao invalida."); break; } } while (!sair); } char tela () { char opcao, linha[80]; puts("Qual a sua opcao?"); puts("[L]istar, [I]nserir, [R]emover, [S]air"); gets(linha); sscanf(linha, "%c", &opcao); return tolower(opcao); } void listar (struct tElemento *ptlista) { int i=0; struct tElemento *pont; pont = ptlista->prox; while (pont != ptlista) { printf("Elemento %d = %d\n", i++, pont->info); pont = pont->prox; } } void insere (struct tElemento *ptlista, int valor) { struct tElemento *pont, *ant, *pt; /* Aqui esta o algoritmo de busca em uma lista circular */ ant = ptlista; pont = ptlista->prox; ptlista->info = valor; while (pont->info < valor) { ant = pont; pont = pont->prox; } if (pont->info == valor && pont != ptlista) puts("Elemento ja existe na tabela."); else { pt = cria_no(); pt->info = valor; pt->prox = pont; ant->prox = pt; } } void meu_remove (struct tElemento *ptlista, int valor) { struct tElemento *pont, *ant; ant = ptlista; pont = ptlista->prox; ptlista->info = valor; while (pont->info < valor) { if (pont->info < valor) { ant = pont; pont = pont->prox; } } if (pont->info == valor && pont != ptlista) { ant->prox = pont->prox; free(pont); } else puts("Elemento nao existe na tabela."); } struct tElemento *cria_no() {

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 67 de 98

68 Tpicos em C reviso 257 de 24/03/2004 23:16 struct tElemento *pt; if (( pt = (struct tElemento *) malloc(sizeof(struct tElemento)) ) == NULL ) { puts("Nao h espao."); exit(1); } pt->info = -1; pt->prox = NULL; return pt; }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 68 de 98

69 Tpicos em C reviso 257 de 24/03/2004 23:16

rvores
Definies Bsicas rvores so estruturas de dados extremamente teis em muitas aplicaes. Uma rvore formada por um conjunto finito T de elementos denominados vrtices ou ns de tal modo que se T = 0 a rvore vazia, caso contrrio temos um n especial chamado raiz da rvore (r), e cujos elementos restantes so particionados em m>=1 conjuntos distintos no vazios, as subrvores de r, sendo cada um destes conjuntos por sua vez uma rvore. A forma convencional de representar uma rvore est indicado na figura abaixo. Esta rvore tem nove ns sendo A o n raiz.
A B D E H F I C G

Os conjuntos das subrvores tem de ser disjuntos tem de ser disjuntos portanto a estrutura indicada na figura seguinte no uma rvore.
A B D E H C F I G

Se n um n da rvore T ento Tn indica uma subrvore de T com raiz no n n. Os ns n1, n2, ..., nk das subrvores de Tn so chamados de filhos de n e n o pai destes ns, que so ns irmos. Os ns B e C so filhos de A e ns irmos. Ns sem filhos como os ns D, H, I, F e G so chamados de folhas. A subrvore da esquerda do n A tem raiz em B e a subrvore da direita tem raiz em C, isto est indicado pelos dois ramos saindo de A. A ausncia de um ramo na rvore indica uma subrvore vazia, como a subrvore da direita do n B. O nmero de de filhos de um n chamado de grau de sada deste n. Por exemplo, o n C tem grau de sada 3 e o n E grau 2. Se o n n a raiz de uma subrvore Tn e n1 pertence a Tn ento n1 descendente de n e n ancestral de n1. Portanto ns sem descendentes prprios uma folha. Por exemplo, o n H ancestral do n C e o n D descendente do n A. Um caminho da rvore composto por uma seqncia de ns consecutivos (n1, n2, ..., nk-1, nk) tal que existe sempre a relao ni pai de ni+1. Os k vrtices formam k-1 pares e um caminho de comprimento igual a k-1. O comprimento do caminho entre o n A e o n H 3. O nvel de um n n pode ser definido do seguinte modo: o n raiz tem nvel 0, os outros ns tem um nvel que maior uma unidade que o nvel de seu pai. Na rvore da figura anterior temos ns nos seguintes nveis:

nvel 0 = A nvel 1 = B, C nvel 2 = D, E, F, G nvel 3 = H, I

A altura de um n n o nmero de ns do maior caminho de n at um de seus descendentes. As folhas tem altura 1. Existem diversas maneiras de representar rvores. Uma representao que reflete a idia de rvores como conjuntos aninhados mostrado na figura abaixo. A figura mostra o mesmo conjunto da figura inicial deste captulo.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 69 de 98

70 Tpicos em C reviso 257 de 24/03/2004 23:16

H E C A

Uma outra notao que encontramos a toda hora, e que est representada na figura abaixo, a forma identada ou de diagrama de barras. Notar que esta representao lembra um sumrio de livro. Os sumrios dos livros so representaes da rvore do contedo do livro.

rvore e sua representao por barras Uma outra forma interessante de representar uma rvore a representao por parnteses aninhados. Da mesma forma que a figura inicial representa uma rvore no plano a representao por parnteses representa uma rvore em uma linha. A seqncia de parnteses representa a relao entre os ns da estrutura. O rtulo do n inserido esquerda do abre parnteses correspondente. A rvore representada planarmente pela figura no inco deste caputlo pode ser representada em uma linha por

(A (B(D))(C(E(H)(I))(F)(G)))
Esta representao tem importncia, por exemplo, no tratamento de expresses aritmticas, j que toda expresso aritmtica pode ser colocada nesta forma. Se colocarmos uma expresso nesta forma podemos ento represent-la como uma rvore, mostrando como ela seria calculada. Para colocarmos uma expresso em forma de rvore devemos considerar cada operador como um n da rvore e os seus operandos como as duas subrvores. Considere a expresso C seguinte

A + (B-C)*D%(E*F)
que aps receber todos os parnteses fica da seguinte maneira

(A + ((B-C)*(D%(E*F))))
A figura seguinte mostra como fica esta expresso representada por uma rvore.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 70 de 98

71 Tpicos em C reviso 257 de 24/03/2004 23:16


Uma expresso e sua representao em rvore + A B C D E * % * F

(A+((B-C)*(D%(E*F))))
rvores Binrias A figura abaixo mostra um importante tipo de rvore que a rvore binria. Em uma rvore binria cada n tem no mximo duas subrvores, e quando h somente uma presente necessrio distinguir entre subrvore esquerda e direita. rvores binrias podem ser vistas em diversas situaes do cotidiano. Por exemplo, um torneio de futebol eliminatrio, do tipo das copas dos pases, como a Copa do Brasil, em que a cada etapa os times so agrupados dois a dois e sempre so eliminados metade dos times uma rvore binria.
A B D E H F I C G

rvore binria Formalmente uma rvore binria pode ser definida como um conjunto finito de ns, que vazio, ou consiste de um n raiz e dois conjuntos disjuntos de ns, a subrvore esquerda e a subrvore direita. importante observar que uma rvore binria no um caso especial de rvore e sim um conceito completamente diferente. Por exemplo, considere a figura, note que so duas rvores idnticas, mas so duas rvores binrias diferentes. Isto porque uma delas tem a subrvore da direita vazia e a outra a subrvore da esquerda.
A B C D D A B C

rvores Binrias Distintas Uma rvore estritamente binria uma rvore binria em que cada n tem 0 ou 2 filhos. Uma rvore binria cheia uma rvore em que somente os ns folhas tem suas subrvores vazias. Uma rvore completa aquela em se n um n com algumas de subrvores vazias, ento n se localiza no penltimo ou no ltimo nvel. Portanto, toda rvore cheia completa e estritamente binria. A figura seguinte mostra uma rvore estritamente binria, completa e cheia.

(a) rvore Estritamente Binria

(b) rvore Binria Completa

(c) rvore Binria Cheia

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 71 de 98

72 Tpicos em C reviso 257 de 24/03/2004 23:16

Armazenamento de rvores Binrias Para armzenar cada n de uma rvore binria precisamos de uma estrutura que contenha dois ponteiros: um aponta para a subrvore esquerda e outro para a subrvore direita. Naturalmente, devemos ter o(s) campo(s) para armazenar as informaes que o n deve conter. Nos algoritmos que iremos mostrar consideraremos que existe a sguinte definio para a estrutura do n: typedef struct sttNo { tipo inf; struct sttNo *esq, *dir; } tNo; A figura seguinte mostra um diagrama de como seria o armazenamento de uma rvore binria. Observar que se desconsiderarmos os campos de informao para armazenar uma rvore com n ns precisamos de 2n+1 unidades de memria.

Raiz A

B D E F

C G

No processo de criar uma rvore precisaremos de trs operaes importantes: cria_arvore, pos_esq e pos_dir. cria_arvore cria uma rvore binria nova consistindo de um nico n, armazena a informao. e retorna um ponteiro para este n. Um algoritmo para esta funo pode ser o seguinte: p = cria_no(); p->info = x; p->esq = NULL; p->dir = NULL; return p; pos_esq aceita um ponteiro p para uma rvore binria sem filho esquerdo e cria um novo filho esquerdo contendo a informao x. Um possvel algoritmo para esta funo pode ser: if (p->left) puts("Operao ilegal"); else { q = cria_arvore(); p->left = q; } O algoritmo pos_dir semelhante a este com a diferena que ele cria um n a direita. Uma Aplicao de rvores Binrias As rvore binrias so estruturas importantes toda vez que uma deciso binria deve ser tomada em algum ponto de um algoritmo. Vamos agora, antes de passar a algoritmos mais complexos, mostrar uma aplicao simples de rvores binrias. Suponhamos que precisamos descobrir nmeros duplicados em uma lista no ordenada de nmeros. Uma maneira comparar cada novo nmero com todos os nmeros j lidos. Isto aumenta em muito a complexidade do algoritmo. Outra possibilidade manter uma lista ordenada dos nmeros e a cada nmero lido fazer uma busca na lista. Outra soluo usar uma rvore binria para manter os nmeros. O primeiro nmero lido colocado na raiz da rvore. Cada novo nmero lido comparado com o elemento raiz, caso seja igual uma duplicata e voltamos a ler outro nmero. Se menor repetimos o processo com a rvore da direita e se maior com a rvore da esquerda. Este processo continua at que uma duplicata encontrada ou uma rvore vazia achada. Neste caso, o nmero inserido na posio devida na rvore. Considere que os nmeros 7 8 2 5 8 3 5 10 4 foram fornecidos pelo usurio, neste caso a rvore binria mostrada na figura seguinte seria construida.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 72 de 98

73 Tpicos em C reviso 257 de 24/03/2004 23:16

10

O programa seguinte mostra este algoritmo. O programa insere os ns na rvore e imprime uma mensagem caso seja fornecido um nmero que j foi lido antes. #include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct stNo { int info; struct stNo *esq, *dir; } tNo ; tNo *cria_arvore( int ); tNo *cria_no( ); void pos_esq (tNo *, int ); void pos_dir (tNo *, int ); void main() { tNo *raiz, *p, *q; char linha[80], *numero; int num; gets(linha); numero = strtok(linha, " "); /* pega o primeiro numero da lista */ num = atoi(numero); raiz = cria_arvore(num); /* insere na raiz */ numero = strtok(NULL, " "); while (numero) { q = raiz; p = raiz; printf("Li numero %d\n", num); /* le novo numero */ num = atoi(numero); while (num != p->info && q) { /* procura na arvore */ p = q; if (num < p->info) q = p->esq; /* passa para arvore esquerda */ else q = p->dir; /* passa para direita */ } if (num == p->info) printf("O numero %d ja existe na arvore.\n", num); else { /* vou inserir o numero na arvore */ if (num < p->info) pos_esq(p, num); else pos_dir(p, num); } numero = strtok(NULL, " "); } /* fim do while (numero) */ } tNo *cria_arvore (int x) { tNo *p; p = cria_no (); if (p) { p->info = x; return p; } else { puts("Faltou espaco para alocar no."); exit(1); } }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 73 de 98

74 Tpicos em C reviso 257 de 24/03/2004 23:16 tNo *cria_no() { tNo *p; if ((p = (tNo *) malloc(sizeof(tNo))) == NULL) return NULL; else { p->esq = NULL; p->dir = NULL; return p; } } void pos_esq(tNo *p, int x) { tNo *q; if (p->esq) puts("Operacao de insercao a esquerda ilegal."); else { q = cria_arvore(x); p->esq = q; } } void pos_dir(tNo *p, int x) { tNo *q; if (p->dir) puts("Operacao de insercao a direita ilegal."); else { q = cria_arvore(x); p->dir = q; } }

Percorrendo rvores Binrias Uma operao muito comum percorrer uma rvore binria, o que significa passar por todos os ns, pelo menos uma vez. O conceito de visitar significa executar uma operao com a informao armazenada no n, por exemplo, imprimir seu contedo. Na operao de percorrer a rvore pode-se passar por alguns ns mais de uma vez, sem porm visit-los. Uma rvore uma estrutura no seqncial, diferentemente de uma lista, por exemplo. No existe ordem natural para percorrer rvores e portanto podemos escolher diferentes maneiras de percorr-las. Ns iremos estudar trs mtodos para percorrer rvores. Todos estes trs mtodos podem ser definidos recursivamente e se baseiam em trs operaes bsicas: visitar a raiz, percorrer a subrvore da esquerda e percorrer a subrvore da direita. A nica diferena entre estes mtodos a ordem em que estas operaes so executadas. O primeiro mtodo, conhecido como percurso em pr-ordem, implica em executar recursivamente os trs passos na seguinte ordem. 1. 2. 3. Visitar a raiz; Percorrer a subrvore da esquerda em pr-ordem; Percorre a subrvore da direita em pr-ordem.

Para a rvore da figura inicial, este percurso forneceria, no caso da visita significar imprimir, os seguintes resultados: F B A D C E H G I. Uma aplicao interessante deste tipo de percurso aplic-lo uma rvore que contenha uma expresso aritmtica, a qual foi expandida e recebeu todos os parnteses. Por exemplo, aplicando-se o percurso em pr-ordem rvore da expresso mostrada anteriormente, obtm-se como resultado a expresso em notao polonesa normal, isto os operandos antes dos operadores. Deste modo o resultado +A*-BC%D*EF. Observar que esta notao diferente da notao polonesa reversa, em que os operadores aparecem depois dos operandos. Um algoritmo recursivo para implementar este modo de percurso pode ser o seguinte: void pre_ordem ( tipoNo *pt) { if (pt) { visita (pt); pre_ordem (pt->esq); pre_ordem (pt->dir); } } Para percorrer a rvore em ordem simtrica executa-se recursivamente os trs passos na seguinte ordem: 1. 2. 3. Percorrer a subrvore da esquerda em ordem simtrica; Visitar a raiz; Percorrer a subrvore da direita em ordem simtrica. Um algoritmo recursivo para implementar este modo de percurso pode ser o seguinte: void em_ordem ( tipoNo *pt) { if (pt) { em_ordem (pt->esq); visita (pt); em_ordem (pt->dir);

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 74 de 98

75 Tpicos em C reviso 257 de 24/03/2004 23:16 } } Para a rvore da figura do incio deste captulo, o percurso forneceria o seguinte resultado A B C D E F G H I. Este tipo de percurso muito empregado em rvores binrias de busca. Considere a rvore mostrada na figura da rvore de busca, que foi gerada como est indicado na seo Uma Aplicao de rvores Binrias. Caso a rvore seja percorrida em ordem simtrica o resultado seria 2 3 4 5 7 8 10 que so os nmeros lidos em ordem crescente sem repetio. O percurso conhecido como ps-ordem feito a partir dos trs passos na seguinte ordem: 1. 2. 3. Percorrer a subrvore da esquerda em ps-ordem; Percorrer a subrvore da direita em ps-ordem; Visitar a raiz;

Para a rvore da figura inicial da seo rvores Binrias, o percurso forneceria o seguinte resultado A C E D B G I H F. O percurso em ps-ordem pode ser aplicado no clculo da altura de uma rvore. Para calcular a altura de uma rvore necessrio calcular o maior caminho da raiz at uma de suas folhas. Deste modo s podemos calcular o comprimento de um caminho a partir de um n v aps percorrermos todos os seus descendentes. O algoritmo mostrado abaixo mostra como fica a implementao da funo visita para que ela calcule a altura do n. void visita (tNo *p) { int alt1, alt2; if (p->esq) alt1 = p->esq->altura; else alt1 = 0; if (p->dir) alt2 = p->dir->altura; else alt2 = 0; if (alt1>alt2) p->altura = alt1 + 1; else p->altura = alt2 + 1; printf("info = %d ", p->info); printf("altura = %d\n", p->altura); } As variveis alt1 e alt2 armazenam a altura das subrvores da esquerda e da direita e o campo altura um novo campo da estrutura que armazena o n. A altura de um n igual ao maior valor entre as alturas esquerda e direita incrementado de um. Algoritmo de Huffman Para analisarmos mais uma aplicao de rvores binrias vamos considerar o problema de codificar uma mensagem composta de uma seqncia de smbolos de um alfabeto de n smbolos. Esta mensagem ser transformada em uma seqncia de bits, depois de a cada smbolo for atribudo um cdigo binrio e os cdigos dos smbolos da mensagem forem concatenados. Considere um alfabeto composto de quatro smbolos A, B, C e D, sendo que a cada um dos smbolos foi atribudo o cdigo indicado a seguir: Smbolo A B C D Cdigo 00 01 10 11

A mensagem ABCADCA seria codificada da seguinte maneira 00011000111000, tendo comprimento de 14 bits. O objetivo do algoritmo criar um cdigo que minimize o comprimento da mensagem. Para criar este cdigo vamos levar em conta a freqncia de cada smbolo na mensagem. A Tabela a seguir mostra a freqncia de cada smbolo na mensagem Smbolo A B C D Freqncia 3 1 2 1

Desta tabela podemos verificar que se atribuirmos ao smbolo A um cdigo binrio mais curto que os atribudos aos smbolos B e D teramos uma mensagem menor. Isto provm do fato que o smbolo A aparece mais vezes do que os smbolos B e D. Suponha que os seguintes cdigos sejam atribudos aos smbolos Smbolo A B C D Cdigo 0 110 10 111

Usando estge cdigo a mensagem ABCADCA ficaria 0110100111100 que requer 13 bits. Em mensagens longas com mais smbolos infrequentes o ganho pode ser maior. Um dos requerimentos deste cdigo que nenhum cdigo seja prefixo de outro, caso a decodificao seja feita da esquerda para direita.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 75 de 98

76 Tpicos em C reviso 257 de 24/03/2004 23:16 Para decodificar a mensagem vamos comear da esquerda para a direita, caso o primeiro bit seja 0 o cdigo corresponde ao smbolo A. No caso contrrio devemos continuar a examinar os bits restantes. Se o segundo bit for 0 o smbolo um C, caso contrrio examinamos o terceiro bit, um 0 indica um B e D no outro caso. Do que vomos at agora o algoritmo para encontrar o algoritmo timo o seguinte. Encontre os dois smbolos que aparecem com menor freqncia, no nosso caso B e D. Atribumos 0 para B e 1 para D. Combine estes dois smbolos em um BD. Este novo smbolo ter freqncia igual a soma das freqncias de B e D, no caso 2. Temos agora os seguintes smbolos A (3), C (2) e BD (2), os nmeros entre parnteses so as freqncias. Novamente devemos escolher os smbolos de menor freqncia, que so C e BD. Atribumos o cdigo 0 ao smbolo C e 1 ao BD. Isto siginifica adicionar 1 aos cdigos de B e D, que passam a valer 10 e 11 respectivamente. Os dois smbolos so combinados ento no smbolo CBD de freqncia 4. Temos agora dois smbolos A (3) e CBD (4). Atribumos 0 ao smbolo A e 1 ao smbolo CBD. O smbolo ACBD o nico smbolo restante e recebe o cdigo NULL de comprimento 0. A figura seguinte mostra a rvore binria que pode ser construda a partir deste exemplo. Cada n representa um smbolo e sua freqncia.
ACBD,7

A,3

CBD,4

C,2

BD,2

B,1 rvore de Huffman

D,1

Vamos considerar outro exemplo em que temos

Removendo Ns de rvores Binrias Para remover um n de uma rvore binria devemos considerar trs casos:

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 76 de 98

77 Tpicos em C reviso 257 de 24/03/2004 23:16 1. 2. 3. n sem filhos; n com um unico filho; n com dois filhos.

O caso de um n sem filhos o mais simples e significa apenas ajustar o ponteiro de seu pai. A Figura remov0 ilustra este caso, onde o n com o valor 8 removido. No caso do n ter um nico filho a mudana na rvore tambm simples significa mover o n filho daquele ser removido uma posio para cima como est ilustrado na Figura remove1, onde o n com o valor 6 removido. O caso mais complexo o do n com dois filhos. Neste caso devemos procurar o sucessor s (ou antecessor) do n dever ocupar este lugar. Este n (sucessor) um descendente que est na subrvore da direita do n e corresponde ao n mais esquerda desta rvore. Ele no tem filhos esquerda e a sua rvore direita pode ser movida para o lugar de s. A Figura remove2 ilustra o caso de remoo do n com o valor 12. Observe que o n 13 (sucessor) assumiu o lugar do n 12.

Figura remove0: Removendo n (8) sem filhos.

Figura remov1: Removendo n (6) com um filho.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 77 de 98

78 Tpicos em C reviso 257 de 24/03/2004 23:16

Figura remov2: Removendo n (12) com dois filhos.


O texto abaixo mosta uma rotina que remove ns de uma rvore, que contm nmeros inteiros. tNo *remover (tNo *tree, int num) { tNo *p, /* p aponta para o no a ser removido */ *q, /* q aponta para o pai do no */ *rp, /* rp aponta que ira substituir o no p */ *f, *s; /* sucessor do no p */ p = tree; q=NULL; /* procura o no com a chave num, p aponta para o no e q aponta para o pai do no */ while ( p && p->info != num) { q = p; if ( num < p->info) p = p->esq; else p = p->dir; } /* fim do while */ if (!p) return NULL; /* a chave nao existe na arvore */ /* agora iremos ver os dois primeiros casos, o no tem um filho no maximo */ if (p->esq == NULL) rp = p->dir; else if (p->dir == NULL) rp = p->esq; else { f=p; rp = p->dir; s = rp->esq; /* s e sempre o filho esq de rp */ while (s != NULL) { f = rp; rp = s; s = rp->esq; } /* neste ponto, rp e o sucessor em ordem de p */ if (f != p) { /* p nao e o pai de rp e rp == f->left */ f->esq = rp->dir; /* remove o no rp de sua atual posicao e o substitui pelo filho direito de rp rp ocupa o lugar de p */ rp->dir = p->dir; } /* define o filho esquerdo de rp de modo que rp ocupe o lugar de p */ rp->esq = p->esq; }

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 78 de 98

79 Tpicos em C reviso 257 de 24/03/2004 23:16 /* insere rp na posicao ocupada anteriormente por p */ if (q == NULL) tree = rp; else if (p == q->esq) q->esq = rp; else q->dir = rp; free(p); return rp; }

rvores rvores Binrias Balanceadas Uma rvore binria balanceada, chamada de rvore AVL, uma rvore binria na qual as alturas das duas subrvores de cada um dos ns nunca diferem em mais de 1. O balanceamento de um n igual a diferena entre as suas altura esquerda e direita. Portanto, cada n de uma rvore balanceada tem balanceamento igual a -1, 0 ou 1, dependendo da comparao entre as alturas esquerda e direita. Lembrando que a altura de um n n da rvore o nmero de ns do maior caminho de n at um de seus descendentes. As folhas tem altura 1. Uma rvore binria completa com n>0 ns tem altura mnima, que igual a 1 + floor(log (n)). A figura seguinte mostra uma rvore binria balanceada. Os valores dentro do n so altura do n e seu balanceamento.

5,-1

3,1 2,0 1,0 2,0

4,-1 3,-1

1,0

1,0

1,0

1,0

1,0

2,0

rvore Binria Balanceada

1,0

1,0

Caso a probabilidade de pesquisar uma chave em uma tabela seja a mesma para todas as chaves, uma rvore binria balanceada ter a busca mais eficiente. Infelizmente o mtodo de insero em rvores binrias apresentado anteriormente no garante que a rvore permanecer balanceada. Como j vimos a estrutura da rvore depende da ordem em que as chaves so inseridas na rvore. A Figura arvbali mostra possibilidades de insero na rvore e o que ocorreria com o seu balanceamento. Cada insero que mantm a rvore balanceada est indicada bor um B e as que desbalanceiam a rvore por um D. Observe que uma rvore se torna desbalanceada quando o n inserido se torna descendente esquerdo de um n que tinha anteriormente um balanceamento de 1 ou se ele se tornar descendente direito de um n que tinha anteriormente balanceamento de -1. Isto fcil de deduzir, por exemplo, um n que tinha balanceamento 1 e recebe um descendente direito aumenta sua altura em 1, portanto aumentando o seu desbalanceamento.

5,-1

3,1 2,0 1,0 1,0 B 1,0 B 1,0 2,0 1,0

4,-1 3,-1 1,0 2,0

1,0

1,0

rvore Balanceada e suas Inseres

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 79 de 98

80 Tpicos em C reviso 257 de 24/03/2004 23:16 Observemos uma subrvore que ser tornar desbalanceada quando ocorrer uma insero. Vamos supor tambm que este n tem um balanceamento de 1. Neste caso o desbalanceamento ocorrer se a insero ocorrer em um n da direita. A prxima figura mostra um exemplo deste caso.

N A

Altura n +1

Altura n

Altura n

novo n Insero em rvore Binria

Observar que o n A tem balanceamento 1, isto significa que a subrvore da esquerda tem altura no nula e maior em uma unidade que a da direita. Ao inserirmos um n na subrvore da direita a sua altura aumenta de um e o balanceamento passa para 2. Observar tambm que como o n mais jovem a se tornar desbalanceado o A, o seu filho pela esquerda tem de ter balalneamento igual a 0. Para manter a rvore balanceada necessrio que a transformao na rvore de tal modo que: 1. 2. a rvore permanea uma rvore de busca binria; a rvore continue a ser uma rvore balanceada.

Para isto vamos definir a operao de rotao em uma rvore. Uma rotao pode ser direita ou esquerda. A figura seguinte mostra uma rvore e os resultados dos dois tipos de rotao sobre esta rvore esto mostrados na Figura arvrota.
A B D E F C G

rvore Original

C A B D E F G D

B A E F Rotao Direita C G

Rotao Esquerda

Um possvel algoritmo para rodar para a esquerda uma rvore enraizada em p : q = p->direita; temp = q->esquerda; q->esquerda = p; p->direita = temp; Para verificar o que fazer em uma rvore T aps a insero de um n q vamos considerar os vrios casos possveis. Primeiro, se aps a incluso todos os ns se mantiveram regulados, ento a rvore se manteve AVL e nada h a fazer. Caso contrrio vamos considerar o n p mais prximo das folhas, que se tornou desregulado. A escolha de p nica, pois qualquer subrvore de T que se tornou desregulada deve incluir p. Sejam hd(p) e he(p) as alturas direita e esquerda das subrvores de p, portanto |hd(p) - he(p)| = 2 pois T era AVL.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 80 de 98

81 Tpicos em C reviso 257 de 24/03/2004 23:16 Temos os seguintes casos:

caso 1: hd(p) >he(p)


neste caso q pertence a subrvore esquerda de p. Alm disso p possui o filho esquerdo u <> q, seno p no estaria desregulado. Sabe-se tambm que hd(u) <>he(u), pela mesma razo. Para o caso 1 temos duas possibilidades: caso 1.1

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 81 de 98

82 Tpicos em C reviso 257 de 24/03/2004 23:16

Grficos em Borland C
Introduo O objetivo deste captulo mostrar de maneira introdutria a biblioteca grfica do Borland C juntamente com alguns conceitos fundamentais para o seu uso. Trata-se de um assunto sem muitas complicaes e, no entanto, extremamente estimulante, pois pode abrir um enorme leque de novas possibilidades para a criao de programas na linguagem C, ou at mesmo para reformular a interface de projetos anteriores. A computao grfica um campo vasto e complexo, apresentando aspectos artsticos, matemticos e computacionais. Em vista disso, ultimamente vem surgindo alguns termos com o objetivo de diferenciar sub-reas dentro desse grande contexto. Um deles a Compugrafia ou, a cincia que estuda como trabalhar com grficos em sistemas de computadores. Entretanto, mesmo a compugrafia algo muito abrangente. Portanto, o objetivo aqui ser apenas introduzir algumas idias bsicas sobre as bibliotecas grficas do C. Com essas noes e uma boa dose de criatividade pode-se vislumbrar uma srie de aplicaes, seja na produo de jogos simples, programas matemticos que tracem grficos modestos ou at mesmo simulaes usando o computador. Este contedo voltado para todos aqueles que pretendem se aventurar nos recursos grficos oferecidos pela linguagem C. Dedicamos ainda algumas observaes especiais para os estudantes de Informtica da Universidade Federal do Rio de Janeiro que trabalhem no LCI. A criao de um programa inclui diversos elementos estruturais como os algoritmos utilizados e a otimizao do cdigo, mas um fator que, tem a sua importncia cada vez mais confirmada, especialmente em programas que tem como objetivo agradar o usurio: o desenho da interface. A definio de interface diz: "uma situao, ou rea, em que duas coisas podem juntar-se e ter um efeito uma na outra". A interface de um programa tem importncia bilateral: 1. Permitir que um usurio razoavelmente preparado possa expressar o que deseja fazer com os dados. 2. Garantir o retorno (ou feedback) das informaes necessrias ao usurio e avis-lo quando precisar de algum dado. O motivo que gerou a criao deste novo captulo da apostila devido ao fato de que a interface de um programa pode (e em alguns casos deve) conter elementos grficos. Antes de entender como fazer uso de recursos grficos na linguagem C, precisamos definir alguns conceitos bsicos, que na verdade no dependem do estilo ou linguagem de programao, mas sim de caractersticas de hardware de um computador e das idias por traz de tudo isso. Os adaptadores grficos dispe normalmente de vrios modos de exibio, e entre eles normalmente esto divididos em modos de texto e modos grficos. Apesar de todas as funes pr-fabricadas e interfaces de desenvolvimento, as operaes com o seu adaptador grfico so todas feitas atravs de manipulaes de dados em uma rea de memria chamada memria de vdeo. H alguns anos atrs, a IBM, numa tentativa de facilitar a vida dos desenvolvedores de software, criou o padro VGA (Video Graphics Array) que definia uma srie de funes de baixo nvel e registradores que podiam manipular com as informaes da memria de vdeo. As placas de vdeo do padro VGA tinham at 256 Kbytes de memria de vdeo, o que lhes permitia usar definies de at 640x400 pixels com 256 cores simultneas na tela. Entretanto, com o aperfeioamento do hardware grfico, surgiu uma nova gerao de adaptadores grficos para PCs, o SuperVGA, que permitia resolues mais altas e maior profundidade de cores. Os SuperVGAs tambm receberam uma padronizao chamada extenso VESA, para permitir que programadores criassem aplicaes grficas que fossem compatveis com a maior parte dos novos adaptadores. Conceitos Principais A Memria de Vdeo - Resoluo e Profundidade de Cores - o Pixel Agora que j mencionamos a memria de vdeo, vamos nos estender um pouco mais sobre a sua verdadeira utilidade. Toda a tela no modo grfico est dividida em pequenos pontos chamados Pixels (Picture Elements - do ingls para Elementos da Figura), que compe a imagem no monitor. Um adaptador pode conter vrios modos grficos, mas o que os diferencia so as suas resolues e profundidades de cores. A resoluo ou definio determina quantos pixels contm uma tela naquele modo de exibio, e indicada atravs de 2 nmeros: a quantidade de pixel em uma linha da tela (resoluo horizontal), e a quantidade em uma coluna (resoluo vertical). Desta maneira, o modo grfico com 320 por 200 pixels contm um total de 64.000 pixels na tela. A profundidade de cores na tela define quantos bytes na memria de vdeo cada pixel deve ocupar. Assim sendo, se quisermos que usar apenas 1 byte para cada pixel na tela, ele poder assumir at 256 valores diferentes (lembre-se que 1 byte contm 8 bits, e neste espao possvel gravar valores de 0 at 255). Vamos imaginar agora um exemplo de modo grfico: 640x400 pixels, com 256 cores. Para gerar 640x400 precisamos de um total de 256.000 pixels. Como vamos usar 256 cores, cada pixel dever ocupar 1 byte toda a tela ocupar 256.000 bytes na memria de vdeo. "Primitivas Grficas" - a Linha, o Retngulo, o Crculo A maneira bsica de gerar uma imagem atravs do que chamamos de primitivas grficas, ou seja, as estruturas e formas das quais derivam muitas outras formas de grficos. Para efeitos de computao grfica, vamos falar de algoritmos das primitivas grficas, ou seja, algoritmos que so usados para desenhar as primitivas grficas. Um detalhe importante que nada nestes algoritmos diz que eles dependem de estruturas do modo grfico, como pixels ou cores diferentes. De fato, possvel implement-los para formar figuras no modo texto. BITMAPs No desenvolvimento de aplicaes grficas, um recurso largamente usado o de Bitmaps (ou Mapas de Pontos). Muitas vezes necessrio usar uma imagem vrias vezes e no se deseja redesenh-la com as formas bsicas cada vez que se queira coloc-la na tela. Um bom exemplo um ponteiro do mouse, que precisa ser movido pela tela constantemente de uma maneira rpida para no tornar o programa ineficiente.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 82 de 98

83 Tpicos em C reviso 257 de 24/03/2004 23:16 Em poucas palavras, um bitmap apenas um conjunto de bytes pr-definidos que pode ser copiado para a memria de vdeo, e exibidos completamente. De fato, os formatos arquivos de imagens mais simples (como o BMP), guardam a sequncia correta destes byte para serem exibidos na tela. A Biblioteca Grfica da Borland Nota sobre as bibliotecas grficas da linguagem C Compiladores C so produzidos por diferentes fabricantes, e pelo fato de as bibliotecas grficas no estarem includas no padro ANSI, pequenas mudanas podem ser encontradas de uma verso para outra. Apesar disso, os princpios de funcionamento e uso das mesmas so semelhantes, mesmo que haja diferenas na sintaxe. Este captulo descreve as funes do Borland C. Para obter informaes sobre a biblioteca da Microsoft, consulte o livro "C Completo e Total", de Herbert Schildt. Notas: 1. Nenhuma funo grfica est definida em ambiente Windows. 2. A numerao, tanto das linhas quanto das colunas do vdeo, comea em zero.

Descrio das Principais Funes da Biblioteca graphics.h


arc circle closegraph ellipse floodfill getbkcolor getdrivername getgraphmode getmaxcolor getmaxy getpalette gettextsettings gety _graphfreemem imagesize installuserfont lineto outtext putimage registerbgidriver sector setaspectratio setfillpattern setgraphmode setrgbpalette setusercharsize setwritemode bar cleardevice detectgraph fillellipse getarccoords getcolor getfillpattern getimage getmaxmode getmodename getpalettesize getviewsettings graphdefaults _graphgetmem Initgraph line moverel outtexttxy putpixel registerbgifont setactivepage setbkcolor setfillstyle setlinestyle settextjustify setviewport textheight bar3d clearviewport drawpoly fillpoly getaspectratio getdefaultpalette getfillsettings getlinesettings getmaxx getmoderange getpixel getx grapherrormsg graphresult installuserdriver linerel moveto Pieslice rectangle restorecrtmode setallpalette setcolor setgraphbufsize setpalette settextstyle setvisualpage textwidth

void far arc(int x, int y, int stangle, int endangle, int radius) Desenha um arco de circunferncia, com coordenada de centro definida por x e y. O arco comea no ngulo stangle, e termina em endangle, ambos em graus. O tamanho do raio determinado por radius.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 83 de 98

84 Tpicos em C reviso 257 de 24/03/2004 23:16

void far bar(int left, int top, int right, int bottom); Desenha uma barra (retngulo com preenchimento interno). A coordenada do canto superior esquerdo determinada por left e top, e a do canto inferior direito por right e bottom. Usa a mscara de preenchimento definida por setfillstile() (caso nenhuma mscara tenha sido definida, como padro usado o preenchimento uniforme, com a cor de desenho atual).

Legenda (da esquerda para direita, de cima para baixo) LINE_FILL LTSLASH_FILL LTBKSLASH_FILL HATCH_FILL WIDE_DOT_FILL CLOSE_DOT_FILL

SLASH_FILL XHATCH_FILL EMPTY FILL

BKSLASH_FILL INTERLEAVE_FILL SOLID_FILL*

void far bar3d(int left, int top, int right, int bottom, int depth, int topflag); Desenha uma barra com efeito 3D. Depth indica a profundidade, em pixels, da barra. Se definirmos topflag igual a zero, a linha superior da barra no desenhada. Para qualquer valor diferente de zero, a barra desenhada integralmente.

Profundidade

Coordenada superior esquerda


Funo relacionada: bar()

Coordenada inferior direita

void far circle(int x, int y, int radius); Desenha um crculo de raio radius, com centro nas coordenadas x (coluna) e y (linha). void far cleardevice(void); Limpa a tela grfica. void far clearviewport(void); Limpa a janela de visualizao corrente. Funo relacionada: setviewport() void far closegraph(void);

A funo closegraph() fecha o sistema grfico.


void far detectgraph(int far *graphdriver, int far *graphmode); Determina o driver grfico e o modo a ser usado, checando o hardware disponvel. Os drivers padres so: 1. CGA 2. MCGA

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 84 de 98

85 Tpicos em C reviso 257 de 24/03/2004 23:16 3. 4. 5. 6. 7. 8. 9. 10. EGA 64K EGA EGA monocromtico IBM 8514 Hercules monocromtico AT&T 6300 PC VGA IBM 3270 PC

void far floodfill(int x, int y, int border); Preenche uma regio delimitada por uma linha. A coluna do ponto de referncia determinada por x, e a linha por y. int far getbkcolor(void); Retorna a cor de fundo corrente. int far getcolor(void); Retorna a cor de desenho corrente. char *far getdrivername(void); Retorna um ponteiro para o nome do driver grfico corrente. Funo relacionada: detectgraph() int far getgraphmode(void); Retorna o modo grfico corrente. char * far getmodename(int mode_number); Retorna um ponteiro para uma string com nome do modo grfico especificado. O modo 2, por exemplo, corresponde a 640 x 480 VGA. int far getmaxx(void); Retorna a mxima coordenada x (nmero de colunas menos um). int far getmaxy(void); Retorna a mxima coordenada y (nmero de linhas menos um).

getmaxx()

int far getx(void) Retorna a posio horizontal (coordenada x, ou coluna) atual da "caneta" na tela int far gety(void) Retorna a posio vertical (coordenada y, linha) atual da "caneta" na tela unsigned far getpixel(int x, int y); Retorna a cor do pixel especificado por x, coluna e y, linha. char *far grapherrormsg(int errorcode) Recebe como nico parmetro um nmero de cdigo que representa o erro ocorrido (este cdigo o retorno da funo graphresult), e retorna uma string, que pode ser impressa na tela para explicar o que causou o erro. int far graphresult(void) Retorna um inteiro representando uma mensagem que define a situao atual da inicializao do modo grfico. Este cdigo pode ser passado para a funo grapherrormsg para receber uma string com algum possvel erro ocorrido. void far initgraph(int far *graphdriver, int far *graphmode, char far *pathtodriver) Esta funo tenta inicializar o modo grfico de acordo com os parmetros passados. Graphdriver um ponteiro distante para inteiro que contm um nico nmero indicando qual o driver que deve ser inicializado. Existem vrios drivers j definidos na biblioteca graphics.h use a referncia da ajuda online do seu compilador para descobrir quais os drivers suportados pela sua biblioteca. De qualquer maneira, possvel usar uma opo especial chamada DETECT que tentar autodetectar o driver do seu dispositivo grfico. Graphmode um ponteiro distante para um inteiro que avisa qual a resoluo e a profundidade de cores que sero usados no modo grfico. Novamente j esto definidas possveis opes para este parmetro, portanto cheque com a ajuda do seu compilador.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 85 de 98

getmaxy()

86 Tpicos em C reviso 257 de 24/03/2004 23:16 Pathtodriver uma string que indica o caminho do arquivo *.BGI (Borland Graphics Interface) que contm o driver que a funo vai precisar para inicializar o modo grfico. void line(int x1, int y1, int x2, int y2) Desenha uma linha que vai das coordenadas da tela x1,y1 at x2,y2. void far outtext(char *far textstring) Com esta funo muito til, possvel imprimir uma string na tela do modo grfico da mesma maneira que na funo puts(). void far outtextxy(int x, int y, char *far textstring) Como na funo anterior, mas com esta possvel escolher onde exatamente o texto ser impresso, como ao usar a funo gotoxy() seguida de puts() no modo texto. void far putpixel(int x, int y, int color) Coloca um nico pixel na tela, nas coordenadas (x,y) e com a cor color. void far rectangle(int left, int top, int right, int bottom) Esta funo muito til, pois desenha um retngulo com as coordenadas passadas como parmetros, e pode ser usada para, entre outras utilidades, desenhar janelas e molduras para seus programas. void setbkcolor(int color) Designa a cor que ser usada no fundo da tela ou janela definida por setviewport. void setcolor(int color) Escolhe a cor usada para escrever, ou desenhar na tela, com funes como rectangle, outtext, circle, entre outras. void far setgraphmode(int mode) Muda o modo grfico para o parmetro mode. void far setlinestyle(int linestyle, unsigned upattern, int thickness) Determina o estilo da linha, o seu padro, e a largura do trao. Consulte a ajuda online do seu compilador para saber quais so os modos disponveis. void far settextstyle(int font, int direction, int charsize) Muda as caractersticas dos caracteres impressos por funes como outtext e outtextxy, os parmetros determinam a fonte usada (existem algumas pre-definidas pela biblioteca), a direo de escrita HORIZ_DIR da esquerda para a direita, e VERT_DIR de baixo para cima -, e o tamanho da fonte (um valor de 1 at 10). void far setviewport(int left, int top, int right, int bottom, int clip) Abre uma nova "janela" dentro da tela nas coordenadas passadas como parmetros, como na funo window() do modo texto. Observaes Finais A biblioteca grfica da Borland sem dvida mais ampla do que isso tudo. Alm disso, a criatividade do programador ainda permite criar dzias de efeitos interessantes combinando-se funes mais simples. Uma boa dica para os mais curiosos que querem ver tudo isto em prtica, este pequeno programa que acompanha os compiladores da Borland: o bgidemo.c. Ele deve ser compilado com a <graphics.h> e os arquivos BGI corretos no diretrio onde o programa executado. Vale a pena conferir o seu cdigo para entender melhor como e onde aplicar as funes desta enorme biblioteca da linguagem C.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 86 de 98

87 Tpicos em C reviso 257 de 24/03/2004 23:16

Apndice : Tabela ASCII


As tabelas mostradas neste apndice representam os 256 cdigos usados nos computadores da famlia IBM. Esta tabela refere-se ao American Standard Code for Information Interchange (cdigo padro americano para troca de informaes), que um conjunto de nmeros representando caracteres ou instrues de controle usados para troca de informaes entre computadores entre si, entre perifricos (teclado, monitor, impressora) e outros dispositivos. Estes cdigos tem tamanho de 1 byte com valores de 00h a FFh (0 a 255 decimal). Podemos dividir estes cdigos em trs conjuntos: controle, padro e estendido. Os primeiros 32 cdigos de 00h at 1Fh (0 a 31 decimal), formam o conjunto de controle ASCII. Estes cdigos so usados para controlar dispositivos, por exemplo uma impressora ou o monitor de vdeo. O cdigo 0Ch (form feed) recebido por ima impressora gera um avano de uma pgina. O cdigo 0Dh (carriage return) enviado pelo teclado quando a tecla ENTER pressionada. Embora exista um padro, alguns poucos dispositivos tratam diferentemente estes cdigos e necessrio consultar o manual para saber exatamente como o equipamento lida com o cdigo. Em alguns casos o cdigo tambm pode representar um caracter imprimvel. Por exemplo o cdigo 01h representa o caracter (happy face). Os 96 cdigos seguintes de 20h a 7Fh (32 a 127 decimal) formam o conjunto padro ASCII. Todos os computadores lidam da mesma forma com estes cdigos. Eles representam os caracteres usados na manipulao de textos: cdigos-fonte, documentos, mensagens de correio eletrnico, etc. So constitudos das letras do alfabeto latino (minsculo e maisculo) e alguns smbolos usuais. Os restantes 128 cdigos de 80h at FFh (128 a 255 decimal) formam o conjunto estendido ASCII. Estes cdigos tambm representam caracteres imprimveis porem cada fabricante decide como e quais smbolos usar. Nesta parte do cdigo esto definidas os caracteres especiais: , , , ... Dec. Hex. Controle 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0Fh 10h 11h 12h 13h 14h 15h 16h 17h 18h 19h 1Ah 1Bh 1Ch 1Dh 1Eh NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS (Null) (Start of Heading) (Start of Text) (End of Text)

(End of Transmision) (Enquiry) (Acknowledge) (Bell) (Backspace) (Horizontal Tab) (Line Feed) (Vertical Tab) (Form Feed) (Carriage Return) (Shift Out) (Shift In) (Data Link Escape) (Device control 1) (Device control 2) (Device control 3) (Device control 4) (Negative Acknowledge) (Synchronous Idle) (End Transmission Block) (Cancel) (End of Media) (Substitute) (Escape) (File Separator) (Group Separator) (Record Separator)

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 87 de 98

88 Tpicos em C reviso 257 de 24/03/2004 23:16 31 1Fh US (Unit Separator)

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 88 de 98

89 Tpicos em C reviso 257 de 24/03/2004 23:16

Caracter
<espao>

Dec.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

Hex.
20h 21h 22h 23h 24h 25h 26h 27h 28h 29h 2Ah 2Bh 2Ch 2Dh 2Eh 2Fh 30h 31h 32h 33h 34h 35h 36h 37h 38h 39h 3Ah 3Bh 3Ch 3Dh 3Eh 3Fh 40h 41h 42h

C
Caracter

67

43h

f g
Caracter

102 103

66h 67h

Dec.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

Hex.
44h 45h 46h 47h 48h 49h 4Ah 4Bh 4Ch 4Dh 4Eh 4Fh 50h 51h 52h 53h 54h 55h 56h 57h 58h 59h 5Ah 5Bh 5Ch 5Dh 5Eh 5Fh 60h 61h 62h 63h 64h 65h

! " # $ % & ' ( ) * + , . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B

D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e

Dec.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

Hex.
68h 69h 6Ah 6Bh 6Ch 6Dh 6Eh 6Fh 70h 71h 72h 73h 74h 75h 76h 77h 78h 79h 7Ah 7Bh 7Ch 7Dh 7Eh 7Fh 80h 81h 82h 83h 84h 85h 86h 87h 88h

h i j k l m n o p q r s t u v w x y z { | } ~
<delete>

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 89 de 98

90 Tpicos em C reviso 257 de 24/03/2004 23:16


Caracter

137 138 139

89h 8Ah 8Bh


Caracter

173 174 175 176

ADh AEh AFh B0h


Caracter

209 210 211 212 213

D1h D2h D3h D4h D5h

Dec.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

Hex.
8Ch 8Dh 8Eh 8Fh 90h 91h 92h 93h 94h 95h 96h 97h 98h 99h 9Ah 9Bh 9Ch 9Dh 9Eh 9Fh A0h A1h A2h A3h A4h A5h A6h A7h A8h A9h AAh ABh ACh

Dec.
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208

Hex.
B1h B2h B3h B4h B5h B6h B7h B8h B9h BAh BBh BCh BDh BEh BFh C0h C1h C2h C3h C4h C5h C6h C7h C8h C9h CAh CBh CCh CDh CEh CFh DOh

Dec.
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

Hex.
D6h D7h D8h D9h DAh DBh DCh DDh DEh DFh E0h E1h E2h E3h E4h E5h E6h E7h E8h E9h EAh EBh ECh EDh EEh EFh F0h F1h F2h F3h F4h

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 90 de 98

91 Tpicos em C reviso 257 de 24/03/2004 23:16


Caracter

245 246 247 248 249 250

F5h F6h F7h F8h F9h FAh

Dec.
251 252 253 254 255

Hex.
FBh FCh FDh FEh FFh

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 91 de 98

92 Tpicos em C reviso 257 de 24/03/2004 23:16

Entre os caracteres da tabela ASCII estendidos os mais teis esto, talvez, os caracteres de desenho de quadro em linhas simples e duplas: os caracteres de B3h at DAh (179 a 218 decimal). Como a visualizao deste conjunto difcil, o desenho abaixo pode auxiliar nesta tarefa: 218 179 195 192 196 194 197 193 209 216 207 191 180 217 201 186 204 200 205 203 206 202 210 215 208 187 185 188

213 198 212

184 181 190

214 199 211

183 182 189

Caracteres de desenho de quadro e seus respectivos cdigos ASCII.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 92 de 98

93 Tpicos em C reviso 257 de 24/03/2004 23:16

Referncias Bibliogrficas
1. 2. 3.
KERNIGHAN, Brian W., PIKE, Rob. A Prtica da Programao. Rio de Janeiro: Campus, 2000. nd B. W. KERNIGHAN ; RITCHIE, D. M. The C Programming Language. 2 ed. Prentice-Hall: Englewood Cliffs, NJ, 1988. SCHILDT, Herbert. C - Completo e Total. So Paulo: Makron Books, 1990.

Prof. Fabio Alexandre Spanhol, M.Sc. Pgina 93 de 98

You might also like