Você está na página 1de 8

RESUMO INTRODUO ESTRUTURA DE DADOS

Para a representao de vetores de cadeias de caracteres, optamos, em geral,


por declarar um vetor de ponteiros e alocar dinamicamente cada elemento (no caso, uma
cadeia de caracteres). Desta forma, otimizamos o uso do espao de memria, pois no
precisamos achar uma dimenso mxima para todas as cadeias do vetor nem desperdiamos
espao excessivo quando temos poucos nomes de alunos a serem armazenados. Cada
elemento do vetor um ponteiro. Se precisarmos armazenar um nome na posio, alocamos
o espao de memria necessrio para armazenar a cadeia de caracteres correspondente.
Assim, nosso vetor com os nomes dos alunos pode ser declarado da seguinte forma:
#define MAX 50
char* alunos[MAX];

Exemplo. Leitura e impresso dos nomes dos alunos.


Vamos escrever uma funo que captura os nomes dos alunos de uma turma. A funo
inicialmente l o nmero de alunos da turma (que deve ser menor ou igual a MAX) e captura
os nomes fornecidos por linha, fazendo a alocao correspondente. Para escrever esta
funo, podemos pensar numa funo auxiliar que captura uma linha e fornece como
retorno uma cadeia alocada dinamicamente com a linha inserida. Fazendo uso das funes
que escrevemos acima, podemos ter:
char* lelinha (void)
{
char linha[121];
/* variavel auxiliar para ler linha */
scanf(" %120[^\n]",linha);
return duplica(linha);
}

