You are on page 1of 34

AEDsII

Material de uso restrito para a Disciplina de Algoritmos e Estruturas de Dados II do Curso de Engenharia Eltrica do UNI-BH

Prof. Eduardo de Queiroz Braga

ndice:
ndice: ------------------------------------------------------------------------------------------------------------ 2 1. Ponteiros ------------------------------------------------------------------------------------------------ 3 1.1. Operaes com Ponteiros------------------------------------------------------------------------ 4 1.2. Aritmtica de Ponteiros --------------------------------------------------------------------------- 5 1.3. Acessando os Endereos ------------------------------------------------------------------------ 5 1.4. Ponteiros e Vetores -------------------------------------------------------------------------------- 9 1.5. Ponteiros e Matrizes ------------------------------------------------------------------------------ 11 1.6. Ponteiros e Strings -------------------------------------------------------------------------------- 14 1.7. Estruturas e Ponteiros para Estruturas------------------------------------------------------- 15 1.8. Ponteiros para Funes ------------------------------------------------------------------------- 24 2. A Funo main() e seus argumentos ------------------------------------------------------------ 25 2.1. A funo void main(int argc, char *argv[]) -------------------------------------------------- 25 3. Alocao Dinmica de Memria ------------------------------------------------------------------ 26 4. Algoritmos de Ordenao e Pesquisa----------------------------------------------------------- 28 4.2. Ordenao Bolha ---------------------------------------------------------------------------------- 28 4.3. Ordenao por seleo -------------------------------------------------------------------------- 28 4.4. Ordenao por insero ------------------------------------------------------------------------- 29 4.5. Ordenao Quicksort ----------------------------------------------------------------------------- 30 5. ESTRUTURAS DE DADOS ----------------------------------------------------------------------- 32 5.1. Filas -------------------------------------------------------------------------------------------------- 32 5.2. Filas Circulares ------------------------------------------------------------------------------------ 34

1. Ponteiros
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 seqncia 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 Tabela 1 mostra um exemplo de um trecho de memria que contm duas variveis num e 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/C++ poderia ser: long int num=10, res=120; TABELA 1: Mapa de Memria Endereos (em decimal) Contedo Varivel 996 1000 1004 --10 120 --num res

Uma funo pode receber os ponteiros que apontem para os endereos dos parmetros. Assim, esta funo pode modificar diretamente os contedos destas variveis. Ou seja, ponteiros fornecem os meios pelos quais as funes podem modificar seus argumentos. Uma aplicao importante de ponteiros apontar para reas de memria que so administradas 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. Ponteiros so um dos aspectos mais fortes e mais perigosos de C/C++. Por exemplo, ponteiros no inicializados, tambm chamados de ponteiros selvagens, podem provocar uma quebra no sistema.

1.1. Operaes com Ponteiros


1.1.1. Declarao de Ponteiros
Antes de serem usados os ponteiros precisam ser declarados, assim como variveis. A forma geral da declarao de um ponteiro a seguinte:
tipo *nome;

Onde tipo qualquer tipo vlido em C/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 */

1.1.2.

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 1. Aps a execuo do trecho de programa abaixo a varivel ponteiro p termina com o valor 1000.

p = #

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 * 4

unrio e, como o operador &, tem precedncia maior que do que todos os operadores aritmticos. Observao: Nunca acesse o contedo de um ponteiro antes de inicializ-lo.

1.2. Aritmtica de Ponteiros


Atribuio de Ponteiros Do mesmo modo que uma varivel comum, contedo de um ponteiro pode ser passado para outro ponteiro do mesmo tipo. As variveis ponteiros 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/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 cout imprimem os valores apontados pelos ponteiros respectivos.
#include<iostream> #include<cstdlib> using namespace std; int main() { int vetor[] = { 10, 20, 30, 40, 50 }; int *p1, *p2; int i = 100; p1 = &vetor[2]; cout<<"*p1 = "<<*p1<<endl; p2 = &i; cout<<"*p2 = "<<*p2<<endl; p2 = p1; cout<<"*p2 = "<<*p2<<endl; system("Pause"); return 0; }

1.3. Acessando os Endereos


O programa abaixo faz com que o endereo da varivel x seja carregado no ponteiro p. Em seguida o programa imprime o que est apontado por p. 5

#include<iostream> #include<cstdlib> using namespace std; int main() { float x=3.14, *p; p = &x; //Ponteiro que carrega o endereo da varivel x cout<<"*p = "<<*p<<endl;; system("Pause"); return 0; }

1.3.1.

Incrementando e Decrementando Ponteiros

O exemplo abaixo mostra que operaes de incremento e decremento podem ser aplicadas em operandos. O primeiro cout imprime 30 o segundo 40 e o terceiro 50.
int main() { int vetor[] = { 10, 20, 30, 40, 50 }; int *p1; p1 = &vetor[2]; cout<<*p1<<endl; p1++; cout<<*p1<<endl; p1 = p1 + 1; cout<<*p1<<endl; system("Pause"); return 0; }

