Вы находитесь на странице: 1из 34
AEDsII Material de uso restrito para a Disciplina de Algoritmos e Estruturas de Dados II

AEDsII

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

Prof. Eduardo de Queiroz Braga

Índice: Índice:
Índice: Índice:

Índice:

Índice: ------------------------------------------------------------------------------------------------------------ 2

1. Ponteiros------------------------------------------------------------------------------------------------ 3

1.1.

Operações com Ponteiros------------------------------------------------------------------------ 4

1.2.

Aritmética de Ponteiros --------------------------------------------------------------------------- 5

1.3.

Acessando os Endereços ------------------------------------------------------------------------ 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

Funções ------------------------------------------------------------------------- 24

2. A Função main() e seus argumentos------------------------------------------------------------ 25

2.1.

A função void main(int argc, char *argv[]) -------------------------------------------------- 25

3. Alocação Dinâmica de Memória------------------------------------------------------------------ 26

4. Algoritmos de Ordenação e Pesquisa----------------------------------------------------------- 28

4.2.

Ordenação

Bolha---------------------------------------------------------------------------------- 28

4.3.

Ordenação

por

seleção -------------------------------------------------------------------------- 28

4.4.

Ordenação

por

inserção ------------------------------------------------------------------------- 29

4.5.

Ordenação

Quicksort----------------------------------------------------------------------------- 30

5. ESTRUTURAS DE DADOS ----------------------------------------------------------------------- 32

5.1.

Filas

-------------------------------------------------------------------------------------------------- 32

5.2.

Filas Circulares ------------------------------------------------------------------------------------ 34

1. Ponteiros Ponteiros são usados em situações em que é necessário conhecer o endereço onde
1. Ponteiros Ponteiros são usados em situações em que é necessário conhecer o endereço onde

1. Ponteiros

Ponteiros são usados em situações em que é necessário conhecer o endereço onde está armazenada a variável e não o seu conteúdo. Um ponteiro é uma variável que contém um endereço de memória e não o conteúdo da posição.

A memória de um computador pode ser vista como uma seqüência de bytes, cada um com

seu próprio endereço. Não há dois bytes com o mesmo endereço. O primeiro endereço

é sempre 0 e o último geralmente é uma potência de 2. Por exemplo, um computador com memória igual a 16 Mbytes tem 16x1024x1024 bytes.

A Tabela 1 mostra um exemplo de um trecho de memória que contém duas variáveis num e

res inteiras de tipo longo (4 bytes cada uma). Observar que os endereços estão pulando de quatro em quatro já que as variáveis são inteiras de tipo longo. Uma possível declaração destas variáveis dentro de um programa C/C++ poderia ser:

long int num=10, res=120;

TABELA 1: Mapa de Memória

Endereços (em decimal)

Conteúdo

Variável

996

---

---

1000

10

num

1004

120

res

Uma função pode receber os ponteiros que apontem para os endereços dos parâmetros. Assim, esta função pode modificar diretamente os conteúdos destas variáveis. Ou seja, ponteiros fornecem os meios pelos quais as funções podem modificar seus argumentos.

Uma aplicação importante de ponteiros é apontar para áreas de memória que são administradas durante a execução do programa. Com ponteiros é possível alocar as

posições de memória necessárias para armazenamento de vetores somente quando o programa estiver rodando. O programador pode reservar o número exato de posições que

o programa requer.

Ponteiros são um dos aspectos mais fortes e mais perigosos de C/C++. Por exemplo, ponteiros não inicializados, também chamados de ponteiros selvagens, podem provocar uma quebra no sistema.

1.1. Operações com Ponteiros 1.1.1. Declaração de Ponteiros Antes de serem usados os ponteiros precisam
1.1. Operações com Ponteiros 1.1.1. Declaração de Ponteiros Antes de serem usados os ponteiros precisam

1.1. Operações com Ponteiros

1.1.1. Declaração de Ponteiros

