Professional Documents
Culture Documents
Material de uso restrito para a Disciplina de Algoritmos e Estruturas de Dados II do Curso de Engenharia Eltrica do UNI-BH
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.
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.
#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.
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.
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;
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
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.
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; } }
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
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.1.
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
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.
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.
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; };
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
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
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.
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
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; }
27
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.
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.
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.
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
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.
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