Pode parecer estranho que um endereo que aponte para um nmero inteiro, que armazenado em quatro bytes, seja incrementado por um e passe para apontar para o prximo nmero inteiro. A resposta para isto que sempre que um ponteiro incrementado (ou decrementado) ele passa a apontar para a posio do elemento seguinte (ou 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.
int main() { 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 cout<<"Diferenca entre ponteiros %d\n"<<p1-p2; system("Pause"); return 0; }

Observao: No possvel multiplicar ou dividir ponteiros, e no se pode adicionar ou subtrair o tipo float ou o tipo double a ponteiros.

1.3.2.

Comparao de Ponteiros

possvel comparar ponteiros em uma expresso relacional. Mas, s podemos comparar ponteiros de mesmo tipo. O trecho de programa abaixo ilustra um exemplo deste tipo de operaes. char *c, *v;
cin>>*c>>*v; if (c == v) // Cuidado com esse tipo de comparao!!! cout<<"As variveis estao na mesma posicao.\n"; else cout<<"As variaveis nao estao na mesma posicao.\n";

1.3.3.

Indireo Multipla

Ponteiros podem apontar para outro ponteiro que aponta para o valor final. Essa situao chamada de indireo mltipla, ou tambm de ponteiros para ponteiros. O valor de um ponteiro convencional o endereo de uma varivel que contm um determinado valor. J para o caso de um ponteiro para ponteiro, o primeiro ponteiro 7

contm o endereo do segundo ponteiro, que finalmente contm o endereo da varivel que contm o valor desejado. Observe a figura.
Ponteiro Varivel

Endereo
Ponteiro

Valor
Ponteiro

Indireo Simples
Varivel

Endereo

Endereo

Valor

Indireo Mltipla

Exemplo: Observe o cdigo abaixo: #include <stdio.h> Void main(void) { int x, *p, **q; x=10; p=&x; q=&p; cout < O valor de x eh <<**q; } Observamos que o valor 10 atribudo varivel x. J p um ponteiro do tipo int que passa a conter o endereo da varivel x. Por outro lado, q um ponteiro do tipo int que contm o endereo do ponteiro p. Isso um caso de indireo mltipla.

1.4. Ponteiros e Vetores


Ponteiros e Vetores esto fortemente relacionados na linguagem C/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. Se p recebe o ponteiro para a primeira posio de v como na declarao abaixo:
int *p=v;

As seguintes declaraes sero idnticas:


v[i] == *(p+i) &v[i] == p+i

Tambm so idnticas as prximas declaraes:

v[i] == *(v+i) &v[i] == v+i

O exemplo ilustrado abaixo mostra as duas notaes sendo usadas para imprimir o mesmo vetor.
int main() { 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++) cout<<v[i]; cout<<endl; for (i=0; i<9; i++) cout<<*(v+i); cout<<endl; system("Pause"); return 0; }

Existe uma diferena fundamental entre declarar um conjunto de dados como um vetor ou atravs de um ponteiro. Na declarao de vetor, o compilador automaticamente reserva um 9

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:
int main() { int list[5], i; list = &i; // O ponteiro list nao pode ser modificado //recebendo o endereco de i // O ponteiro list nao pode ser incrementado

list++; }

Para percorrer um vetor alm da maneira mostrada no exemplo possvel usar um ponteiro varivel como ilustrado no exemplo abaixo.
int main() { 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++) cout<<v[i]<<" "; cout<<endl; for(i=0; i<9; i++) cout<<*(v+i)<<" "; cout<<endl; for(i=0, p=v; i<9; i++, p++) cout<<*p<<" "; }

Observe como o ponteiro p recebe seu valor inicial e a maneira que ele incrementado.

10

1.5. 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:
#include<iostream> #include<cstdlib> using namespace std; const int LIN=3; const int COL=4; void leMatriz(float m[LIN][COL]); void imprimeMatriz(float m[LIN][COL]); int main() { float matriz[LIN][COL]; cout<<"Entre com os dados da Matriz: \n"; leMatriz(matriz); cout<<"\nMatriz digitada: \n"; imprimeMatriz(matriz); cout<<endl; system("pause"); return 0; } void leMatriz(float m[LIN][COL]) { for(int i=0; i<LIN; i++) { for(int j=0; j<COL; j++) { cout<<"Elemento ["<<i<<"]["<<j<<"]: "; cin>>m[i][j]; } } } void imprimeMatriz(float m[LIN][COL]) { for(int i=0; i<LIN; i++) { for(int j=0; j<COL; j++) cout<<m[i][j]<<" "; cout<<endl; }

11

Caso o programa utilizasse ponteiros ao invs de notao de matrizes constantes o programa ficaria maneira apresentada a seguir. 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 1 ilustra como ser feito o armazenamento desta matriz.

Figura 1: Exemplo de como implementar uma matriz


#include<iostream> #include<cstdlib> using namespace std; void void void void alocaMatriz(float **&m, int l, int c); desalocaMatriz(float **&m, int lin); leMatriz(float **m, int l, int c); imprimeMatriz(float **m, int l, int c);

