Escolar Documentos
Profissional Documentos
Cultura Documentos
LEEC
Estruturas de Dados e Operações
Dados e Algoritmos: o que são?
Dados
Conjunto de valores, que representam elementos informativos (ex: o
valor inteiro 74 pode indicar o peso de uma pessoa, o ano da revolução
do 25 de Abril, ...)
Os valores podem estar armazenados de forma permanente -
constantes (ex:p), ou podem ser substituídos em localizações
variáveis (ex: saldo da conta bancária, luz do semáforo, ...)
Algoritmo
Sequência de instruções que, a partir de vários dados (previamente
armazenados, ou indicados por entidades externas ao programa como
utilizadores ou ficheiros), calcula novos dados.
Ex: cálculo do maior divisor comum, determinação do valor das compras
num supermercado, ...
2 AED (IST/DEEC)
Que estrutura de dados escolher?
A escolha da forma como se armazenam os dados depende de
Operações que sejam mais frequentes no algoritmo a implementar
Quando o acesso e modificação são a base do algoritmo, as tabelas deverão
ser escolhidas
Quando a inserção e remoção são mais frequentes
Usar listas simples, se estas operações ocorrem sempre no início
(“STACK”)
Usar listas simples com ponteiro adicional para o fim da lista, se a inserção
se fizer no fim e a remoção no início (“QUEUE”)
Previsibilidade sobre a memória necessária:
O uso de tabelas pressupõe que se saiba que memória é suficiente, ou que se
defina um valor suficientemente alto que comporte todas as instâncias de
interesse.
Quando tal valor é incomportável, o único recurso razoável consiste em gerir
a memória dinamicamente.
3 AED (IST/DEEC)
Tipologia de dados [1]
Simples
Inteiros (int Index);
Reais (float Stock, double Income);
Caracteres (char Answer)
Estruturados
Tabelas (‘‘array’’) – estáticas ou dinâmicas: vários elementos do mesmo
tipo
Unidimensionais (Array1[N])
Multidimensionais (Array2[N1]...[Nk] ou Array2[N1,...,Nk])
Acervos (Heap[N])
Estruturas (‘‘struct’’): elementos de tipos possivelmente distintos
4 AED (IST/DEEC)
Tipologia de dados [2]
Dinâmicos: volume dos dados pode ser modificado durante a
execução do algoritmo
Listas
Lista simples
Pilha (“stack”), Fila (“queue”), Fila Prioritária (“priority queue”)
Duplamente ligadas, anéis
Árvores
Grafos
Composições
listas de listas, listas de estruturas, lista de tabelas, tabelas de listas, ...
5 AED (IST/DEEC)
Tabelas
Definição: Uma tabela (“array”) é uma série de localizações,
todas do mesmo tipo, acedidas por índices numéricos a partir
de 0.
Sintaxe: após a declaração base inserir uma das opções
{[Dimensão]}+
Restrições:
as dimensões são inteiros positivos.
Exemplos
int Tab1[4]; /* tabela de 4 inteiros */
char Tab2[3][2]; /* tabela de char 3 por 2 */
char Tab3[5][14][2]; /* tabela de char 5 por 14 por 2 */
6 AED (IST/DEEC)
Estruturas [1]
As tabelas apenas permitem alocar, estática ou dinamicamente,
“caixas” de memória para objectos do mesmo tipo;
Só inteiros, só reais, só caracteres...
Para agrupar objectos de tipos diferentes numa mesma
variável existem as estruturas
Exemplo:
#define SIZE 80
struct {
char nome[SIZE];
int idade;
float altura;
} pessoa;
7 AED (IST/DEEC)
Estruturas [2]
A partir do momento que se definem estruturas, mais ou
menos complexas, podem definir-se novos tipos
Exemplo:
typedef struct {
char nome[SIZE];
int idade;
float altura;
} pessoa;
8 AED (IST/DEEC)
Estruturas [3]
Uma estrutura pode ter qualquer número de campos e cada
campo pode ser qualquer um dos tipos definidos e/ou
ponteiros para esses tipos.
struct {
int numero;
pessoa aluno;
float *notas_de_cadeiras;
} aluno_IST;
9 AED (IST/DEEC)
Tipos de dados e Variáveis
Qualquer variável, em qualquer linguagem, tem um tipo de
dados associado
O tipo de dados define os valores e as operações possíveis
sobre esses valores
define portanto os valores que uma variável desse tipo pode tomar e as
operações que sobre ela podem ser feitas
Mas um tipo de dados é uma definição genérica, abstracta
a sua definição não corresponde a nenhuma variável
ao definir um tipo de dados não estamos a instanciar nenhuma variável
10 AED (IST/DEEC)
Tipos básicos [1]
Definição: Um tipo de dados define os valores e as
operações possíveis sobre esses valores.
Inteiros
int (o tipo mais usado; valores dependem da máquina:16 bits entre -32768 e
32767, 32 bits entre -215 e 215-1)
short (2 Bytes entre -32768 e 32767)
long (32 bits)
Os literais podem ter base decimal (ex: 27, -15), octal (iniciados por 0, ex:
031) ou hexadecimal (iniciados por 0x ou 0X, ex: 0x1f5, 0XFF).
Reais
double (precisão dupla) : o mais usado
float (precisão simples)
Os literais possuem duas partes
inteira e fraccionária, separadas por ponto (ex: -27.3, 9.5) ou
base e expoente, separadas por E (ex: 2.3E-2, 4E3).
11 AED (IST/DEEC)
Tipos básicos [2]
Caracteres
Char
Os literais são delimitados por “plicas”
carácter impresso (ex: 'a', '}')
12 AED (IST/DEEC)
Organização em C das definições
de tipos
Em C podem ser definidos novos tipos
A definição dos tipos, no C, é dividida em dois ficheiros:
A interface, num ficheiro «header» (.h):
Definição do tipo.
Protótipos das funções usadas para manipular objectos deste tipo, na forma
tipo-retorno nome(tipo par1, tipo par2,...);
13 AED (IST/DEEC)
Definição de tipo: exemplo [1]
14 AED (IST/DEEC)
Definição de tipo: exemplo [2]
Utilização do tipo (CLIENTE)
#include “Point.h”
main ()
{
Point Pt1, Pt2;
Number Numb;
Pt1 = create_point(1.0, 2.0);
Pt2 = create_point(3.7, 4.5);
Numb = distance(Pt1, Pt2);
...
}
15 AED (IST/DEEC)
Ponteiros
A linguagem C permite aceder a posições de memória
específicas por meio de ponteiros;
Para cada tipo é possível criar ponteiros;
Exemplo:
int N1;
Reserva uma posição de memória onde a variável “N1” irá morar;
&N1 é o endereço de memória onde o valor de “N1” é armazenado;
int *N2;
Reserva uma posição de memória, de nome “N2”, onde se poderá colocar o
endereço de um inteiro, ou do primeiro inteiro de uma tabela de
inteiros;
&N2 é o endereço de memória onde se poderá colocar um endereço
16 AED (IST/DEEC)
Listas [1]
Definição: uma lista é uma sequência de nós compostos por
um item, contendo informação específica do nó.
um apontador para o nó seguinte.
Um nó é uma estrutura recursiva (“recursive” ou “recurrent”)
Último elemento (terminal) aponta para vazio (NULL)
Os restantes elementos (recorrentes, ou indutivos), entre o primeiro e o
penúltimo, contêm um apontador para um elemento do mesmo tipo.
17 AED (IST/DEEC)
Listas [2]
Estrutura conceptual
Disposição em memória
“head”
   NULL