A funo para capturar os nomes dos alunos preenche o vetor de nomes e pode ter como
valor de retorno o nmero de nomes lidos:
int lenomes (char** alunos)
{
int i;
int n;
do {
scanf("%d",&n);
} while (n>MAX);

for (i=0; i<n; i++)


alunos[i] = lelinha();
return n;

A funo para liberar os nomes alocados na tabela pode ser implementada por:
void liberanomes (int n, char** alunos)
{
int i;
for (i=0; i<n; i++)
free(alunos[i]);
}

Uma funo para imprimir os nomes dos alunos pode ser dada por:
void imprimenomes (int n, char** alunos)
{
int i;
for (i=0; i<n; i++)
printf("%s\n", alunos[i]);
}

Um programa que faz uso destas funes mostrado a seguir:


int main (void)
{
char* alunos[MAX];
int n = lenomes(alunos);
imprimenomes(n,alunos);
liberanomes(n,alunos);
return 0;
}

Parmetros da funo main**


Em todos os exemplos mostrados, temos considerado que a funo principal, main, no
recebe parmetros. Na verdade, a funo main pode ser definida para receber zero ou dois
parmetros, geralmente chamados argc e argv. O parmetro argc recebe o nmero de
argumentos passados para o programa quando este executado; por exemplo, de um
comando de linha do sistema operacional. O parmetro argv um vetor de cadeias de
caracteres, que armazena os nomes passados como argumentos. Por exemplo, se temos um
programa executvel com o nome mensagem e se ele for invocado atravs da linha de
comando:
> mensagem estruturas de dados

a varivel argc receber o valor 4 e o vetor argv ser inicializado com os seguintes
elementos: argv[0]="mensagem", argv[1]="estruturas", argv[2]="de", e
argv[3]="dados". Isto , o primeiro elemento armazena o prprio nome do executvel e
os demais so preenchidos com os nomes passados na linha de comando. Esses parmetros
podem ser teis para, por exemplo, passar o nome de um arquivo de onde sero capturados
os dados de um programa. A manipulao de arquivos ser discutida mais adiante no curso.
Por ora, mostramos um exemplo simples que trata os dois parmetros da funo main.
#include <stdio.h>
int main (int argc, char** argv)
{
int i;
for (i=0; i<argc; i++)
printf("%s\n", argv[i]);
return 0;
}

Se este programa tiver seu executvel chamado de mensagem e for invocado com a linha
de comando mostrada acima, a sada ser:
mensagem
estruturas
de
dados

Na linguagem C, existem os tipos bsicos (char, int, float, etc.) e seus respectivos
ponteiros que podem ser usados na declarao de variveis. Para estruturar dados
complexos, nos quais as informaes so compostas por diversos campos, necessitamos de
mecanismos que nos permitam agrupar tipos distintos. Neste captulo, apresentaremos os
mecanismos fundamentais da linguagem C para a estruturao de tipos.

O tipo estrutura
Em C, podemos definir um tipo de dado cujos campos so compostos de vrios valores de
tipos mais simples. Para ilustrar, vamos considerar o desenvolvimento de programas que
manipulam pontos no plano cartesiano. Cada ponto pode ser representado por suas
coordenadas x e y, ambas dadas por valores reais. Sem um mecanismo para agrupar as duas
componentes, teramos que representar cada ponto por duas variveis independentes.
float x;
float y;

No entanto, deste modo, os dois valores ficam dissociados e, no caso do programa


manipular vrios pontos, cabe ao programador no misturar a coordenada x de um ponto
com a coordenada y de outro. Para facilitar este trabalho, a linguagem C oferece recursos
para agruparmos dados. Uma estrutura, em C, serve basicamente para agrupar diversas
variveis dentro de um nico contexto. No nosso exemplo, podemos definir uma estrutura
ponto que contenha as duas variveis. A sintaxe para a definio de uma estrutura
mostrada abaixo:
struct ponto {
float x;
float y;
};

Desta forma, a estrutura ponto passa a ser um tipo e podemos ento declarar variveis
deste tipo.
struct ponto p;

Esta linha de cdigo declara p como sendo uma varivel do tipo struct ponto. Os
elementos de uma estrutura podem ser acessados atravs do operador de acesso
ponto (.). Assim, vlido escrever:
ponto.x = 10.0;
ponto.y = 5.0;

Manipulamos os elementos de uma estrutura da mesma forma que variveis simples.


Podemos acessar seus valores, atribuir-lhes novos valores, acessar seus endereos, etc.

Exemplo: Capturar e imprimir as coordenadas de um ponto.


Para exemplificar o uso de estruturas em programas, vamos considerar um exemplo simples
em que capturamos e imprimimos as coordenadas de um ponto qualquer.
/* Captura e imprime as coordenadas de um ponto qualquer */
#include <stdio.h>
struct ponto {
float x;
float y;
};
int main (void)
{
struct ponto p;

printf("Digite as coordenadas do ponto(x y): ");


scanf("%f %f", &p.x, &p.y);
printf("O ponto fornecido foi: (%.2f,%.2f)\n", p.x, p.y);
return 0;

A varivel p, definida dentro de main, uma varivel local como outra qualquer. Quando a
declarao encontrada, aloca-se, na pilha de execuo, um espao para seu
armazenamento, isto , um espao suficiente para armazenar todos os campos da estrutura
(no caso, dois nmeros reais). Notamos que o acesso ao endereo de um campo da estrutura
feito da mesma forma que com variveis simples: basta escrever &(p.x), ou
simplesmente &p.x, pois o operador de acesso ao campo da estrutura tem precedncia
sobre o operador endereo de.
Ponteiro para estruturas
Da mesma forma que podemos declarar variveis do tipo estrutura:
struct ponto p;

podemos tambm declarar variveis do tipo ponteiro para estrutura:


struct ponto *pp;

Se a varivel pp armazenar o endereo de uma estrutura, podemos acessar os campos dessa


estrutura indiretamente, atravs de seu ponteiro:
(*pp).x = 12.0;

Neste caso, os parnteses so indispensveis, pois o operador contedo de tem


precedncia menor que o operador de acesso. O acesso de campos de estruturas to
comum em programas C que a linguagem oferece outro operador de acesso, que permite
acessar campos a partir do ponteiro da estrutura. Este operador composto por um trao
seguido de um sinal de maior, formando uma seta (->). Portanto, podemos reescrever a
atribuio anterior fazendo:

pp->x = 12.0;

Em resumo, se temos uma varivel estrutura e queremos acessar seus campos, usamos o
operador de acesso ponto (p.x); se temos uma varivel ponteiro para estrutura, usamos o
operador de acesso seta (pp->x). Seguindo o raciocnio, se temos o ponteiro e queremos
acessar o endereo de um campo, fazemos &pp->x!
Passagem de estruturas para funes
Para exemplificar a passagem de variveis do tipo estrutura para funes, podemos
reescrever o programa simples, mostrado anteriormente, que captura e imprime as
coordenadas de um ponto qualquer. Inicialmente, podemos pensar em escrever uma funo
que imprima as coordenadas do ponto. Esta funo poderia ser dada por:
void imprime (struct ponto p)
{
printf("O ponto fornecido foi: (%.2f,%.2f)\n", p.x, p.y);
}

A passagem de estruturas para funes se processa de forma anloga passagem de


variveis simples, porm exige uma anlise mais detalhada. Da forma como est escrita no
cdigo acima, a funo recebe uma estrutura inteira como parmetro. Portanto, faz-se uma
cpia de toda a estrutura para a pilha e a funo acessa os dados desta cpia. Existem dois
pontos a serem ressaltados. Primeiro, como em toda passagem por valor, a funo no tem
como alterar os valores dos elementos da estrutura original (na funo imprime isso
realmente no necessrio, mas seria numa funo de leitura). O segundo ponto diz
respeito eficincia, visto que copiar uma estrutura inteira para a pilha pode ser uma
operao custosa (principalmente se a estrutura for muito grande). mais conveniente
passar apenas o ponteiro da estrutura, mesmo que no seja necessrio alterar os valores dos
elementos dentro da funo, pois copiar um ponteiro para a pilha muito mais eficiente do
que copiar uma estrutura inteira. Um ponteiro ocupa em geral 4 bytes, enquanto uma
estrutura pode ser definida com um tamanho muito grande. Desta forma, uma segunda (e
mais adequada) alternativa para escrevermos a funo imprime :
void imprime (struct ponto* pp)
{
printf("O ponto fornecido foi: (%.2f,%.2f)\n", pp->x, pp->y);
}

Podemos ainda pensar numa funo para ler a hora do evento. Observamos que, neste caso,
obrigatoriamente devemos passar o ponteiro da estrutura, caso contrrio no seria possvel
passar ao programa principal os dados lidos:
void captura (struct ponto* pp)
{
printf("Digite as coordenadas do ponto(x y): ");
scanf("%f %f", &p->x, &p->y);
}

Com estas funes, nossa funo main ficaria como mostrado abaixo.
int main (void)
{
struct ponto p;
captura(&p);
imprime(&p);
return 0;
}

Exerccio: Funo para determinar a distncia entre dois pontos.


Considere a implementao de uma funo que tenha como valor de retorno a distncia
entre dois pontos. O prottipo da funo pode ser dado por:
float distancia (struct ponto *p, struct ponto *q);

Nota: A distncia entre dois pontos dada por: d = ( x2 x1 ) 2 + ( y2 y1 ) 2


Alocao dinmica de estruturas
Da mesma forma que os vetores, as estruturas podem ser alocadas dinamicamente. Por
exemplo, vlido escrever:
struct ponto* p;
p = (struct ponto*) malloc (sizeof(struct ponto));

Neste fragmento de cdigo, o tamanho do espao de memria alocado dinamicamente


dado pelo operador sizeof aplicado sobre o tipo estrutura (sizeof(struct ponto)). A
funo malloc retorna o endereo do espao alocado, que ento convertido para o tipo
ponteiro da estrutura ponto.
Aps uma alocao dinmica, podemos acessar normalmente os campos da estrutura,
atravs da varivel ponteiro que armazena seu endereo:
...
p->x = 12.0;
...

Definio de "novos" tipos


A linguagem C permite criar nomes de tipos. Por exemplo, se escrevermos:
typedef float Real;

podemos usar o nome Real como um mnemnico para o tipo float. O uso de typedef
muito til para abreviarmos nomes de tipos e para tratarmos tipos complexos. Alguns
exemplos vlidos de typedef:
typedef unsigned char UChar;
typedef int* PInt;
typedef float Vetor[4];

OPERAES BSICAS
A seguir, apresentaremos a estrutura de dados e as operaes bsicas sobre listas, implementadas na linguagem C.
Definio da ED:
#define MAX ________

/* tamanho mximo da lista */

typedef ______ telem;


typedef struct {
telem v[MAX];
int n;
} tlista;

/* tipo base dos elementos da lista */


/* vetor que contm a lista */
/* posio do ltimo elemento da lista */
/* tipo lista */

tlista
n

Pato

cabra

rato

anta

macaco

...
5

Operaes simples utilizando lista sequencial:


1) Criar uma lista vazia
void criar (tlista *L) {
L->n = 0;
}
2) Verificar se uma lista est vazia
int vazia (tlista L) {
return (L.n == 0);
}
3) Verificar se uma lista est cheia
int cheia (tlista L) {
return (L.n == MAX);
}
4) Obter o tamanho de uma lista
int tamanho (tlista L) {
return (L.n);
}
5) Obter o i-simo elemento de uma lista
int elemento (tlista L, int pos, telem *dado) {
/* O parmetro dado ir receber o elemento encontrado */
/* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */
if ( (pos > L.n) || (pos <= 0) ) return (0);
*dado = L.v[pos-1];
return (1);
}