int main() { int lin, col; float **matriz; cout<<"Entre com numero de linhas da Matriz: "; cin>>lin; while (lin<=0) { cout<<"Numero invalido! Digite valor maior que zero!"; cout<<"Entre com numero de linhas da Matriz: "; cin>>lin; }

12

cout<<"Entre com numero de colunas da Matriz: "; cin>>col; while (col<=0) { cout<<"Numero invalido! Digite valor maior que zero!"; cout<<"Entre com numero de colunas da Matriz: "; cin>>col; } alocaMatriz(matriz, lin, col); cout<<"Entre com os dados da Matriz: \n"; leMatriz(matriz, lin, col); cout<<"\nMatriz digitada: \n"; imprimeMatriz(matriz, lin, col); cout<<endl; desalocaMatriz(matriz, lin); system("pause"); return 0; }

void alocaMatriz(float **&m, int l, int c) { //Aloca primeiro as linhas m = new float*[l]; //Aloca depois as colunas for (int i=0; i<l; i++) m[i]=new float[c]; } void desalocaMatriz(float **&m, int l) { //Desaloca primeiro as colunas for (int i=0; i<l; i++) delete [] m; //Desaloca as linhas delete [] m; m=NULL; } void leMatriz(float **m, int l, int c) { for(int i=0; i<l; i++) { for(int j=0; j<c; j++) { cout<<"Elemento ["<<i<<"]["<<j<<"]: ";

13

cin>>m[i][j]; } } }

void imprimeMatriz(float **m, int l, int c) { for(int i=0; i<l; i++) { for(int j=0; j<c; j++) cout<<m[i][j]<<" "; cout<<endl; } }

1.6. 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 cout, como no exemplo abaixo
cout<<"Acabou o programa.\n";

Quando uma string como este enviado para a funo o que passado o ponteiro para a string. No exemplo abaixo 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 caractere a caractere.
#include<iostream> #include<cstdlib> using namespace std; int strtam(const char *s); // Prototipo de funo int main() { char lista[]="Isto e um teste!"; cout<<"O tamanho do string \""<<lista<<"\" e "; cout<<strtam(lista)<<" caracteres.\n"; system("pause"); return 0; } int strtam(const char *s)

14

{ int tam=0; while(*(s + tam++) != '\0'); return (tam-1); }

Outro exemplo ilustrado abaixo. E mostra uma funo que copia uma string para outra.
#include<iostream> #include<cstdlib> using namespace std; int strcop(char *d, const char *o); int main() { char destino[20]; char origem[]="string de origem"; strcop(destino, origem);//copia o string origem para o destino cout<<"Origem: "<<origem<<endl; cout<<"Destino: "<<destino<<endl; system("pause"); return 0; }

int strcop(char *d, const char *o) { while ((*d++ = *o++) != '\0'); return 1; }

1.7. Estruturas e Ponteiros para Estruturas


Uma estrutura agrupa vrias variveis numa s. Imagine uma ficha pessoal que contenha nome, telefone, endereo, etc. A ficha uma estrutura onde cada campo ser um tipo de dado diferente. A estrutura um conjunto de dados no homogneos ou um agrupamento de dados no similares, para formar um novo tipo de dados. Estruturas constituem um recurso importante para organizar os dados utilizados por um programa graas possibilidade de tratar um grupo de valores como uma nica varivel.

1.7.1.

Criando uma Estrutura

Para se criar uma estrutura usa-se o comando struct. Sua forma geral :
struct nome_do_tipo_da_estrutura { tipo_1 nome_1; tipo_2 nome_2;

15

... tipo_n nome_n; };

A string aps o comando struct representa o nome. Esta no a varivel ainda, mas o tipo que ela ter quando for declarada. Ou seja, o nome_do_tipo_da_estrutura o nome que se dar para a estrutura. Um primeiro exemplo: struct est { int i; float f; }; est a, b; Neste caso, est o nome do tipo de uma estrutura de dados que conter as variveis i e f. Foram tambm declaradas duas variveis, a e b. Elas so estruturas do tipo est. Isto , a possui os campos i e f, o mesmo acontecendo com b. Vamos criar uma estrutura de endereo: struct tipo_endereco { string rua; int numero; string bairro; string cidade; string sigla_estado; long int CEP; }; Vamos agora criar uma estrutura chamada ficha_pessoal com os dados pessoais: struct ficha_pessoal { string nome; long int telefone; tipo_endereco endereco; }; Observe que foi declarada dentro da estrutura ficha_pessoal os campos nome, telefone e endereo. Esta ltima uma varivel chamada endereo, que uma estrutura do tidp tipo_endereco, ou seja, que contm os campos rua, numero, etc. Vemos que uma estrutura pode fazer parte de outra ( a estrutura tipo_endereco usada pela estrutura ficha_pessoal). Vamos agora utilizar as estruturas declaradas na seo anterior para escrever um programa que preencha uma ficha.

16

#include <iostream> #include <cstdlib> #include <string> using namespace std; struct tipo_endereco { string rua; int numero; string bairro; string cidade; string sigla_estado; long int CEP; };