18 AED (IST/DEEC)
Listas [3]
Definição em C – Cada nó da lista é composto por :
Um item, que contém a informação específica ao elemento,
normalmente definido por declaração tipo:
typedef xxx Item;
Um apontador para o próximo elemento da lista.
19 AED (IST/DEEC)
Listas [4]
Declaração de uma instância
uma lista é representada por um apontador para uma estrutura de tipo
list
list *l;
Alocação de um elemento de lista
l = (list *) malloc(sizeof(list));
if (l == NULL) {
fprintf(stderr, “Não há mais memória...!\n”);
}
20 AED (IST/DEEC)
Listas [5]
Acesso a informação
list *l;
(*l).item e (*l).next permitem aceder aos dois campos da
estrutura.
Mais simplesmente, podemos usar o operador -> e escrever:
l->item, l->next
Elementos seguintes na lista:
l->item – item do primeiro elemento da lista,
l->next->item – item do segundo elemento da lista,
l->next->next->item – item do terceiro elemento da lista
21 AED (IST/DEEC)
Interface para Listas [1]
A interface do tipo list, é constituída por:
A definição do tipo list:
Ficheiro contendo a
#include “defs.h”
definição de Item
typedef struct _list
{
Item item;
struct _list *next;
} list;
22 AED (IST/DEEC)
Interface para Listas [2]
Inserção de um elemento a seguir a outro
void InsertNext(list *, list *);
23 AED (IST/DEEC)
Interface para Listas [3]
No ficheiro “defs.h” define-se o item
Por exemplo
24 AED (IST/DEEC)
Implementação para Listas [1]
Implementação do tipo list:
Alocação de um novo elemento:
list * NewElement(Item N)
{
list *El = (list *) malloc(sizeof(list));
El ->next = NULL;
El->item = N;
return(El);
}
25 AED (IST/DEEC)
Implementação para Listas [2]
Inserção de um elemento a seguir a outro
void InsertNext(list *pEl1, list *pEl2)
{
pEl1->next = pEl2->next;
pEl2->next = pEl1;
}
pEl2
item item item etc.
item
pEl1 pEl2
item item item etc.
pEl1
26 AED (IST/DEEC)
Implementação para Listas [2]
Inserção de um elemento a seguir a outro
void InsertNext(list *pEl1, list *pEl2)
{
pEl1->next = pEl2->next;
pEl2->next = pEl1;
}
pEl2
item item item etc.
item
pEl1 pEl2
item item item etc.
pEl1
27 AED (IST/DEEC)
Implementação para Listas [3]
Remoção de um elemento a seguir a outro
void DeleteNext(list *pEl)
{
list *Aux;
Aux = pEl->next->next;
FreeElement(pEl->next);
pEl->next = Aux;
}
pE1 aux
item item item etc.
primeiro
pE1 aux
item item item etc.
depois
28 AED (IST/DEEC)
Implementação para Listas [3]
Remoção de um elemento a seguir a outro
void DeleteNext(list *pEl)
{
list *Aux;
Aux = pEl->next->next;
FreeElement(pEl->next);
pEl->next = Aux;
}
pE1 aux
item item item etc.
primeiro
pE1 aux
item item item etc.
depois
29 AED (IST/DEEC)
Implementação para Listas [4]
Função para aceder ao elemento seguinte
list * Next(list *pEl)
{
return pEl->next;
}
30 AED (IST/DEEC)
Implementação para Listas [5]
Processamento de listas
Para percorrer uma lista,
for (Ptr = pFirst; Ptr != NULL; Ptr = Next(Ptr))
31 AED (IST/DEEC)
Implementação para Listas [6]
Função para reverter uma lista
32 AED (IST/DEEC)
Síntese da Aula 1 de Tipologia e
Operações
Introdução aos Dados e Algoritmos
Listagem de tipos de dados mais vulgares e critérios de
escolha
Tipologia de dados
Simples, Tabelas, Estruturas
Tipos de dados e variáveis
Tipos básicos
Organização em C das definições de tipos
Ponteiros
Listas simples
Interface para listas
Implementação para listas
33 AED (IST/DEEC)
Tipos abstractos [1]
Balanço dos tipos de dados no C (básicos, estruturados,
dinâmicos e composições)
Vantagens:
muito próximos dos tipos das linguagem máquina (Bit,Word)
manipulação eficiente
Inconvenientes:
Porque os algoritmos são expressos por instruções simples (=, if, do,...)
resultam muitas vezes em falhas de implementação
tipo de linguagem muito afastada do raciocínio dos analistas
Mesmo que o algoritmo seja igual, a alteração do tipo de dados obriga a
modificar o código (exemplo: se nas listas da aula anterior for alterado o tipo
do campo item, de int para char, a função newElement tem de ser modificada)
Solução: usar abstracções que permitem descrever o problema, deixando
os detalhes de implementação para depois!
34 AED (IST/DEEC)
Tipos abstractos [2]
Um tipo abstracto é
um tipo genérico de dados, dos quais não se conhece os valores
uma interface que define os acessos e as operações sobre os dados
O conjunto das propriedades algébricas da interface, que delimitam as
possíveis implementações.
A implementação transcreve o tipo abstracto, codificando
os programas que efectuam as operações definidas na
interface (satisfazendo as propriedades).
O uso permite compilar a implementação, instanciando o
tipo genérico para um dos tipos de C
Um programa que usa um tipo abstracto é um cliente. O
cliente não tem acesso à implementação.
35 AED (IST/DEEC)
Tipos abstractos [3]
Exemplo dos passos no desenvolvimento de um programa
Análise:
[Especificação] entram números codificados, sendo depois devolvido o valor
decifrado.
[Tipo abstracto de dados] operações: entrada e remoção de elementos de
uma lista genérica, cifra e descodificação.
Implementação:
Opção A: Tipo de dados por tabela, com índice para o primeiro índice livre
Opção B: Tipo de dados por lista simplesmente ligada
Cifra/descodificação por chave pública
Uso
(Opção B) O tipo de dados é uma lista simplesmente ligada de inteiros.
Compilação
0’s e 1’s, do processador
36 AED (IST/DEEC)
Contentores
Muitos tipos abstractos representam contentores.
Os contentores servem para armazenar um conjunto de items: listas,
pilhas, anéis, tabelas de dispersão (‘‘hash-tables’’), ...
Para definir um tipo abstracto para um contentor deve-se:
Definir a interface do tipo abstracto em função do tipo Item.
A interface inclui o ficheiro “Item.h” (que contém a definição do tipo);
Definir a implementação;
Cada cliente cria um ficheiro “Item.h”, consoante as suas
necessidades;
Compilar.
37 AED (IST/DEEC)
Exemplo: Pilha [1]
Operações abstractas
inicialização.
limpeza da pilha.
colocação de um elemento na pilha.
remoção de um elemento (o último colocado).
Indica se pilha está vazia.
Pilha.h Usando a mesma interface, podem
typedef enum {FALSE,TRUE} BOOL; ser definidas várias implementações ...
void Init(unsigned pNumb);/*parâmetro é comprimento máximo*/
int Empty();
void Push(Item pI);
Item Pop();
BOOL IsEmpty();
38 AED (IST/DEEC)
Exemplo: Pilha [2]
Algumas propriedades algébricas da pilha
TODO n: IsEmpty(Init(n)) = TRUE – pilha é sempre inicializada vazia
TODO i: IsEmpty(Push(i))=FALSE – depois de inserido um elemento, nunca a pilha fica vazia
TODO i: Pop(Push(i))=i – elemento removido da pilha é sempre o último a ser inserido
39 AED (IST/DEEC)
Exemplo: Pilha [3]
Implementação do tipo pilha usando uma tabela
Pilha.c
#include “Item.h” /* aqui o cliente define os dados */
#include “Pilha.h” /* aqui está definida a interface */
static Item *gP;
static int gN; /*primeiro índice livre*/
BOOL IsEmpty() {
return gN==0? TRUE : FALSE; }
...
40 AED (IST/DEEC)
Exemplo: Pilha [4]
Implementação do tipo pilha usando uma tabela
Exemplo
Push(i3)
i3
i2 i2
gN=2 i1 gN=3 i1
Vantagens:
Simplicidade.
Desvantagens:
Tamanho máximo limitado à partida.
41 AED (IST/DEEC)
Exemplo: Pilha [5]
Implementação do tipo pilha usando uma lista
Pilha.c
#include “Item.h” /* aqui o cliente define os dados */
#include “Pilha.h” /* aqui está definida a interface */
typedef struct pilha {
Item item;
struct pilha *next;
} pilha;
static pilha *gP;
void Init(unsigned pNumb) {
gP = NULL;}
void Push(Item pI) {
pilha *Top = (pilha *)malloc(sizeof(pilha));
Top->item = pI; Top->next = gP;
gP = Top;}
BOOL IsEmpty() {
return gP==NULL? TRUE : FALSE;} ...
42 AED (IST/DEEC)
Exemplo: Pilha [6]
Implementação do tipo pilha usando uma lista
Exemplo
gP i2 i1
Push(i3)
gP i3 i2 i1
Vantagens:
Cresce à medida que novos elementos são postos na pilha
Decresce quando se removem elementos
Desvantagens:
Ocupa mais memória (para os apontadores)
Mais lento (gestão de memória)
43 AED (IST/DEEC)
Exemplo: Pilha [7]
Utilização do tipo abstracto pilha por clientes
Vários clientes poderão usar diferentes definições para o tipo Item,
de modo a usar, por exemplo
44 AED (IST/DEEC)
Exemplo: Pilha [8]
46 AED (IST/DEEC)
Exemplo: Pilha [10]
Exemplo de aplicação do tipo abstracto de pilha: pretende-se validar
expressões aritméticas em notação pós-fixada (considerados apenas operadores
soma e multiplicação).
Explicação: Os operadores aritméticos binários, são colocados segundo a
notação:
In-fixada: entre os operandos (usada pelos humanos, mas obriga a definir prioridades-multiplicação
maior que soma, e usar parêntesis para tornear as prioridades.
Ex: 2 + 3 *4 [resultado 14]; (2 +3)*4 [resultado 20]
Pós-fixada: depois dos operandos (usada pelos computadores, cálculo efectuado logo que se
encontra o operador o que evita definição de prioridades e parêntesis)
Ex: 2 3 4 * + [resultado 14]; 2 3 + 4 * [resultado 20]
Método de cálculo de 2 3 4 * +
(a) inserir 2 na pilha
(b) inserir 3 na pilha
(c) inserir 4 na pilha
(d) detectada a multiplicação, retirar os dois operandos da pilha «4»,«3» e inserir o resultado
«12»
(e) detectada a soma, retirar os dois operandos da pilha «2»,«12» e inserir o resultado «14»
(f) Só há um operando na pilha: a expressão 2 3 4 * + é válida!
47 AED (IST/DEEC)
Exemplo: Pilha [11]
#include <stdio.h>
#include <string.h>
#include “Item.h” /* O cliente usa pilha de inteiros */
#include “Pilha.h”
Init(Dim);
for (Idx = 1; Idx <= Dim; Idx ++) {
if (!strcmp(pArgv[Idx],”+”)) Push(Pop() + Pop());
else if (!strcmp(pArgv[Idx],”*”)) Push(Pop() * Pop());
else Push(atoi(pArgv[Idx]));
}
Res = Pop();
if (IsEmpty()) printf(“Valido com resultado=%d\n”, Res);
else printf(“Expressao invalida!\n”);
}
48 AED (IST/DEEC)
Implementações
O cliente não depende da implementação.
O cliente não tem de saber como a pilha foi implementada.
Escolha-se a implementação na altura da edição de ligações
(‘‘link’’) do programa.
A escolha deve ser feita em função da aplicação.
Em muitos casos as diferenças estão na gestão da memória:
Uma gestão mais rígida (baseada em tabelas) tem o defeito que é preciso
conhecer o tamanho máximo à partida. O espaço ocupado em memória
corresponde sempre ao tamanho máximo.
Uma gestão mais flexível (baseada em listas, por exemplo) tem o defeito
de ser menos eficiente (tempo de computação) mas permite o
tratamento de problemas maiores devido a uma gestão mais apertada da
memória.
49 AED (IST/DEEC)
Tipos de 1ª classe
Os tipos abstractos que já vimos têm um defeito:
Só pode existir uma única instância do tipo por programa
Definida como variável estática na parte da implementação
O ideal seria poder usar os tipos abstractos da uma maneira
semelhante aos tipos básicos (int, float, char etc..).
Definição: Um tipo de 1ª classe é um tipo para o qual pode
haver muitas instâncias que podem ser atribuídas a variáveis do
programa. Os tipos de 1ª classe podem ser usados nas
declarações como os tipos básicos.
50 AED (IST/DEEC)
Exemplo: tipo complexo [1]
O tipo de 1ª classe complexo é definido pela interface:
complex.h:
typedef struct
{
float Re;
float Im;
} complex;
51 AED (IST/DEEC)
Exemplo: tipo complexo [2]
#include “complex.h”
complex InitComplex (float pRe, float pIm)
{
complex Res;
Res.Re = pRe;
Res.Im = pIm;
return Res;
}
52 AED (IST/DEEC)
Exemplo: tipo complexo [3]
O programa cliente calcula as raízes complexas da equação
𝑧𝑵 = 1. As raízes são dadas pela fórmula : cos(2pk/N) + i sin(2pk/N).
#include <math.h> Compilação requer biblioteca de funções
#include “complex.h” matemáticas: usar
main (int pArgc, char **pArgv) { gcc –o roots roots.c complex.c -lm
int Idx1, Idx2, pN;
complex t, x;
if (pArgc < 2) exit(1);
pN = atoi(pArgv[1]);
printf(“raízes para N=%d\n”, pN);
for (Idx1=0; Idx1 < pN; Idx1++) {
float r = 2.0 * M_PI * Idx1 / pN;
t = initComplex (cos(r), sin(r));
printf(“Z%d: %6.3f %6.3f\t“, Idx1, Re(t), Im(t));
for (x=t, Idx2=0; Idx2 < pN-1; Idx2++) x = mult(t,x);
printf(“Z%d^%d = %6.3f %6.3f\n”, Idx1, pN, Re(x),Im(x));}}
53 AED (IST/DEEC)
Exemplo: tipo complexo [4]
Neste exemplo, podemos observar que:
O tipo complex é usado como se fosse um tipo predefinido,
Certas linguagens de programação (por exemplo o Java) permitem a
definição de operadores (+,-,*,/, etc...) para tipos novos.
Em Java podíamos ter definido um novo * em vez de mult
Apesar de o cliente ser independente da implementação, tem acesso à
forma como foi implementado o tipo (a definição do tipo está visível na
interface)
Perigo potencial ...
54 AED (IST/DEEC)
Exemplo: tipo complexo [5]
Se o cliente tiver acesso à definição do tipo:
Tem a possibilidade de aceder directamente aos campos da
estrutura:
Pode usar t.Re em vez de Re(t).
Os programas clientes tornam-se dependentes da
implementação.
se a implementação for alterada (usando, por exemplo a
notação polar ou angular para os complexos), é necessário
alterar todos os ficheiros clientes.
A solução consiste em esconder a definição do tipo.
55 AED (IST/DEEC)
Complexos: 2ª versão [1]
A interface passa a ser: complex.h
typedef struct _complex complex;
56 AED (IST/DEEC)
Complexos, 2ª versão [2]
A implementação é alterada: complex.c
struct _complex {float Re; float Im;};
57 AED (IST/DEEC)
Complexos, 2ª versão [3]
O cliente não precisa de ser alterado.
Resumindo:
A implementação do tipo é 100% privada
Escondida na implementação
O uso é feito através de um ponteiro explícito
Todas as variáveis do tipo complexo são referências para o objecto
O interface funcional esclarece o mesmo
Não era possível implementar esta solução sem recorrer a um
apontador
O apontador permite criar um tipo sem especificar os pormenores
O compilador pode compilar um programa (interface + cliente) porque
um apontador tem um tamanho definido: 32bits (64 bits nas máquinas
recentes).
58 AED (IST/DEEC)
Síntese da Aula 2 de Tipologia e Operações
Tipos abstractos
Contentores
Pilhas
Implementação em tabela
Implementação em lista
60 AED (IST/DEEC)
Exemplo: Filas [2]
Implementação (usando uma lista):
Fila.c
A estrutura element define o nó base da fila
#include <stdlib.h> (formato de lista) e a estrutura fila, referida
#include <stdio.h> na interface, é definida
#include “Item.h”
#include “Fila.h”
struct fila
{
element * primeiro;
element * ultimo;
};
/* continua */
61 AED (IST/DEEC)
Exemplo: Filas [3]
A função new serve para alocar e inicializar um novo
elemento na lista:
element *FilaNew(Item vItem, element *pNext)
{
element *x = (element *) malloc(sizeof (element));
x->item = vItem;
x->next = pNext;
return x;
}
62 AED (IST/DEEC)
Exemplo: Filas [4]
Função que coloca um item na fila:
void FilaPut(F *pFila, Item vI)
{
if (pFila->primeiro == NULL) { /* a fila está vazia */
pFila->ultimo = FilaNew(vI, pFila->primeiro);
pFila->primeiro = pFila->ultimo; return; }
/* o item é colocado no fim da lista */
pFila->ultimo->next = FilaNew(vI, NULL);
pFila->ultimo = pFila->ultimo->next;
}
Fila após inicialização Fila com um elemento Fila com dois elementos
63 AED (IST/DEEC)
Exemplo: Filas [5]
A função get remove o primeiro elemento da fila:
64 AED (IST/DEEC)
Contentores
O exemplo do tipo fila demonstra a possibilidade de criar
tipos de 1ª classe para novos contentores.
Os contentores criados até agora dependem de um tipo
Item que representa o tipo de dados que se quer armazenar
no contentor
Um defeito desta abordagem é que num programa o tipo
Item só pode ser definido uma única vez.
Em consequência não podemos ter no mesmo programa, uma
fila de inteiros e uma fila de elementos de tipo «aluno».
65 AED (IST/DEEC)
Items
A solução consiste, como no caso dos tipos de 1ª classe, em
usar um ponteiro de modo a não especificar o tipo dos items.
Podemos manter a interface e indicar explicitamente a
utilização de pointeiros, ou
Redefinir o tipo Item é definido como:
typedef void *Item;
O tipo void * corresponde a um apontador para um
objecto cujo tipo é desconhecido.
Vamos agora, criar filas de vários tipos.
66 AED (IST/DEEC)
Filas de complexos
Para criar filas de complexos, o cliente pode usar o tipo
complex já definido:
F *pFilaC = FilaInit(10); /* declaração da fila */
complex *z = initComplex (1,1); /* declaração de um complexo */
67 AED (IST/DEEC)
Filas de matrizes [1]
Para definir filas de matrizes, temos de definir primeiro um
tipo abstracto matrix
Interface – Matrix.h
typedef struct _matrix matrix;
matrix *NewMatrix(int, int);
matrix *MatrixMult(matrix *, matrix *);
float MatrixElement(matrix *,int,int);
etc...
Implementação – Matrix.c
struct _matrix {
float ** valores;
int cols;
int rows;
};
etc...
68 AED (IST/DEEC)
Filas de matrizes [2]
69 AED (IST/DEEC)
Filas de matrizes [3]
Uma vez o tipo matrix definido, o cliente pode criar uma fila
de matrizes:
F *pFilaM = FilaInit(10);
matrix *m = NewMatrix(10,10);
Colocar m na fila:
FilaPut(pFilaM,(Item) m);
70 AED (IST/DEEC)
Listas duplamente ligadas [1]
As filas duplamente ligadas são usadas quando é preciso
percorrer a lista do início até ao fim e do fim até ao início.
Interface – dlist.h:
71 AED (IST/DEEC)
Listas duplamente ligadas [2]
Implementação – dlist.c
Definição do tipo dlist
struct dlist {
Item item;
struct dlist *next;
struct dlist *previous;
};
Dl *NewDlist() {
return ((Dl*) NULL);
}
72 AED (IST/DEEC)
Listas duplamente ligadas [3]
Inserção de um Item no início da lista
73 AED (IST/DEEC)
Listas duplamente ligadas [4]
Inserção de um Item no final da lista pela função AddAtEnd
Dl *last(Dl *lst)
{
Dl *ultimo = lst;
while (ultimo->next != NULL)
ultimo = ultimo->next;
return ultimo;
}
Dl *AddAtEnd(Item i, Dl *list) {
Dl *ultimo = last(list);
ultimo->next = malloc(sizeof(dlist));
ultimo->next->item = i;
ultimo->next->previous = ultimo;
ultimo->next->next = NULL;
return list;
}
74 AED (IST/DEEC)
Listas duplamente ligadas [5]
Para concatenar duas listas
Dl *Concat(Dl *lst1,Dl *lst2)
{
Dl *lst3 = last(lst1);
lst3->next = lst2; lst2->previous = lst3;
return lst1;
}
Vários acessores
Item First(Dl *lst) {return lst->item;}
Item Last(Dl *lst) {return last(lst)->item;}
Item Next(Dl *lst) {return lst->next->item;}
Item Previous(Dl *lst) {return lst->previous->item;}
75 AED (IST/DEEC)
Listas duplamente ligadas [6]
A função Find ilustra a utilidade de passar funções no argumento
Item Find(Item i, Dl *lst, int test(Item,Item)) {
while (!(test(i,lst->item)) && lst->next != NULL)
lst = lst->next;
if (lst->next == NULL)
if (test(i,lst->item))
return lst->item;
else return (Item)NULL; O terceiro argumento da função é uma função de
return lst->item; comparação entre 2 items. O utilizador escreve a
} função de teste em conformidade com o objectivo da
procura.
A função Apply aplica a função f a todos os elementos da lista
76 AED (IST/DEEC)
Síntese da Aula 3 de Tipologia e
Operações
Tipos abstractos de 1ª Classe
Filas de Complexos
Filas de matrizes
77 AED (IST/DEEC)
Apêndice
79 AED (IST/DEEC)
Conversão de tipos
A linguagem C permite alterar o tipo de variáveis, quando
conveniente e/ou necessário
Conversão implícita:
int Numb;
float Inflation;
Inflation = Numb / 3.5;
O operador / converte Numb para real antes de efectuar a divisão.
Conversão explícita (‘‘casting’’):
int Numb1, Numb2;
float Price;
Price = ((float) Numb1) / Numb2;
Para que o resultado final seja um float, é necessário converter
explicitamente um dos inteiros para float
80 AED (IST/DEEC)
Ponteiros
A linguagem C permite aceder a posições de memória
específicas por meio de ponteiros;
Para cada tipo é possível criar ponteiros;
Exemplo:
int N1;
Reserva uma posição de memória onde a variável “N1” irá morar;
&N1 é o endereço de memória onde o valor de “N1” é armazenado;
int *N2;
Reserva uma posição de memória, de nome “N2”, onde se poderá colocar o
endereço de um inteiro, ou do primeiro inteiro de uma tabela de
inteiros;
&N2 é o endereço de memória onde se poderá colocar um endereço
81 AED (IST/DEEC)
Ponteiros [2]
Exemplo (continuação):
int N1;
N1
??
N1 = 3;
N1
3
&N1
N1
3
&N1
82 AED (IST/DEEC)
Ponteiros [3]
Exemplo (continuação):
int *N2;
N2
?? 3
N2 = &N1;
N2
&N1 3
A alocação de
&N1 memória ocor-
re em qualquer
N2 = (int *) malloc(3*sizeof(int)); sítio, podendo
ser acima ou a-
N2 baixo do local
onde “mora” o
&N2[0] 3 ?? ?? ?? ponteiro.
&N1
&N2[0]
83 AED (IST/DEEC)
Ponteiros [4]
Exemplo (continuação):
N2[1] = 5;
N2
&N2[0] 3 ?? 5 ??
&N1
&N2[1]
printf(“%d”, *(N2+1));
•5
• porque N2+1 é &N2[1]: quando a variável for de tipo
ponteiro para tabela, + adiciona espaço de cada célula
printf(“%d”, *N2+1);
• ??+1
• porque *N2 é o conteúdo de N2[0] que contém ??.
84 AED (IST/DEEC)
Tabelas [1]
Definição: Uma tabela (“array”) é uma série de localizações,
todas do mesmo tipo, acedidas por índices numéricos a partir
de 0.
Sintaxe: após a declaração base inserir uma das opções
{[Dimensão]}+
Restrições:
as dimensões são inteiros positivos.
Exemplos
int Tab1[4]; /* tabela de 4 inteiros */
char Tab2[3][2]; /* tabela de char 3 por 2 */
char Tab3[5][14][2]; /* tabela de char 5 por 14 por 2 */
85 AED (IST/DEEC)
Tabelas [2]
Os índices são valores entre 0 e largura-1, para cada dimensão.
Exemplos:
Tab1[0]=0; /* correcto*/
Tab1[4]=-25; /* incorrecto: limite máximo é 3 */
Tab2[2][0]='?'; /* correcto*/
86 AED (IST/DEEC)
Tabelas [3]
As tabelas são inicializadas por ciclos de varrimento ou lista de
valores delimitada por { }.
Exemplos
/* tabela Tab1 inicializada com ciclo */
for (Idx=0; Idx<4; Idx++)
Tab1[Idx]=Idx;
/* tabela Tab1 inicializada com lista de valores */
Tab1 = {0,1,2,3};
87 AED (IST/DEEC)
Tabelas [4]
Em C, existe uma correspondência entre tabelas e ponteiros:
*Array Array[0]
*(Array + 1) Array[1]
*(Array + Idx) Array[Idx]
As instruções seguintes:
int Array[10]; int *Ptr;
Array[2] = 3;
Ptr = Array + 2;
printf(“%d\n”, *Ptr);
3
Escrevem no terminal o valor .....
88 AED (IST/DEEC)
Tabelas [5]
Quando o tamanho da tabela é conhecido apenas na altura da
execução deve-se alocar a memória de maneira dinâmica
#include <stdlib.h>
#include <stdio.h>
main (int pArgc, char *pArgv[])
{
int Numb = atoi(pArgv[1]);
int *Space = (int *) malloc(Numb * sizeof (int));
if (Space == NULL)
fprintf(stderr, “Memória insuficiente.\n”);
…
89 AED (IST/DEEC)
Tabelas multidimensionais [1]
A declaração
float Vect[10][20];
cria um vector de 10 elementos, em que cada um deles é um vector de 20
objectos do tipo float.
Esta tabela é gerada na compilação
Alocação dinâmica (gerada na execução do programa)
float ** Newmatrix(int pRows, int pColumns)
{
int Idx;
float **mat; /* mat é um apontador para uma tabela que contém float ! */
return mat;
}
90 AED (IST/DEEC)
Tabelas multidimensionais [2]
Exemplo:
float ** mat;
??
mat
mat = (float **) malloc(3*sizeof(float *));
&mat[0] ?? ?? ??
mat
&mat[0]
?? ?? ?? ?? ??
mat
93 AED (IST/DEEC)
Tabelas multidimensionais [5]
Exemplo: multiplicação de matrizes
float **Mult(float **pA, float **pB, unsigned pLA, unsigned pCB, unsigned
pS)
{
int IdxI,IdxJ,IdxK;
float **Res = Newmatrix(pLA,pCB);
/* uso da função */
#define LINEA 10
#define COLB 15
#define SIZE 20
float **ArrA = Newmatrix(LINEA,SIZE), **ArrB = Newmatrix(SIZE,COLB), **ArrC;
ArrC = Mult(ArrA, ArrB, LINEA, COLB, SIZE);
94 AED (IST/DEEC)
Cadeias de caracteres [1]
Definição: Uma cadeia de caracteres (“string”) é uma tabela
de caracteres, sendo obrigatoriamente '\0' o último
caracter.
Uma cadeia pode ser indicada entre aspas (ex: “perigo!” é uma
cadeia de 7+1=8 caracteres).
Numa tabela inicializada, o compilador de C determina a dimensão.
Exemplo
char Msg[]=“texto”; /* tabela 5+1=6 caracteres */
int Tab[]={-4,17,5}; /* tabela 3 inteiros */
Os ponteiros e a gestão dinâmica de memória facilitam o tratamento de
cadeias de caracteres.
95 AED (IST/DEEC)
Cadeias de caracteres [2]
Processamento de cadeias de caracteres
Exemplo: procura numa cadeia de caracteres
96 AED (IST/DEEC)
Cadeias de caracteres [3]
A biblioteca do C string.h oferece um conjunto de
funções para manipular cadeias de caracteres:
Função para copiar o conteúdo de uma cadeira de caracteres:
char *strcpy(char *dest, char *src);
O ponteiro dest deve ter a memória alocada suficiente para conter a
cadeia src.
Função que devolve uma cópia da cadeia de caracteres:
char *strdup(char *s);
Função que compara duas cadeias de caracteres:
int strcmp(char *s1, char *s2);
devolve um inteiro maior, igual ou menor do que 0 consoante a cadeia
s1 for menor, igual ou maior do que s2.
97 AED (IST/DEEC)
Estruturas [1] (omitem-se [2] e [3])
As tabelas apenas permitem alocar, estática ou dinamicamente,
“caixas” de memória para objectos do mesmo tipo;
Só inteiros, só reais, só caracteres...
Para agrupar objectos de tipos diferentes numa mesma
variável existem as estruturas
Exemplo:
#define SIZE 80
struct {
char nome[SIZE];
int idade;
float altura;
} pessoa;
98 AED (IST/DEEC)
Estruturas [4]
Fazer
no * aed;
equivale a fazer
??
aed
E fazer
aed = (no *) malloc(1*sizeof(no));
equivale a fazer
&aed.numero ?? ?? ??
aed
&aed.numero
numero aluno proximo
99 AED (IST/DEEC)
Estruturas [5]
E fazer
aed->proximo = (no *) malloc(1*sizeof(no));
equivale a fazer
 ?? ??  ?? ?? ??
aed

Notar que qualquer campo de uma estrutura que seja um ponteiro apenas
reserva o espaço para o ponteiro aquando da alocação;
Compete ao programador estar consciente de que tem ponteiros como
campos das estruturas. Tem de reservar posteriormente memória para
guardar os objectos apontados por esse ponteiro, assim como lembrar-se
de libertar essa memória.
100 AED (IST/DEEC)
Estruturas [6]
Como se liberta a memória dos objectos criados atrás
Correctamente
Free(aed->proximo);
Free(aed);
??
aed
Incorrectamente
Free(aed);
?? ?? ?? ??
aed

101 AED (IST/DEEC)
Tabela de listas
Como se cria uma tabela de listas?
list **tlst;
tlst = (list **) malloc(4*sizeof(list *));
&tlst[0] ?? ?? ?? ??
tlst
&tlst[0]
&tlst[1]
&tlst[2]
&tlst[3]
lst_lst *exemplo;
E as instruções
exemplo = (lst_lst *) malloc(sizeof(lst_lst));
exemplo->seguinte = NULL;
produzem
 ?? 00........00
exemplo

lista seguinte
104 AED (IST/DEEC)
Lista de listas [3]
Suponha-se que de seguida se faz
exemplo->seguinte = (lst_lst *)malloc(sizeof(lst_lst));
exemplo->seguinte->seguinte = NULL;
As instruções
ltab * xpto;
xpto = (ltab *) malloc(sizeof(ltab));
xpto->seguinte = NULL;
criam
 ?? 00........00
xpto

dlst * frente_verso;
frente_verso = (dlst *) malloc(sizeof(dlst));
frente_verso->anterior = frente_verso->posterior = NULL;
frente_verso
 ?? 00........00 00........00

anterior
objecto posterior
108 AED (IST/DEEC)
Listas duplamente ligadas [2]
Para acrescentar um novo elemento há que fazer
frente_verso
 ?? 00........00  ??  00........00

anterior anterior
objecto posterior objecto posterior

Implementado por
struct _aluno { char *nome; int num; };
void PrintAluno(Item a)
{
printf(“Aluno nº %d, %s\n”, Num((aluno *) a), Nome((aluno *) a));
}