Antes de serem usados os ponteiros precisam ser declarados, assim como variáveis. A forma geral da declaração de um ponteiro é a seguinte:

tipo *nome;

Onde tipo é qualquer tipo válido em C/C++ e nome é o nome da variável ponteiro. Por exemplo:

int *res;

/* ponteiro para uma variavel inteira */

float *div;

/* ponteiro para uma variavel de ponto flutuante */

1.1.2. Os Operadores de Ponteiros

Existem dois operadores especiais para ponteiros: * e &. Os dois operadores são unários, isto é, requerem somente um operando.

O operador & devolve o endereço de memória do seu operando. Por exemplo,

pint = &soma;

/* o endereco de soma e carregado em pint */

No exemplo seguinte considere a Tabela 1. Após a execução do trecho de programa abaixo a variável ponteiro p termina com o valor 1000.

p = #

O operador * é o complemento de &. O operador * devolve o valor da variável localizada no endereço que o segue. Por exemplo, o comando

num = *p;

significa que a variável num recebe o valor apontado por p. Estes operadores não devem ser confundidos com os já estudados em capítulos anteriores. O operador * para ponteiros não tem nada a ver com o operador multiplicação. O operador ponteiro * é

unário e, como o operador &, tem precedência maior que do que todos os operadores
unário e, como o operador &, tem precedência maior que do que todos os operadores

unário e, como o operador &, tem precedência maior que do que todos os operadores aritméticos.

Observação: Nunca acesse o conteúdo de um ponteiro antes de inicializá-lo.

1.2. Aritmética de Ponteiros

Atribuição de Ponteiros

Do mesmo modo que uma variável comum, conteúdo de um ponteiro pode ser passado para outro ponteiro do mesmo tipo. As variáveis ponteiros devem sempre apontar para os tipos de dados corretos. Uma variável ponteiro declarada como apontador de dados inteiros deve sempre apontar para dados deste tipo.

Observar que em C/C++ possível atribuir qualquer endereço a uma variável ponteiro. Deste modo é possível atribuir o endereço de uma variável do tipo float a um ponteiro inteiro. No entanto, o programa não irá funcionar da maneira correta.

Por exemplo, no trecho de programa abaixo o endereço do terceiro elemento do vetor v é carregado em p1 e o endereço da variável i é carregado em p2. Além disso, no final o endereço 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 Endereços

O programa abaixo faz com que o endereço da variável x seja carregado no ponteiro p. Em seguida o programa imprime o que está apontado por p.

#include<iostream> #include<cstdlib> using namespace std; int main() { float x=3.14, *p; p = &x;
#include<iostream> #include<cstdlib> using namespace std; int main() { float x=3.14, *p; p = &x;

#include<iostream>

#include<cstdlib>

using namespace std;

int main()

{

float x=3.14, *p;

p = &x; cout<<"*p = "<<*p<<endl;;

//Ponteiro que carrega o endereço da variável x

system("Pause"); return 0;

}

1.3.1. Incrementando e Decrementando Ponteiros

O exemplo abaixo mostra que operações 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 endereço que aponte para um número inteiro, que é armazenado em quatro bytes, seja incrementado por um e passe para apontar para o próximo número inteiro. A resposta para isto é que sempre que um ponteiro é incrementado (ou decrementado) ele passa a apontar para a posição do elemento seguinte (ou anterior). Do mesmo modo somar três a um ponteiro, faz com que ele passe apontar para o terceiro elemento após o atual. Portanto, um incremento em um ponteiro que aponta para um valor que é armazenado em n bytes faz que n seja somado ao endereço.

É possível se usar o seguinte comando

*(p+1)=10;

Este comando armazena o valor 10 na posição seguinte àquela apontada por p. É possível
Este comando armazena o valor 10 na posição seguinte àquela apontada por p. É possível

Este comando armazena o valor 10 na posição seguinte àquela apontada por p. É possível somar-se e subtrair-se inteiros de ponteiros. A operação abaixo faz com que o ponteiro p passe a apontar para o terceiro elemento após o atual.