struct ficha_pessoal { string nome; long int telefone; tipo_endereco endereco; }; int main () { ficha_pessoal ficha; ficha.nome="Fulano de Tal"; ficha.telefone=4921234; ficha.endereco.rua="Rua das Flores"; ficha.endereco.numero=10; ficha.endereco.bairro="Cidade Velha"; ficha.endereco.cidade="Belo Horizonte"; ficha.endereco.sigla_estado="MG"; ficha.endereco.CEP=31340230; cout<<endl<<"\t**** Dados da ficha pessoal ****"; cout<<endl<<endl; cout<<"Nome:\t"<<ficha.nome<<endl; cout<<"Tel:\t"<<ficha.telefone<<endl; cout<<"Rua:\t"<<ficha.endereco.rua<<endl; cout<<"Numero:\t"<<ficha.endereco.numero<<endl; cout<<"Bairro:\t"<<ficha.endereco.bairro<<endl; cout<<"Cidade:\t"<<ficha.endereco.cidade<<endl; cout<<"Estado:\t"<<ficha.endereco.sigla_estado<<endl; cout<<"CEP:\t"<<ficha.endereco.CEP<<endl<<endl; system("pause"); return 0; }

17

O programa declara a varivel ficha do tipo ficha_pessoal e preenche os seus campos. O exemplo mostra como podemos acessar um elemento de uma estrutura: basta usar o ponto (.). Assim, para acessar o campo telefone de ficha, escrevemos: ficha.telefone = 4921234; Como a struct ficha pessoal possui um campo, endereco, que tambm uma struct mas do tipo tipo_endereco, podemos fazer acesso aos campos desta struct interna da seguinte maneira: ficha.endereco.numero = 10; ficha.endereco.CEP = 31340230; Desta forma, estamos acessando, primeiramente, o campo endereco da struct ficha e, dentro deste campo, estamos acessando os campos numero e CEP.

1.7.2.

Matrizes de estruturas

Uma estrutura como qualquer outro tipo de dado no C/C++. Podemos, portanto, criar matrizes de estruturas. Vamos ver como ficaria a declarao de um vetor de 100 fichas pessoais: ficha_pessoal fichas [100]; Poderamos ento acessar a segunda letra da sigla de estado da dcima terceira ficha fazendo: fichas[12].endereco.sigla_estado[1]; Analise atentamente como isto est sendo feito. Podemos atribuir duas estruturas que sejam do mesmo tipo. O C ir, neste caso, copiar uma estrutura, campo por campo, na outra. Veja o programa abaixo:
#include <iostream> #include <cstdlib> #include <string> using namespace std; struct est1 { int i; float f; }; int main () { // Declara primeira e segunda como structs do tipo est1 est1 primeira, segunda; primeira.i = 10; primeira.f = 3.1415; segunda = primeira; // A segunda struct e' agora igual a primeira

18

cout<<"Os valores armazenasdos na segunda struct sao: "; cout<<segunda.i<<" e "<<segunda.f<<endl; system("pause"); return 0; }

So declaradas duas estruturas do tipo est1, uma chamada primeira e outra chamada segunda. Atribuem-se valores aos dois campos da struct primeira. Os valores de primeira so copiados na estrutura segunda apenas com a expresso de atribuio: segunda = primeira; Todos os campos de primeira sero copiados na segunda. Note que isto diferente do que acontecia em vetores, onde, para fazer a cpia dos elementos de um vetor em outro, tnhamos que copiar elemento por elemento do vetor. Para structs muito mais fcil! Porm, devemos tomar cuidado na atribuio de structs que contenham campos ponteiros. Veja abaixo:
/* Este programa um exemplo INCORRETO para atribuir estruturas que contenham ponteiros */ #include <iostream> #include <cstdlib> #include <string.h> using namespace std; //Define a estrutura tipo_end com dois atributos struct tipo_end { char *rua; //A struct possui um campo que um ponteiro int numero; }; //Funo Principal int main() { tipo_end end1, end2; char buffer[50]; cout<<"\nEntre o nome da rua:"; gets(buffer); // Le o nome da rua em uma string de buffer /* Aloca a quantidade de memoria suficiente para armazenar a string */ end1.rua = new char[strlen(buffer)+1]; strcpy(end1.rua, buffer); // Copia a string cout<<"\nEntre o numero:"; cin>>end1.numero; /* ERRADO end2.rua e end1.rua estao apontando para a mesma regiao de memoria */

19

end2 = end1; cout<<"Depois da atribuicao:\n Endereco em end1 "; cout<<end1.rua<<" "<<end1.numero; cout<<"\n Endereco em end2 "<<end2.rua<<" "<<end2.numero; /* Uma modificacao na memoria apontada por end2.rua causara' a modificacao do que e' apontado por end1.rua, o que, esta' errado !!! */ strcpy(end2.rua, "Rua Mesquita"); end2.numero = 1100; //Nesta atribuicao nao ha problemas

cout<<" \n\nApos modificar o endereco em end2:\n "; cout<<"Endereco em end1 "<<end1.rua<<" "<<end1.numero; cout<<"\n Endereco em end2 "<<end2.rua<<" "<<end2.numero; cout<<endl<<endl; system("pause"); return 0; }

Neste programa h um ERRO GRAVE, pois ao se fazer a atribuio end2 = end1, o campo rua de end2 estar apontando para a mesma posio de memria que o campo rua de end1. Assim, ao se modificar o contedo apontado por end2.rua estaremos tambm modificando o contedo apontado por end1.rua !!!

1.7.3.

Passando para funes

