Curso Profissional de Informática de Gestão
Módulo 7: Estruturas dinâmicas
LINGUAGENS DE PROGRAMAÇÃO
Conteúdos 1. Introdução Conceitos de estruturas dinâmicas 2. Regras de declaração de estruturas dinâmicas 1.
2. Técnicas de manipulação de informação em
estruturas dinâmicas 3. Noções de pilha e fila de espera 4. Operações básicas sobre listas unidireccionais e bidireccionais
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Conceitos básicos (Quase) tudo o que acontece num computador, passa-se em
memória – normalmente RAM. É na RAM que são carregados os nossos programas, jogos,
processadores de texto, etc. É também na RAM que são armazenadas as variáveis que fazem parte dos nosso programas. De facto a memória RAM pode ser vista como um enorme
vector de Bytes consecutivos. 100
101
102
…
5000
5001
5002
…
Sempre que declaramos uma variável, temos que indicar qual o
seu tipo e qual o seu nome:
char ch; ch
100
101
102
…
5000
5001
5002
…
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Conceitos básicos ch
100
101
102
…
5000
5001
5002
…
A variável ch ocupa, o Byte de memória número 5000. Para os programadores, é muito mais simples referenciar uma variável pelo seu nome de que referenciá-la pela posição que essa variável ocupa em memória. Quando se faz ch = ’A’; o que se está a dizer ao computador é “vai à posição 5000 e coloca o caracter ’A’”. É o compilador que realiza esta conversão e não o programador. Este apenas tem que se preocupar em escrever o programa correctamente. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Conceitos básicos NOTA NOTA: O compilador associa a cada variável uma posição ÚNICA em memória, capaz de suportar dados do tipo dessa variável. Sempre que num programa se faz referência a uma variável, estamos na realidade a referir o endereço ou conjunto de endereços que essa variável ocupa.
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Apontadores Os apontadores são um mecanismo particularmente flexível
de manipulação de dados, pois permitem manipular directamente dados contidos em endereços específicos de memória. Suponhamos então que tínhamos ao nosso dispor um
apontador denominado ptr. Como qualquer variável ptr ocupa uma posição em memória. ptr
100
ch
101
102
…
5000
5001
5002
…
Para colocarmos ptr a apontar para a variável ch, bastará
colocar o endereço de ch em ptr. Isso faz-se utilizando o operador & (Endereço Endereço de). de ptr = &ch; ptr
ch
5000 100
101
102
…
5000
5001
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
5002
…
Declaração de apontadores Um apontador é uma variável como qualquer
outra. O seu objectivo é armazenar o endereço de outra variável, que é, por sua vez, um número. A sintaxe da declaração de um apontador é a seguinte: tipo * ptr; Em que: – nome da variável do tipo apontador – tipo da variável à qual apontará – indica que é uma variável do tipo apontador
ptr tipo *
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Exemplos char a, b, *p, c, *q; int idade, *p_idade;
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Inicialização de Apontadores A inicialização de apontadores faz-se através
do operador Endereço de – &. Tal operador pode também ser utilizado para inicializar uma variável do tipo apontador no momento da sua declaração. Exemplo: int x = 5; float pi = 3.14; int *ptr_x = &x; float *pointer_to_pi = π Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Um bom hábito para evitar problemas na
programação é efectuar SEMPRE a inicialização dos apontadores. No entanto podem existir situações em que se declara um apontador que não aponta para variável alguma. Nesse caso pode colocar-se a apontar para NULL. A constante simbólica NULL, quando colocada
num apontador, indica que este não aponta para nenhuma variável.
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Apontadores e vectores O nome de um vector corresponde ao
endereço do seu primeiro elemento, isto é se &v[0] [0]. v for um vector v == &v [o nome de um vector não é mais do que o endereço do primeiro elemento do vector]
Embora o nome de um vector seja um
apontador para o primeiro elemento do vector, este apontador não pode ser alterado durante a execução do programa a que pertence. Se tal fosse possível, havia o risco de perder o vector previamente declarado. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Apontadores e vectores int v[3] = {10, 20, 30}; int *ptr; Existem duas formas de colocar o apontador ptr a apontar para o primeiro elemento de v ptr = &v[0]; ptr = v; Ao contrário de v, que é um vector, ptr é um apontador puro, pelo que pode ser alterado com endereços diferentes ao longo da execução do programa, não estando obrigado a apontar, eternamente, para o primeiro elemento do vector, como acontece com v. Pode assim apontar para cada um dos elementos do vector v. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Aritmética de apontadores Incremento / Decremento Um apontador pode ser incrementado como qualquer variável. No entanto o incremento de 1 unidade não significa que o endereço anteriormente armazenado no apontador seja incrementado um Byte. Na realidade, se ptr é um apontador para um determinado tipo, quando ptr é incrementado, por exemplo 1 unidade, o endereço que passa a conter é igual ao endereço anterior de ptr + sizeof(tipo) para que o apontador aponta, isto é, o apontador avança não um Byte mas sim a dimensão do tipo do objecto para o qual aponta. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Aritmética de apontadores Incremento / Decremento O decremento funciona da mesma forma que
o incremento anteriormente apresentado. Um apontador do tipo xyz recua sempre sizeof(xyz) Bytes por cada unidade de decremento.
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Diferença A operação de diferença entre dois
apontadores para elementos do mesmo tipo permite saber quantos elementos existem entre um endereço e outro. Por exemplo, o comprimento de uma string pode ser obtido através da diferença entre o endereço do caracter ’\0’ e o endereço do primeiro caracter (ou da string). A diferença entre apontadores só pode ser realizada entre apontadores do mesmo tipo. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Comparação É também possível a comparação entre dois
apontadores para o mesmo tipo, utilizando os operadores relacionais. A diferença e a comparação entre apontadores só pode ser realizada entre apontadores do mesmo tipo.
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Quadro-resumo Operação
Exemplo
Observações
Atribuição
ptr = &x
Podemos atribuir um valor (endereço) a um apontador. Se quisermos que aponte para nada podemos atribuir-lhe o valor constante NULL.
Incremento
ptr = ptr+2 Incremento de 2*sizeof(tipo) de ptr.
Decremento
ptr=ptr-10
Decremento de 10*sizeof(tipo) de ptr.
Apontado por *ptr
O operador asterisco permite obter o valor existente na posição cujo endereço está armazenado em ptr.
Endereço de
&ptr
Tal como qualquer outra variável, um apontador ocupa espaço em memória. Desta forma podemos saber qual o endereço que um apontador ocupa em memória.
Diferença
ptr1-ptr2
Permite saber qual o nº de elementos entre ptr1 e ptr2.
Comparação
ptr1>ptr2
Permite verificar, por exemplo, qual a ordem de dois elementos num vector, através do valor dos seus endereços.
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Apontadores e vectores – Acesso aos elementos Suponhamos a seguinte declaração:
char s[] = ”OlaOleOli”; char *ptr = s; /* ptr fica com o &s[0] */ Como poderemos aceder ao caracter ’a’ presente na string? s[2]
Caracter existente na posição índice 2 do vector
*(ptr+2)
Como ptr contém o endereço do primeiro caracter, se lhe adicionarmos 2, obtemos o endereço do caracter ’a’. Para obter o caracter ’a’ bastará usar o operador * (apontado por).
*(s+2)
Se s == &s[0] pode usar-se a mesma estratégia que foi usada no exemplo anterior.
ptr[2]
O endereçamento de elementos através de parêntesis rectos pode ser realizado também por apontadores, como se de um vector se tratasse. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Passagem de vectores para funções Sempre que se declara um vector, o seu espaço é alocado contiguamente na memória. A posição que este ocupa em memória pode ser obtida pelo nome do vector que contém o endereço do primeiro elemento. Sempre que é invocada uma função e passado um vector como parâmetro, esta na realidade não recebe o vector na sua totalidade, mas apenas o endereço inicial do vector, pois estamos a passar s == &s[0]. Se passamos um endereço, então a variável que o recebe terá que ser um apontador para o tipo dos elementos do vector. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Exemplo
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Passagem de vectores para funções Sempre que se passa um vector para uma
função, apenas o endereço original deste é efectivamente enviado para a função. É assim impossível saber, dentro de uma função, qual a dimensão dos vectores que forma passados, a menos que se envie um outro parâmetro indicador do nº de elementos ou um terminador em cada vector. É da responsabilidade do programador garantir que os vectores enviados para as funções contêm os elementos necessários ao processamento a que irão ser sujeitos. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Bibliografia Damas, Luís; LINGUAGEM C; FCA; 2ª edição; Lisboa;
1999 Aguilar, Luis Joyanes; FUNDAMENTOS DE PROGRAMAÇÃO – Algoritmos, estruturas de dados e objetos; Mc Graw Hill; Tradução da 3ª edição; São Paulo; 2008 Azul, Artur Augusto; BASES DE PROGRAMAÇÃO 10; Porto Editora; Porto http://www.eisnt.com/GPSI/programacao/Aula4C.pdf http://www.linhadecodigo.com.br/Artigo.aspx?id=425 http://www.criarweb.com/artigos/825.php http://deei.fct.ualg.pt/PI_flobo/teorica7.html http://www.di.ubi.pt/~fsilva/prog/Prog_Cap4.pdf (em 10/11/2008) Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Exercício Implemente a função:
char *strchr(char *str, char ch) Que devolve o endereço da primeira ocorrência de ch em s, caso não exista devolve NULL (o endereço e não o índice). Escreva um programa que solicite a string e um caracter e mostre no ecrã os caracteres da string original que se encontram a partir do caracter lido (inclusive).
RESOLUÇÃO Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Memória dinâmica Até agora para trabalhar com strings ou
vectores de outros tipos de dados, era absolutamente necessário saber, à partida, quantos elementos seriam necessários. Por exemplo, para criar duas strings iguais com um valor a introduzir pelo utilizador, declaram-se as duas strings com uma dimensão suficientemente grande, de tal modo que a string introduzida pelo utilizador possa ser copiada para o outro vector.
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Memória dinâmica
Porque razão é que a string que irá conter a cópia tem que ser declarada com determinada dimensão? Uma vez que o seu objectivo é conter uma cópia de outra string, deve ser criada apenas depois de se saber qual o espaço ocupado pela string introduzida pelo utilizador. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Memória dinâmica Poupa-se assim memória, ao evitar reservar
conjuntos elevados de memória que só seriam novamente libertados quando o programa ou a função onde foram declarados terminar. Todas as funções que tratam da alocação dinâmica de memória estão acessíveis através do ficheiro de cabeçalho stdlib.h
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Alocação de memória A alocação dinâmica de memória é realizada,
normalmente, através das funções malloc e calloc. calloc void *malloc(size_t nBytes) O objectivo é criar um Bloco constituído por nBytes Bytes e devolver o endereço desse bloco.
A função malloc permite alocar o conjunto de
Bytes indicados pelo programador, devolvendo um apontador para o Bloco de Bytes criados, ou NULL caso a alocação falhe. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Alocação de memória
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Alocação de memória Existe outra função que permite a alocação
de memória. void *calloc(size_t num, size_t size) Esta função permite criar, dinamicamente, num elementos, cada um com dimansão size Bytes.
A função calloc permite criar num elementos,
cada um deles com o mesmo número de Bytes (size). A função coloca todos os Bytes alocados com o valor zero. Devolve o endereço da zona criada ou NULL.
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Alocação de memória A função realloc permite alterar o número de
Bytes que estão presentemente associados a um bloco previamente criado utilizando a função malloc ou calloc. void *realloc(void *ptr, size_t new_size)
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Alocação de memória
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Alocação de memória Notas relativas a realloc realloc: Se o bloco actualmente alocado puder suportar a nova
dimensão, a memória adicional e também reservada e é retornado ptr. Se não existir espaço suficiente para prolongar o bloco, é então criado um novo bloco com a totalidade dos Bytes necessários. Os dados são copiados para a nova localização e é retornado o novo endereço. Se o parâmetro ptr é igual a NULL, então a função comporta-se como o malloc. Se, por algum motivo, não for possível a alocação de memória, ou se o número de Bytes for igual a zero é devolvido NULL. Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Libertar a memória A libertação da memória alocada através das
funções malloc, calloc e realloc pode ser realizada através da função: void free(void *ptr)
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Exercício Implemente a função strdup que cria uma
nova string exactamente igual à que lhe foi passada por parâmetro.
char *strdup(char *s) { char * tmp = (char *) malloc(strlen(s)+1); if (tmp != NULL) strcpy(tmp, s); return tmp; }
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Estruturas de dados dinâmicas Para demonstrar como se pode utilizar a memória dinâmica, vamos implementar uma estrutura clássica denominada FILA DE ESPERA. Uma fila caracteriza-se por manter a regra: “O primeiro a entrar é o primeiro a sair”. Como não sabemos à partida qual vai ser o número de indivíduos que pode ter que esperar, a fila terá que ter um crescimento dinâmico. Para tal é necessária uma estrutura composta pelos dados e por um apontador para a próxima posição, isto é, para a próxima pessoa da fila de espera. Neste caso, a fila será suportada por um apontador. PROGRAMA Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7
Bibliografia Damas, Luís; LINGUAGEM C; FCA; 2ª edição; Lisboa;
1999 Aguilar, Luis Joyanes; FUNDAMENTOS DE PROGRAMAÇÃO – Algoritmos, estruturas de dados e objetos; Mc Graw Hill; Tradução da 3ª edição; São Paulo; 2008 Azul, Artur Augusto; BASES DE PROGRAMAÇÃO 10; Porto Editora; Porto Em 10/11/2008
http://www.eisnt.com/GPSI/programacao/Aula4C.pdf http://www.linhadecodigo.com.br/Artigo.aspx?id=425 http://www.criarweb.com/artigos/825.php http://deei.fct.ualg.pt/PI_flobo/teorica7.html http://www.di.ubi.pt/~fsilva/prog/Prog_Cap4.pdf
Curso Profissional de Informática de Gestão - 2008/2011 - Módulo 7