p

= p + 3;

A diferença 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]; p2 = &vetor;

// endereco do terceiro elemento // endereco do primeiro elemento

cout<<"Diferenca entre ponteiros %d\n"<<p1-p2;

system("Pause"); return 0;

}

Observação: Não é possível multiplicar ou dividir ponteiros, e não se pode adicionar ou subtrair o tipo float ou o tipo double a ponteiros.

1.3.2. Comparação de Ponteiros

É possível comparar ponteiros em uma expressão relacional. Mas, só podemos comparar ponteiros de mesmo tipo. O trecho de programa abaixo ilustra um exemplo deste tipo de operações.

char *c, *v;

cin>>*c>>*v; if (c == v) // Cuidado com esse tipo de comparação!!! cout<<"As variáveis estao na mesma posicao.\n";

else

cout<<"As variaveis nao estao na mesma posicao.\n";

1.3.3. Indireção Multipla

Ponteiros podem apontar para outro ponteiro que aponta para o valor final. Essa situação é chamada de indireção múltipla, ou também de ponteiros para ponteiros. O valor de um ponteiro convencional é o endereço de uma variável que contém um determinado valor. Já para o caso de um ponteiro para ponteiro, o primeiro ponteiro

contém o endereço do segundo ponteiro, que finalmente contém o endereço da variável que contém
contém o endereço do segundo ponteiro, que finalmente contém o endereço da variável que contém

contém o endereço do segundo ponteiro, que finalmente contém o endereço da variável que contém o valor desejado. Observe a figura.

Ponteiro Variável Indireção Simples Endereço Valor Ponteiro Ponteiro Variável Endereço Endereço Valor
Ponteiro
Variável
Indireção Simples
Endereço
Valor
Ponteiro
Ponteiro
Variável
Endereço
Endereço
Valor

Exemplo: Observe o código abaixo:

#include <stdio.h>

Void main(void)

{

 

int x, *p, **q;

x=10;

p=&x; q=&p; cout < “O valor de x eh ”<<**q;

}

Indireção Múltipla

Observamos que o valor 10 é atribuído à variável x. Já p é um ponteiro do tipo int que passa a conter o endereço da variável x. Por outro lado, q é um ponteiro do tipo int que contém o endereço do ponteiro p. Isso é um caso de indireção múltipla.

1.4. Ponteiros e Vetores Ponteiros e Vetores estão fortemente relacionados na linguagem C/C++. O nome
1.4. Ponteiros e Vetores Ponteiros e Vetores estão fortemente relacionados na linguagem C/C++. O nome

1.4. Ponteiros e Vetores

Ponteiros e Vetores estão fortemente relacionados na linguagem C/C++. O nome de um vetor é um ponteiro que aponta para a primeira posição do vetor e todas as operações já mencionadas para ponteiros podem ser executadas com um nome de vetor. Por exemplo, a declaração:

int v[100];

declara um vetor de inteiros de 100 posições. Se p recebe o ponteiro para a primeira posição de v como na declaração abaixo:

int *p=v;

As seguintes declarações serão idênticas:

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

Também são idênticas as próximas declarações:

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

O exemplo ilustrado abaixo mostra as duas notações 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 diferença fundamental entre declarar um conjunto de dados como um vetor ou através de um ponteiro. Na declaração de vetor, o compilador automaticamente reserva um

bloco de memória para que o vetor seja armazenado. Quando apenas um ponteiro é declarado
bloco de memória para que o vetor seja armazenado. Quando apenas um ponteiro é declarado

bloco de memória 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 memória, sem que espaço seja reservado.

O nome de um vetor é chamado de ponteiro constante e, portanto, não pode ter o seu valor alterado. Assim, os comandos abaixo não são válidos:

int main()

{

int list[5], i;

list = &i;

// O ponteiro list nao pode ser modificado //recebendo o endereco de i

list++;

// O ponteiro list nao pode ser incrementado

}