MAX-1

6) Pesquisar um dado elemento, retornando a sua posio


int posicao (tlista L, telem dado) {
/* Retorna a posio do elemento ou 0 caso no seja encontrado */
int i;
for (i=1; i<=L.n; i++)
if (L.v[i-1] == dado)
return (i);
return (0);
}
7) Insero de um elemento em uma determinada posio
Requer o deslocamento direita dos elementos v(i+1)...v(n)
int inserir (tlista *L, int pos, telem dado) {
/* Retorna 0 se a posio for invlida ou se a lista estiver cheia */
/* Caso contrrio, retorna 1 */
int i;
if ( (L->n == MAX)) || (pos > L->n + 1) )

return (0);

for (i=L->n; i>=pos; i--)


L->v[i] = L->v[i-1];
L->v[pos-1] = dado;
(L->n)++;
return (1);
}
8) Remoo do elemento de uma determinada posio
Requer o deslocamento esquerda dos elementos v(p+1)...v(n)
int remover (tlista *L, int pos, telem *dado) {
/* O parmetro dado ir receber o elemento encontrado */
/* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */
int i;
if ( (pos > L->n) || (pos <= 0) ) return (0);
*dado = L->v[pos-1];
for (i=pos; i<=(L->n)-1; i++)
L->v[i-1] = L->v[i];
(L->n)--;
return (1);
}

Você também pode gostar