No exemplo apresentado no tem usando, vimos o seguinte comando: strcpy (ficha.nome,"Luiz Osvaldo Silva"); Neste comando um elemento de uma estrutura passado para a funo strcpy, que vai copiar a string Fulano de Tal para a varivel nome (campo dentro da estrutura ficha). Podemos tambm passar uma estrutura inteira como argumento para uma funo. Veja a seguinte funo: void PreencheFicha (ficha_pessoal ficha) { ... } Como vemos acima fcil passar a estrutura como um todo para a funo. Devemos observar que, como em qualquer outra funo no C/C++, a passagem da estrutura feita por valor. A estrutura que est sendo passada, vai ser copiada, campo por campo, em uma varivel local da funo PreencheFicha. Isto significa que alteraes na estrutura dentro da funo no tero efeito na varivel fora da funo. Mais uma vez podemos contornar este detalhe usando ponteiros e passando para a funo um ponteiro para a estrutura. 20

1.7.4.

Ponteiros para Estruturas

Podemos ter um ponteiro para uma estrutura. Vamos ver como poderia ser declarado um ponteiro para as estruturas de ficha que estamos usando nestas sees: ficha_pessoal *p; Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados no C/C++. Para us-lo, haveria duas possibilidades. A primeira apont-lo para uma varivel struct j existente, da seguinte maneira: ficha_pessoal ficha; ficha_pessoal *p; p = &ficha; A segunda alocando memria para ficha_pessoal usando, por exemplo, new:
/* Programa para cadastro de pessoal */ #include <iostream> #include <cstdlib> #include <cstdio> #include <string> using namespace std; //Definicao da estrutura para armazenar endereco struct tipo_endereco { string rua; int numero; string bairro; string cidade; string sigla_estado; long int CEP; }; //Definicao da estrutura para armazenar dados do pessoal struct ficha_pessoal { string nome; long int telefone; tipo_endereco endereco; }; int main () { ficha_pessoal *p; // Faremos a alocacao dinamica de n fichas pessoais int n; cout<<"Entre com o numero de fichas:"; cin>>n; fflush(stdin);

21

p = new ficha_pessoal[n]; for (int i=0; i<n; i++) { cout<<endl<<"\t**** Dados da ficha pessoal "<<i<<" ****"; cout<<endl; cout<<"Nome:\t"; getline(cin,p[i].nome); fflush(stdin); cout<<"Tel:\t"; cin>>p[i].telefone; fflush(stdin); cout<<"Rua:\t"; getline(cin,p[i].endereco.rua); fflush(stdin); cout<<"Numero:\t"; cin>>p[i].endereco.numero; fflush(stdin); cout<<"Bairro:\t"; getline(cin,p[i].endereco.bairro); fflush(stdin); cout<<"Cidade:\t"; getline(cin,p[i].endereco.cidade); fflush(stdin); cout<<"Estado:\t"; cin>>p[i].endereco.sigla_estado; fflush(stdin); cout<<"CEP:\t"; cin>>p[i].endereco.CEP; fflush(stdin); } for (int i=0; i<n; i++) { cout<<endl<<"\t**** Dados da ficha pessoal "<<i<<" ****"; cout<<endl; cout<<"Nome:\t"<<p[i].nome<<endl; cout<<"Tel:\t"<<p[i].telefone<<endl; cout<<"Rua:\t"<<p[i].endereco.rua<<endl; cout<<"Numero:\t"<<p[i].endereco.numero<<endl; cout<<"Bairro:\t"<<p[i].endereco.bairro<<endl; cout<<"Cidade:\t"<<p[i].endereco.cidade<<endl; cout<<"Estado:\t"<<p[i].endereco.sigla_estado<<endl; cout<<"CEP:\t"<<p[i].endereco.CEP<<endl; } cout<<endl; delete [] p; //Ateno a alocao dinamica

22

system("pause"); return 0; }

H um detalhe importante. Se apontarmos o ponteiro p para uma estrutura qualquer (como fizemos em p = &ficha;) e quisermos acessar um elemento da estrutura poderamos fazer: (*p).nome Os parnteses so necessrios, porque o operador . tem precedncia maior que o operador * . Porm, este formato no muito usado. O que comum de se fazer acessar o elemento nome atravs do operador seta, que formado por um sinal de "menos" (-) seguido por um sinal de "maior que" (>), isto : -> . Assim faremos: p->nome A declarao acima muito mais fcil e concisa. Para acessarmos o elemento CEP dentro de endereco faramos: p->endereco.CEP Exerccio: Seja a seguinte struct que utilizada para descrever os produtos que esto no estoque de uma loja : struct Produto { char nome[30]; int codigo; double preco; };

// Nome do produto // Codigo do produto // Preco do produto

a)Escreva uma instruo que declare uma matriz de Produto com 10 itens de produtos; b)Atribua os valores "Pe de Moleque", 13205 e R$0,20 aos membros da posio 0 e os valores "Cocada Baiana", 15202 e R$0,50 aos membros da posio 1 da matriz anterior; c) Faa as mudanas que forem necessrias para usar um ponteiro para Produto ao invs de uma matriz de Produtos. Faa a alocao de memria de forma que se possa armazenar 10 produtos na rea de memria apontada por este ponteiro e refaa as atribuies da letra b; d)Escreva as instrues para imprimir os campos que foram atribudos na letra c.