Para percorrer um vetor além da maneira mostrada no exemplo é possível usar um ponteiro variável 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.

1.5. Ponteiros e Matrizes Um ponteiro aponta para uma área de memória que é endereçada
1.5. Ponteiros e Matrizes Um ponteiro aponta para uma área de memória que é endereçada

1.5. Ponteiros e Matrizes

Um ponteiro aponta para uma área de memória que é endereçada de maneira linear. Deste modo, é necessário mapear o endereço de cada elemento na matriz, que é dado por linha coluna, em um endereço 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;

}

} Caso o programa utilizasse ponteiros ao invés de notação de matrizes constantes o programa
} Caso o programa utilizasse ponteiros ao invés de notação de matrizes constantes o programa

}

Caso o programa utilizasse ponteiros ao invés de notação de matrizes constantes o programa ficaria maneira apresentada a seguir.

Neste exemplo iremos criar um vetor de ponteiros que irá armazenar o endereço inicial de cada linha. Portanto, para obter um elemento da matriz primeiro devemos descobrir onde está a linha no vetor que armazena os endereços das linhas, em seguida procuramos na linha o elemento. A figura 1 ilustra como será feito o armazenamento desta matriz.

1 ilustra como será feito o armazenamento desta matriz. Figura 1: Exemplo de como implementar uma

Figura 1: Exemplo de como implementar uma matriz

#include<iostream>

#include<cstdlib>

using namespace std;

void alocaMatriz(float **&m, int l, int c); void desalocaMatriz(float **&m, int lin); void leMatriz(float **m, int l, int c); void 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;

}

cout<<"Entre com numero de colunas da Matriz: "; cin>>col; while (col<=0) {
cout<<"Entre com numero de colunas da Matriz: "; cin>>col; while (col<=0) {

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<<"]: ";

cin>>m[i][j]; } } } void imprimeMatriz( float **m, int l, int c) { for (
cin>>m[i][j]; } } } void imprimeMatriz( float **m, int l, int c) { for (

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 função cout, como no exemplo abaixo

cout<<"Acabou o programa.\n";

Quando uma string como este é enviado para a função o que é passado é o ponteiro para a string. No exemplo abaixo aparece uma função que conta o número de caracteres de um string. Observe o ponteiro para o string constante e na função o ponteiro *(s+tam++) apontando caractere a caractere.

#include<iostream>

#include<cstdlib>

using namespace std;

int strtam(const char *s); // Prototipo de função

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)

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

{

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

}

Outro exemplo é ilustrado abaixo. E mostra uma função 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 várias variáveis numa só. Imagine uma ficha pessoal que contenha nome, telefone, endereço, etc. A ficha é uma estrutura onde cada campo será um tipo de dado diferente. A estrutura é um conjunto de dados não homogêneos ou um agrupamento de dados não similares, para formar um novo tipo de dados. Estruturas constituem um recurso importante para organizar os dados utilizados por um programa graças à possibilidade de tratar um grupo de valores como uma única variável.

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;

tipo_n nome_n; }; Esta não é a variável ainda, mas o tipo que ela terá
tipo_n nome_n; }; Esta não é a variável ainda, mas o tipo que ela terá

tipo_n nome_n;

};

Esta não é a variável 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:

A string após o comando struct representa o nome.

struct est

{

int i;

float f;

};

est a, b;

Neste caso, est é o nome do tipo de uma estrutura de dados que conterá as variáveis i e f. Foram também declaradas duas variáveis, a e b. Elas são estruturas do tipo est. Isto é, a possui os campos i e f, o mesmo acontecendo com b.

Vamos criar uma estrutura de endereço:

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 endereço. Esta última é uma variável chamada endereço, que é uma estrutura do tidp tipo_endereco, ou seja, que contém 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 seção anterior para escrever um programa que preencha uma ficha.

