Escolar Documentos
Profissional Documentos
Cultura Documentos
A partir desses dois arquivos fontes, podemos gerar um programa executvel compilando cada um dos arquivos separadamente e depois ligando-os em um nico
Estruturas de Dados PUC-Rio 9-1
arquivo executvel. Por exemplo, com o compilador Gnu C (gcc) utilizaramos a seguinte seqncia de comandos para gerar o arquivo executvel prog1.exe:
> gcc c str.c > gcc c prog1.c > gcc o prog1.exe str.o prog1.o
O mesmo arquivo str.c pode ser usado para compor outros programas que queiram utilizar suas funes. Para que as funes implementadas em str.c possam ser usadas por um outro mdulo C, este precisa conhecer os cabealhos das funes oferecidas por str.c . No exemplo anterior, isso foi resolvido pela repetio dos cabealhos das funes no incio do arquivo prog1.c. Entretanto, para mdulos que ofeream vrias funes ou que queiram usar funes de muitos outros mdulos, essa repetio manual pode ficar muito trabalhosa e sensvel a erros. Para contornar esse problema, todo mdulo de funes C costuma ter associado a ele um arquivo que contm apenas os cabealhos das funes oferecidas pelo mdulo e, eventualmente, os tipos de dados que ele exporte (typedefs, structs, etc). Esse arquivo de cabealhos segue o mesmo nome do mdulo ao qual est associado, s que com a extenso .h. Assim, poderamos definir um arquivo str.h para o mdulo do exemplo anterior, com o seguinte contedo:
/* Funes oferecidas pelo modulo str.c */ /* Funo comprimento ** Retorna o nmero de caracteres da string passada como parmetro */ int comprimento (char* str); /* Funo copia ** Copia os caracteres da string orig (origem) para dest (destino) */ void copia (char* dest, char* orig); /* Funo concatena ** Concatena a string orig (origem) na string dest (destino) */ void concatena (char* dest, char* orig);
Observe que colocamos vrios comentrios no arquivo str.h. Isso uma prtica muito comum, e tem como finalidade documentar as funes oferecidas por um mdulo. Esses comentrios devem esclarecer qual o comportamento esperado das funes exportadas por um mdulo, facilitando o seu uso por outros programadores (ou pelo mesmo programador algum tempo depois da criao do mdulo). Agora, ao invs de repetir manualmente os cabealhos dessas funes, todo mdulo que quiser usar as funes de str.c precisa apenas incluir o arquivo str.h. No exemplo anterior, o mdulo prog1.c poderia ser simplificado da seguinte forma:
#include <stdio.h> #include "str.h" int main (void) { char str[101], str1[51], str2[51]; printf("Entre com uma seqncia de caracteres: "); scanf(" %50[^\n]", str1); printf("Entre com outra seqncia de caracteres: "); scanf(" %50[^\n]", str2);
Estruturas de Dados PUC-Rio 9-2
Note que os arquivos de cabealhos das funes da biblioteca padro do C (que acompanham seu compilador) so includos da forma #include <arquivo.h>, enquanto que os arquivos de cabealhos dos seus mdulos so geralmente includos da forma #include "arquivo.h". O uso dos delimitadores < > e " " indica para o compilador onde ele deve procurar esses arquivos de cabealhos durante a compilao.
9-3
Arquivo ponto.h:
/* TAD: Ponto (x,y) */ /* Tipo exportado */ typedef struct ponto Ponto; /* Funes exportadas */ /* Funo cria ** Aloca e retorna um ponto com coordenadas (x,y) */ Ponto* cria (float x, float y); /* Funo libera ** Libera a memria de um ponto previamente criado. */ void libera (Ponto* p); /* Funo acessa ** Devolve os valores das coordenadas de um ponto */ void acessa (Ponto* p, float* x, float* y); /* Funo atribui ** Atribui novos valores s coordenadas de um ponto */ void atribui (Ponto* p, float x, float y); /* Funo distancia ** Retorna a distncia entre dois pontos */ float distancia (Ponto* p1, Ponto* p2);
Note que a composio da estrutura Ponto (struct ponto) no exportada pelo mdulo. Dessa forma, os demais mdulos que usarem esse TAD no podero acessar diretamente os campos dessa estrutura. Os clientes desse TAD s tero acesso s informaes que possam ser obtidas atravs das funes exportadas pelo arquivo ponto.h. Agora, mostraremos uma implementao para esse tipo abstrato de dados. O arquivo de implementao do mdulo (arquivo ponto.c ) deve sempre incluir o arquivo de interface do mdulo. Isto necessrio por duas razes. Primeiro, podem existir definies na interface que so necessrias na implementao. No nosso caso, por exemplo, precisamos da definio do tipo Ponto. A segunda razo garantirmos que as funes implementadas correspondem s funes da interface. Como o prottipo das funes exportadas includo, o compilador verifica, por exemplo, se os parmetros das funes implementadas equivalem aos parmetros dos prottipos. Alm da prpria interface, precisamos naturalmente incluir as interfaces das funes que usamos da biblioteca padro.
#include #include #include #include <stdlib.h> <stdio.h> <math.h> "ponto.h" /* malloc, free, exit */ /* printf */ /* sqrt */
9-4
Como s precisamos guardar as coordenadas de um ponto, podemos definir a estrutura ponto da seguinte forma:
struct ponto { float x; float y; };
A funo que cria um ponto dinamicamente deve alocar a estrutura que representa o ponto e inicializar os seus campos:
Ponto* cria (float x, float y) { Ponto* p = (Ponto*) malloc(sizeof(Ponto)); if (p == NULL) { printf("Memria insuficiente!\n"); exit(1); } p->x = x; p->y = y; return p; }
Para esse TAD, a funo que libera um ponto deve apenas liberar a estrutura que foi criada dinamicamente atravs da funo cria:
void libera (Ponto* p) { free(p); }
As funes para acessar e atribuir valores s coordenadas de um ponto so de fcil implementao, como pode ser visto a seguir.
void acessa (Ponto* p, float* x, float* y) { *x = p->x; *y = p->y; } void atribui (Ponto* p, float x, float y) { p->x = x; p->y = y; }
J a operao para calcular a distncia entre dois pontos pode ser implementada da seguinte forma:
float distancia (Ponto* p1, Ponto* p2) { float dx = p2->x p1->x; float dy = p2->y p1->y; return sqrt(dx*dx + dy*dy); }
Exerccio: Escreva um programa que faa uso do TAD ponto definido acima. Exerccio: Acrescente novas operaes ao TAD ponto, tais como soma e subtrao de pontos.
9-5
Exerccio: Acrescente novas operaes ao TAD ponto, de tal forma que seja possvel obter uma representao do ponto em coordenadas polares. Exerccio: Implemente um novo TAD para representar pontos no R3. Exemplo 2: TAD Matriz Como foi discutido anteriormente, a implementao de um TAD fica escondida dentro de seu mdulo. Assim, podemos experimentar diferentes maneiras de implementar um mesmo TAD, sem que isso afete os seus clientes. Para ilustrar essa independncia de implementao, vamos considerar a criao de um tipo abstrato de dados para representar matrizes de valores reais alocadas dinamicamente, com dimenses m por n fornecidas em tempo de execuo. Para tanto, devemos definir um tipo abstrato, que denominaremos de Matriz , e o conjunto de funes que operam sobre esse tipo. Neste exemplo, vamos considerar as seguintes operaes: cria: operao que cria uma matriz de dimenso m por n; libera: operao que libera a memria alocada para a matriz; acessa: operao que acessa o elemento da linha i e da coluna j da matriz; atribui: operao que atribui o elemento da linha i e da coluna j da matriz; linhas: operao que devolve o nmero de linhas da matriz; colunas: operao que devolve o nmero de colunas da matriz. A interface do mdulo pode ser dada pelo cdigo abaixo: Arquivo matriz.h:
/* TAD: matriz m por n */ /* Tipo exportado */ typedef struct matriz Matriz; /* Funes exportadas */ /* Funo cria ** Aloca e retorna uma matriz de dimenso m por n */ Matriz* cria (int m, int n); /* Funo libera ** Libera a memria de uma matriz previamente criada. */ void libera (Matriz* mat); /* Funo acessa ** Retorna o valor do elemento da linha i e coluna j da matriz */ float acessa (Matriz* mat, int i, int j); /* Funo atribui ** Atribui o valor dado ao elemento da linha i e coluna j da matriz */ void atribui (Matriz* mat, int i, int j, float v); /* Funo linhas ** Retorna o nmero de linhas da matriz */ int linhas (Matriz* mat);
Estruturas de Dados PUC-Rio 9-6
/* Funo colunas ** Retorna o nmero de colunas da matriz */ int colunas (Matriz* mat);
A seguir, mostraremos a implementao deste tipo abstrato usando as duas estratgias apresentadas no captulo 8: matrizes dinmicas representadas por vetores simples e matrizes dinmicas representadas por vetores de ponteiros. A interface do mdulo independe da estratgia de implementao adotada, o que altamente desejvel, pois podemos mudar a implementao sem afetar as aplicaes que fazem uso do tipo abstrato. O arquivo matriz1.c apresenta a implementao atravs de vetor simples e o arquivo matriz2.c apresenta a implementao atravs de vetor de ponteiros. Arquivo matriz1.c:
#include <stdlib.h> #include <stdio.h> #include "matriz.h" struct matriz { int lin; int col; float* v; }; Matriz* cria (int m, int n) { Matriz* mat = (Matriz*) malloc(sizeof(Matriz)); if (mat == NULL) { printf("Memria insuficiente!\n"); exit(1); } mat->lin = m; mat->col = n; mat->v = (float*) malloc(m*n*sizeof(float)); return mat; } void libera (Matriz* mat){ free(mat->v); free(mat); } float acessa (Matriz* mat, int i, int j) { int k; /* ndice do elemento no vetor */ if (i<0 || i>=mat->lin || j<0 || j>=mat->col) { printf("Acesso invlido!\n"); exit(1); } k = i*mat->col + j; return mat->v[k]; } void atribui (Matriz* mat, int i, int j, float v) { int k; /* ndice do elemento no vetor */ if (i<0 || i>=mat->lin || j<0 || j>=mat->col) { printf("Atribuio invlida!\n"); exit(1); }
Estruturas de Dados PUC-Rio 9-7
k = i*mat->col + j; mat->v[k] = v; } int linhas (Matriz* mat) { return mat->lin; } int colunas (Matriz* mat) { return mat->col; }
Arquivo matriz2.c:
#include <stdlib.h> #include <stdio.h> #include "matriz.h" struct matriz { int lin; int col; float** v; }; Matriz* cria (int m, int n) { int i; Matriz* mat = (Matriz*) malloc(sizeof(Matriz)); if (mat == NULL) { printf("Memria insuficiente!\n"); exit(1); } mat->lin = m; mat->col = n; mat->v = (float**) malloc(m*sizeof(float*)); for (i=0; i<m; i++) mat->v[i] = (float*) malloc(n*sizeof(float)); return mat; } void libera (Matriz* mat) { int i; for (i=0; i<mat->lin; i++) free(mat->v[i]); free(mat->v); free(mat); } float acessa (Matriz* mat, int i, int j) { if (i<0 || i>=mat->lin || j<0 || j>=mat->col) { printf("Acesso invlido!\n"); exit(1); } return mat->v[i][j]; } void atribui (Matriz* mat, int i, int j, float v) { if (i<0 || i>=mat->lin || j<0 || j>=mat->col) { printf("Atribuio invlida!\n"); exit(1); } mat->v[i][j] = v; } int linhas (Matriz* mat) { return mat->lin; } /* malloc, free, exit */ /* printf */
9-8
Exerccio: Escreva um programa que faa uso do TAD matriz definido acima. Teste o seu programa com as duas implementaes vistas. Exerccio: Usando apenas as operaes definidas pelo TAD matriz, implemente uma funo que determine se uma matriz ou no quadrada simtrica. Exerccio: Usando apenas as operaes definidas pelo TAD matriz, implemente uma funo que, dada uma matriz, crie dinamicamente a matriz transposta correspondente. Exerccio: Defina um TAD para implementar matrizes quadradas simtricas, de acordo com a representao sugerida no captulo 8.
9-9