23

1.8. Ponteiros para Funes


Muito embora uma funo no seja uma varivel, ela ocupa uma posio fsica na memria, e assim, essa posio pode ser atribuda a um ponteiro. O endereo de uma funo chamado de ponto de entrada desta funo, pois, quando a funo compilada, o cdigo-fonte transformado em cdigo-objeto e um ponto de entrada estabelecido para ela. Um ponteiro para a funo pode ser usado para chamar a funo, pois ele pode apontar para o ponto de entrada desta funo. O endereo de uma funo obtido usando o nome da funo sem parnteses ou argumentos. Observe o exemplo:
#include <stdio.h> #include <string.h> void check (char *a, char *b, int (*cmp) ( const char *, const char *)); void main(void) { char s1[80], s2[80]; int (*p)(); p=srtcmp; gets(s1); gets(s2); check(s1, s2, p); }

//p recebe o ponto de entrada de strcmp

//chamada da funo check()

void check (char *a, char *v, int (*cmp) (const char *, const char *)) { cout <<Testando igualdade de strings \n; if(!(cmp)(a,b)) cout <<igual; //nesse ponto, a funo strcmp chamada else cout << Diferente ; }

Ao ser chamada a funo check(), so passados dois strings s1 e s2 e um ponteiro p para funo - que aponta para o ponto de entrada da funo strcmp() - como argumentos. Na funo check(), a funo strcmp() chamada atravs do ponteiro cmp (declarado no prottipo dela). Lembre-se de que cmp recebeu o contedo de p, que na verdade o endereo da funo strcmp()

24

2. A Funo main() e seus argumentos

2.1. A funo void main(int argc, char *argv[])


Muitas vezes interessante passar parmetros para um programa ao execut-lo. Esses argumentos so passados atravs da funo main(). Um argumento na linha de comando a informao que segue o nome do programa na linha de comando do sistema operacional. Ex: ao executarmos na linha de comando do prompt do Windows, o comando dir, por exemplo: c>\ dir /p

Neste caso, estamos passando o parmetro /p para o programa dir (lista contedo de diretrio) que far com haja uma pausa na listagem do contedo do diretrio (tampem chamado de pasta) a cada vez que a tela do prompt preenchida. Observe o cdigo abaixo: #include <stdio.h> #include <stdlib.h> void main ( int argc, char *argv[]) { if (argc=!2){ cout << Voce esqueceu de digitar o seu nome. \n; exit (1) } cout<<Ola <<argv[1]; } Se o nome do programa fosse chamada.exe e seu nome fosse Carlos, ento, para rodar o programa, voc digitaria chamada Carlos e a resposta seria: Ola Carlos

2.1.1.

O char argv[ ] e o int argc

Os colchetes vazios indicam que a matriz de tamanho indeterminado. Cada item da matriz de strings pode ser acessado indexando o argv. Ou seja, argv armazena cada um dos strings separados por espao como um elemento desta matriz. J o int argc armazena quantos strings foram digitados. O menor valor para argc 1, que representa o nome do programa chamado. No caso do exemplo anterior, para imprimir Ola fulano, argc seria igual a 2 (nome do programa mais a string fulano)

25

3. Alocao Dinmica de Memria


As funes bsicas de alocao dinmica de memria em C++ so new e delete. O exemplo abaixo ilustra o uso das funo new e delete.
#include<iostream> #include<cstdlib> using namespace std; int main() { float *v; int i, tam; cout<<"Qual o tamanho do vetor? "; cin>>tam; v = new float[tam]; //Aloca a quantidade pedida pelo usuario for (i=0; i<tam; i++) { cout<<"Elemento "<<i<<" ?"; cin>>v[i]; cout<<"Li valor "<<*(v+i)<<"\n"; } delete [] v; //Para todo new deve haver sempre um delete! system("pause"); return 0; }

Outro exemplo, empregando a funo new est mostrado no exemplo abaixo. Observe que neste exemplo tambm mostramos situaes onde um endereo de varivel foi passado para uma funo de modo que a funo main() possa receber um valor (vezes).
#include<iostream> #include<cstdlib> using namespace std; void leVetor (float *v, int tam); float procuraMaior (float *v, int tam, int *vezes); int main() { float *v, maior; int i, tam, vezes; cout<<"Qual o tamanho do vetor? "; cin>>tam; v = new float[tam]; leVetor(v, tam);

26

maior = procuraMaior (v, tam, &vezes); cout<<"O maior elemento e "<<maior; cout<<" e aparece "<<vezes<<" vezes.\n\n"; delete [] v; system("pause"); return 0; } void leVetor (float *v, int tam) { int i; for (i=0; i<tam; i++) { cout<<"Elemento "<<i<<": "; cin>>*(v+i); cout<<"Li valor "<<*(v+i)<<"\n"; } }

float procuraMaior (float *v, int tam, int *vezes) { int i; float maior; maior = v[0]; *vezes = 1; for (i=1; i<tam; i++) { if (v[i] > maior) { maior = v[i]; *vezes = 1; } else if (maior == v[i]) *vezes=*vezes+1;; } return maior; }