#include <iostream> #include <cstdlib> #include <string> using namespace std; struct
#include <iostream> #include <cstdlib> #include <string> using namespace std; struct

#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;

}

O programa declara a variável ficha do tipo ficha_pessoal e preenche os seus campos. O
O programa declara a variável ficha do tipo ficha_pessoal e preenche os seus campos. O

O programa declara a variável 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 também é 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 declaração de um vetor de 100 fichas pessoais:

ficha_pessoal fichas [100];

Poderíamos então acessar a segunda letra da sigla de estado da décima 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

cout<<"Os valores armazenasdos na segunda struct sao: "; cout<<segunda.i<<" e
cout<<"Os valores armazenasdos na segunda struct sao: "; cout<<segunda.i<<" e

cout<<"Os valores armazenasdos na segunda struct sao: "; cout<<segunda.i<<" e "<<segunda.f<<endl;

system("pause"); return 0;

}

São 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 são copiados na estrutura segunda apenas com a expressão de atribuição:

segunda = primeira;

Todos os campos de primeira serão copiados na segunda. Note que isto é diferente do que acontecia em vetores, onde, para fazer a cópia dos elementos de um vetor em outro, tínhamos que copiar elemento por elemento do vetor. Para structs é muito mais fácil!

Porém, devemos tomar cuidado na atribuição 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;

};

//Função 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); cout<<"\nEntre o numero:";

cin>>end1.numero;

// Copia a string

/* ERRADO end2.rua e end1.rua estao apontando para a mesma regiao de memoria */

end2 = end1; cout<<"Depois da atribuicao:\n Endereco em end1 "; cout<<end1.rua<<"
end2 = end1; cout<<"Depois da atribuicao:\n Endereco em end1 "; cout<<end1.rua<<"

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 atribuição end2 = end1, o campo rua de end2 estará apontando para a mesma posição de memória que o campo rua de end1. Assim, ao se modificar o conteúdo apontado por end2.rua estaremos também modificando o conteúdo apontado por end1.rua !!!

1.7.3. Passando para funções

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 função strcpy, que vai copiar a string “Fulano de Talpara a variável nome (campo dentro da estrutura ficha).

Podemos também passar uma estrutura inteira como argumento para uma função. Veja a seguinte função:

void PreencheFicha (ficha_pessoal ficha)

{

}

Como vemos acima é fácil passar a estrutura como um todo para a função. Devemos observar que, como em qualquer outra função no C/C++, a passagem da estrutura é feita por valor. A estrutura que está sendo passada, vai ser copiada, campo por campo, em uma variável local da função PreencheFicha. Isto significa que alterações na estrutura dentro da função não terão efeito na variável fora da função. Mais uma vez podemos contornar este detalhe usando ponteiros e passando para a função um ponteiro para a estrutura.

1.7.4. Ponteiros para Estruturas Podemos ter um ponteiro para uma estrutura. Vamos ver como poderia
1.7.4. Ponteiros para Estruturas Podemos ter um ponteiro para uma estrutura. Vamos ver como poderia

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 seções:

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 variável struct já existente, da seguinte maneira:

ficha_pessoal ficha;

ficha_pessoal *p;

p

= &ficha;

A segunda é alocando memória 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);

p = new ficha_pessoal[n]; for ( int i=0; i<n; i++) {   cout<<endl<<"\t**** Dados da
p = new ficha_pessoal[n]; for ( int i=0; i<n; i++) {   cout<<endl<<"\t**** Dados da

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; //Atenção a alocação dinamica

system("pause"); return 0; } Há um detalhe importante. Se apontarmos o ponteiro p para uma
system("pause"); return 0; } Há um detalhe importante. Se apontarmos o ponteiro p para uma

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 poderíamos fazer:

(*p).nome

Os parênteses são necessários, porque o operador . tem precedência maior que o operador * . Porém, este formato não é muito usado. O que é comum de se fazer é acessar o elemento nome através do operador seta, que é formado por um sinal de "menos" (-) seguido por um sinal de "maior que" (>), isto é: -> . Assim faremos:

p->nome

A declaração acima é muito mais fácil e concisa. Para acessarmos o elemento CEP dentro de endereco faríamos:

p->endereco.CEP

Exercício:

Seja a seguinte struct que é utilizada para descrever os produtos que estão 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 instrução 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 posição 0 e os valores "Cocada Baiana", 15202 e R$0,50 aos membros da posição 1 da matriz anterior;

c) Faça as mudanças que forem necessárias para usar um ponteiro para Produto ao invés de uma matriz de Produtos. Faça a alocação de memória de forma que se possa armazenar 10 produtos na área de memória apontada por este ponteiro e refaça as atribuições da letra b;

d)Escreva as instruções para imprimir os campos que foram atribuídos na letra c.

1.8. Ponteiros para Funções Muito embora uma função não seja uma variável, ela ocupa uma
1.8. Ponteiros para Funções Muito embora uma função não seja uma variável, ela ocupa uma

1.8. Ponteiros para Funções

Muito embora uma função não seja uma variável, ela ocupa uma posição física na memória, e assim, essa posição pode ser atribuída a um ponteiro. O endereço de uma função é chamado de ponto de entrada desta função, pois, quando a função é compilada, o código-fonte é transformado em código-objeto e um ponto de entrada é estabelecido para ela. Um ponteiro para a função pode ser usado para chamar a função, pois ele pode apontar para o ponto de entrada desta função. O endereço de uma função é obtido usando o nome da função sem parênteses 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 função 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”; else cout << “ Diferente” ;

//nesse ponto, a função strcmp é chamada

}

Ao ser chamada a função check(), são passados dois strings s1 e s2 e um ponteiro p para função - que aponta para o ponto de entrada da função strcmp() - como argumentos. Na função check(), a função strcmp() é chamada através do ponteiro cmp (declarado no protótipo dela). Lembre-se de que cmp recebeu o conteúdo de p, que na verdade é o endereço da função strcmp()

2. A Função main() e seus argumentos 2.1. A função void main(int argc, char *argv[])
2. A Função main() e seus argumentos 2.1. A função void main(int argc, char *argv[])

2. A Função main() e seus argumentos

2.1. A função void main(int argc, char *argv[])

Muitas vezes é interessante passar parâmetros para um programa ao executá-lo. Esses argumentos são passados através da função main(). Um argumento na linha de comando é

a informação 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 parâmetro /p para o programa dir (lista conteúdo de diretório) que fará com haja uma pausa na listagem do conteúdo do diretório (tampem chamado de pasta) a cada vez que a tela do prompt é preenchida.

Observe o código 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, então, 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 espaço 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)

3. Alocação Dinâmica de Memória As funções básicas de alocação dinâmica de memória em C++
3. Alocação Dinâmica de Memória As funções básicas de alocação dinâmica de memória em C++

3. Alocação Dinâmica de Memória

As funções básicas de alocação dinâmica de memória em C++ são new e delete.

O exemplo abaixo ilustra o uso das função 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 função new está mostrado no exemplo abaixo. Observe que neste exemplo também mostramos situações onde um endereço de variável foi passado para uma função de modo que a função 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);

maior = procuraMaior (v, tam, &vezes); cout<<"O maior elemento e "<<maior; cout<<"
maior = procuraMaior (v, tam, &vezes); cout<<"O maior elemento e "<<maior; cout<<"

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 não confundir a notação:

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

4. Algoritmos de Ordenação e Pesquisa Duas das tarefas mais fundamentais e extensivamente utilizadas em
4. Algoritmos de Ordenação e Pesquisa Duas das tarefas mais fundamentais e extensivamente utilizadas em

4. Algoritmos de Ordenação e Pesquisa

Duas das tarefas mais fundamentais e extensivamente utilizadas em relação às estruturas de dados são: a ordenação e a pesquisa. Algorítmos para essas tarefas são utilizadas em praticamente todos os programas de bancos de dados, bem como compiladores, interpretadores e sistemas operacionais. Ordenação é o processo de arranjar um conjunto de informações semelhantes em uma ordem crescente ou decrescente, utilizando para isso, uma parte da informação chamada chave de ordenação. Por exemplo, em uma lista postal, pode-se escolher uma ordenação pelo CEP ou por ordem alfabética, bastando para isso escolher como chave o campo CEP ou o campo nome.

4.1. Ordenação Bolha

Observe o algoritmo abaixo:

//Ordenação 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 código, item é um ponteiro para uma matriz de caracteres a ser ordenanda e count é o número de elementos da matriz. Há dois laços. O laço mais externo faz a matriz ser varrida cout-1 vezes. Isso garante que, na pior das hipóteses, todo elemento estará na posição correta quando a função terminar. No laço interno são feitas as comparações e trocas.

Neste algoritmo é feito sempre o mesmo número de comparações 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 não, o número de passos do algoritmo continua o mesmo. Há outros algoritmos cujo esforço computacional é, na pior das hipóteses igual ao Bolha.

4.2. Ordenação por seleção

No algoritmo de ordenação por seleção, o elemento, cuja chave possua o menor valor, é
No algoritmo de ordenação por seleção, o elemento, cuja chave possua o menor valor, é

No algoritmo de ordenação por seleção, o elemento, cuja chave possua o menor valor, é selecionado e trocado pelo primeiro elemento da lista. Então, 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:

//Ordenação por Seleção 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 número de comparações seja o mesmo que para o Bolha, o número

de trocas no caso médio ( dados mais ou menos ordenados

) é muito menor.

4.3. Ordenação por inserção

Neste algoritmo, primeiramente são ordenados os dois primeiros elementos da lista. Em seguida, insere-se o terceiro elemento em sua ordenação em relação aos dois primeiros. Posteriormente, insere-se o quarto elemento em relação aos três primeiros, e assim vai até que todos os elementos estejam inseridos e ordenados. Observe o código abaixo:

//Ordenação por Insersão 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;

}

} } } Ao contrário dos algoritmos anteriores, o número de comparações depende de como
} } } Ao contrário dos algoritmos anteriores, o número de comparações depende de como

}

}

}

Ao contrário dos algoritmos anteriores, o número de comparações depende de como a lista está inicialmente ordenada. Se a lista estiver em ordem, o número de comparações será n- 1. Já se estiver fora de ordem, será um número muito grande, se aproximando do pior caso que é a Ordenação Bolha.

4.4. Ordenação Quicksort

A ordenação quicksort foi inventada por C.A.R. Hoare 1 e é superior a todas as

anteriores. Geralmente é considerado o melhor algoritmo de ordenação de propósito geral

atualmente disponível.

O quicksort é baseado na idéia de partição. O procedimento geral é selecionar um

valor, chamado de comparando e, então, fazer a partição da lista (ou matriz) de dados em duas seções: 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 seção recursivamente, até que toda a lista esteja ordenada. Observe o exemplo abaixo:

// Ordenação 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)

if (i < rigth) qs(item, i, rigth);

qs(item, left, j);

1 Sir Charles Antony Richard Hoare (Tony Hoare ou C.A.R. Hoare, nascido em 11 de janeiro de 1934) é um cientista da computação britânico, mais conhecido pelo desenvolvimento do Quicksort em 1960, o algoritmo de ordenação mais utilizado no mundo, e muito provavelmente o algoritmo mais usado dentre todos os tipos existentes

} A função quick() executa a chamada à função de ordenação principal qs(). Após a
} A função quick() executa a chamada à função de ordenação principal qs(). Após a

}

A função quick() executa a chamada à função de ordenação principal qs(). Após a primeira chamada de qs(), esta é chamada recursivamente até que todas as sub-seções sejam feitas e os dados sejam ordenados. em todos os exemplos, a chave é um caractere que está em uma determinada posição da matriz do tipo char apontada pelo ponteiro item.