Cuidado para no confundir a notao:


int *p = new int(5); // aloca espao para um int e armazena nele o //valor 5 int *q = new int[5]; // aloca espao para um vetor com 5 //elementos do tipo int

27

4. Algoritmos de Ordenao e Pesquisa


Duas das tarefas mais fundamentais e extensivamente utilizadas em relao s estruturas de dados so: a ordenao e a pesquisa. Algortmos para essas tarefas so utilizadas em praticamente todos os programas de bancos de dados, bem como compiladores, interpretadores e sistemas operacionais. Ordenao o processo de arranjar um conjunto de informaes semelhantes em uma ordem crescente ou decrescente, utilizando para isso, uma parte da informao chamada chave de ordenao. Por exemplo, em uma lista postal, pode-se escolher uma ordenao pelo CEP ou por ordem alfabtica, bastando para isso escolher como chave o campo CEP ou o campo nome.

4.1. Ordenao Bolha


Observe o algoritmo abaixo:
//Ordenao Bolha void bolha (char *item, int count) { register int a, b; register char t; for (a=1; a<count; ++a) { for (b=count-1; b>=a; b--){ if (item[b-1] > item[b]) { t = item[b-1]; item[b-1]=item[b]; item[b] = t; } } } }

Neste cdigo, item um ponteiro para uma matriz de caracteres a ser ordenanda e count o nmero de elementos da matriz. H dois laos. O lao mais externo faz a matriz ser varrida cout-1 vezes. Isso garante que, na pior das hipteses, todo elemento estar na posio correta quando a funo terminar. No lao interno so feitas as comparaes e trocas. Neste algoritmo feito sempre o mesmo nmero de comparaes e trocas, independentemente de os dados estarem mais ou menos ordenados ou totalmente desordenados. Isso o ponto fraco desse algoritmo. Ou seja, independente de os dados estarem ordenados ou no, o nmero de passos do algoritmo continua o mesmo. H outros algoritmos cujo esforo computacional , na pior das hipteses igual ao Bolha.

4.2. Ordenao por seleo


28

No algoritmo de ordenao por seleo, o elemento, cuja chave possua o menor valor, selecionado e trocado pelo primeiro elemento da lista. Ento, para os n-1 elementos restantes, encontrado o elemento de menor chave, que trocado pelo segundo. E assim vai at que todos os n elementos estejam ordenados. Observe o algoritmo abaixo:
//Ordenao por Seleo void selecao (char *item, int count) { register int a, b, c; int Exchange; char t; for (a=0; a<count; ++a) { exchange = 0; c = a; t = item[a]; for(b=a+1; b<count; ++b){ if(item[b]<t) { c = b; t = item[b]; exchange = 1; }

} if (exchange) { item[c] = item[a]; item[a] = t; } } } Neste caso, embora o nmero de comparaes seja o mesmo que para o Bolha, o nmero de trocas no caso mdio ( dados mais ou menos ordenados...) muito menor.

4.3. Ordenao por insero


Neste algoritmo, primeiramente so ordenados os dois primeiros elementos da lista. Em seguida, insere-se o terceiro elemento em sua ordenao em relao aos dois primeiros. Posteriormente, insere-se o quarto elemento em relao aos trs primeiros, e assim vai at que todos os elementos estejam inseridos e ordenados. Observe o cdigo abaixo:
//Ordenao por Inserso void insercao (char *item, int count) { register int a, b; register char t; for (a=1; a<count; ++a) { t = item[a]; for (b=a-1; b>=0 && t<item[b]; b--){ item[b+1]=item[b]; item[b+1] = t; }

29

} } }

Ao contrrio dos algoritmos anteriores, o nmero de comparaes depende de como a lista est inicialmente ordenada. Se a lista estiver em ordem, o nmero de comparaes ser n1. J se estiver fora de ordem, ser um nmero muito grande, se aproximando do pior caso que a Ordenao Bolha.

4.4. Ordenao Quicksort


A ordenao quicksort foi inventada por C.A.R. Hoare1 e superior a todas as anteriores. Geralmente considerado o melhor algoritmo de ordenao de propsito geral atualmente disponvel. O quicksort baseado na idia de partio. O procedimento geral selecionar um valor, chamado de comparando e, ento, fazer a partio da lista (ou matriz) de dados em duas sees: a primeira com todos os elementos cuja chave seja menor que a chave do comparando e a segunda, com todos os elementos cuja chave seja maior que a do comparando. Esse processo repetido para cada seo recursivamente, at que toda a lista esteja ordenada. Observe o exemplo abaixo:
// Ordenao Quicksort void quick(char *item, count-1) { qs(item, 0, count-1) } //O quicksort propriamente dito void qs (char *item, int left, int rigth) { register int i, j; char x, y; i = left; j = rigth; x = item[(left+rigth)/2]; do{ while (item[i]<x && i<rigth) i++; while (x<item[j] && j>left) j--; if(i<=j) { y = item[i]; item[i] = item[j]; item[j] = y; i++; j--; } } while (i<=j); if (left < j) qs(item, left, j); if (i < rigth) qs(item, i, rigth);
1