5. Estruturas de Dados 5.1. Filas As filas Uma fila (queue) é uma seqüência de
5. Estruturas de Dados 5.1. Filas As filas Uma fila (queue) é uma seqüência de

5. Estruturas de Dados

5.1. Filas

As filas Uma fila (queue) é uma seqüência de n nodos de um determinado tipo, cujo comportamento simula uma fila de registros. Normalmente representamos uma lista por

uma seqüência de elementos separados por vírgula x1, x2, x3,

, xn. Onde n >= 0.

O número n de elementos é o comprimento da fila. Se n > 0 então 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 então a fila está vazia. Uma propriedade importante da fila é que os elementos são inseridos no final e removidos no início. O primeiro registro a ser inserido é o primeiro a sair :FIFO (first in first

Também chamada de lista linear de informações, 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:

a última pessoa a entrar será a última pessoa a sair: Imaginemos na figura acima como

Imaginemos na figura acima como uma fila de Banco, onde cada célula representa um cliente a ser atendido. Os clientes, naturalmente, são ordenados na ordem de chegada (aqui não 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 não tenha mais nenhum elemento (cliente) na fila, dizemos que a fila está vazia. Caso o número 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 (não pode guardar mais elementos).

empty – Verificar se a fila está vazia (não contém elementos)

• construct – Colocar a fila num estado “pronto” a ser utilizada (Prepará-la) Observe um
• construct – Colocar a fila num estado “pronto” a ser utilizada (Prepará-la) Observe um

construct – Colocar a fila num estado “pronto” a ser utilizada (Prepará-la)

Observe um modelo de fila simples (Schildt, 1997) onde são manipulados três elementos:

A, B e C:

1997) onde são manipulados três elementos: A, B e C: A função qstore() armazena os elementos

A função qstore() armazena os elementos A e B, em seguida, a função qretrieve() retira os dois

elementos, e por fim, a função qstore() armazena o elemento C. Observe nessa implementação, que há um apontador spos que é responsável por indicar a primeira posição vazia apta a armazenar um elemento. Há também o apontador rpos que indica a posição do próximo elemento a ser retirado da fila. Um vetor de elementos poderia ser utilizado para a construção 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 funções de retirada e inserção de elementos na fila:

as funções de retirada e inserção de elementos na fila: Função de inserção de um elemento

Função de inserção 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 endereço de um vetor de caracteres ( ex: um nome). Chamamos a atenção para o fato de que só podemos inserir um elemento se a fila não estiver cheia ( spos não pode apontar para MAX).

Na função qretrieve() os elementos são retirados.É claro que só será retirado elementos se houver
Na função qretrieve() os elementos são retirados.É claro que só será retirado elementos se houver
Na função qretrieve() os elementos são retirados.É claro que só será retirado elementos se houver

Na função qretrieve() os elementos são 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 próximo elemento a ser retirado, o elemento a ser retornado está indicado pela posição 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 conotação de fila sem fim, ou seja, é como se as pontas (início e fim) da fila ficassem ligadas, fechando um ciclo sem fim.

e fim) da fila ficassem ligadas, fechando um ciclo sem fim. Vamos tomar com exemplo, os

Vamos tomar com exemplo, os apontadores spos e rpos utilizados na fila simples do item anterior. Vamos imaginar que a fila tenha 12 posições como na figura acima. Neste caso, se chegássemos na posição MAX, mas anteriormente, tivéssemos retirado uns três elementos do início da fila, por que não reutilizar os espaços que vagos? É aí que entra a fila circular. Nela, enquanto houver espaço devido à recuperação dos dados e conseqüente atualização de rpos , que mostra a posição do próximo elemento a ser retirado, spos poderia avançar para reutilizar estes espaços. Em outras palavras, a fila circular somente estaria cheia se os índices de armazenamento e de recuperação de dados forem iguais. Caso contrário, a fila terá espaços vagos.