Sir Charles Antony Richard Hoare (Tony Hoare ou C.A.R. Hoare, nascido em 11 de janeiro de 1934) um cientista da computao britnico, mais conhecido pelo desenvolvimento do Quicksort em 1960, o algoritmo de ordenao mais utilizado no mundo, e muito provavelmente o algoritmo mais usado dentre todos os tipos existentes

30

A funo quick() executa a chamada funo de ordenao principal qs(). Aps a primeira chamada de qs(), esta chamada recursivamente at que todas as sub-sees sejam feitas e os dados sejam ordenados. em todos os exemplos, a chave um caractere que est em uma determinada posio da matriz do tipo char apontada pelo ponteiro item.

31

5. Estruturas de Dados

5.1. Filas
As filas Uma fila (queue) uma seqncia de n nodos de um determinado tipo, cujo comportamento simula uma fila de registros. Normalmente representamos uma lista por uma seqncia de elementos separados por vrgula x1, x2, x3, ...., xn. Onde n >= 0. O nmero n de elementos o comprimento da fila. Se n > 0 ento dizemos que x1 o primeiro elemento da fila e xn o ltimo elemento. Sendo que x1 foi primeiro elemento e o xn o ltimo elemento a chegar. Se n = 0 ento a fila est vazia. Uma propriedade importante da fila que os elementos so inseridos no final e removidos no incio. O primeiro registro a ser inserido o primeiro a sair :FIFO (first in first out). . Tambm chamada de lista linear de informaes, que acessada na seguinte ordem: Primeiro elemento a entrar, primeiro elemento a sair ( FIFO:First in, First Out).

Ou seja, o primeiro colocado na fila o primeiro a sair; o segundo colocado o segundo a sair, etc. Uma maneira de visualizar uma fila imagin-la como uma fila de banco, onde a ltima pessoa a entrar ser a ltima pessoa a sair:

Imaginemos na figura acima como uma fila de Banco, onde cada clula representa um cliente a ser atendido. Os clientes, naturalmente, so ordenados na ordem de chegada (aqui no estamos colocando casos especiais, como gestantes, etc.). de se esperar que os clientes sejam atendidos na mesma ordem quem que chegaram, ou seja, o primeiro, em seguida, o segundo, at o ltimo. Caso no tenha mais nenhum elemento (cliente) na fila, dizemos que a fila est vazia. Caso o nmero de elementos ultrapasse a capacidade da fila, dizemos que a fila est cheia e incapaz de armazenar elementos seguintes.

Uma fila tem por norma as seguintes funcionalidade: add ou store guardar um elemento na fila remove ou retrieve retirar um elemento da fila top retornar o elemento do topo da fila full Verificar se a fila est cheia (no pode guardar mais elementos). empty Verificar se a fila est vazia (no contm elementos) 32

construct Colocar a fila num estado pronto a ser utilizada (Prepar-la)

Observe um modelo de fila simples (Schildt, 1997) onde so manipulados trs elementos: A, B e C:

A funo qstore() armazena os elementos A e B, em seguida, a funo qretrieve() retira os dois elementos, e por fim, a funo qstore() armazena o elemento C. Observe nessa implementao, que h um apontador spos que responsvel por indicar a primeira posio vazia apta a armazenar um elemento. H tambm o apontador rpos que indica a posio do prximo elemento a ser retirado da fila. Um vetor de elementos poderia ser utilizado para a construo desta fila. bom salientar que o elemento pode ser qualquer tipo de dados, ou seja, poderia ser char, int, float, uma estrutura, etc. Observe este modelo para as funes de retirada e insero de elementos na fila:

Funo de insero de um elemento na fila onde MAX representa o tamanho da fila. No caso, o elemento a ser inserido um ponteiro *q que recebe o endereo de um vetor de caracteres ( ex: um nome). Chamamos a ateno para o fato de que s podemos inserir um elemento se a fila no estiver cheia ( spos no pode apontar para MAX).

33

Na funo qretrieve() os elementos so retirados. claro que s ser retirado elementos se houver elementos na fila. Da o teste se rpos==spos. Observe que neste modelo, como rpos aponta para o prximo elemento a ser retirado, o elemento a ser retornado est indicado pela posio dada por rpos-1.

5.2. Filas Circulares


Observe o modelo de fila circular ilustrado abaixo (Schildt, 1997). Neste tipo de fila, o termo circular tem uma conotao de fila sem fim, ou seja, como se as pontas (incio e fim) da fila ficassem ligadas, fechando um ciclo sem fim.

Vamos tomar com exemplo, os apontadores spos e rpos utilizados na fila simples do item anterior. Vamos imaginar que a fila tenha 12 posies como na figura acima. Neste caso, se chegssemos na posio MAX, mas anteriormente, tivssemos retirado uns trs elementos do incio da fila, por que no reutilizar os espaos que vagos? a que entra a fila circular. Nela, enquanto houver espao devido recuperao dos dados e conseqente atualizao de rpos , que mostra a posio do prximo elemento a ser retirado, spos poderia avanar para reutilizar estes espaos. Em outras palavras, a fila circular somente estaria cheia se os ndices de armazenamento e de recuperao de dados forem iguais. Caso contrrio, a fila ter espaos vagos.

34

You might also like