Slide 1 - Moodle UFSC

Propaganda
INE5408
Estruturas de Dados
Alocação Dinâmica de
Memória
As Funções de Alocação Dinâmica de
Memória em "C"
• Alocação Dinâmica é um meio pelo qual o
programa pode obter memória enquanto está
em execução.
• Já visto até agora:
– Constantes são "codificadas" dentro do código
objeto de um programa em tempo de compilação.
– Variáveis globais (estáticas) têm a sua alocação
codificada em tempo de compilação e são
alocadas logo que um programa inicia a execução.
– Variáveis locais em funções (ou métodos) são
alocadas através da requisição de espaço na pilha
(stack).
#include <stdio.h>
char *a, *b;
StackPointer
Início da Pilha
Topo da Memória
int func_A () {
int local1, local2;
…
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Programa
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
#include <stdio.h>
char *a, *b;
StackPointer
Início da Pilha
Topo da Memória
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
#include <stdio.h>
char *a, *b;
StackPointer
Início da Pilha
Topo da Memória
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
&func_B-#2
local1
local2
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
&func_B-#2
local1
local2
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
&func_B-#3
local1
local2
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
&func_B-#3
local1
local2
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Topo da Memória
#include <stdio.h>
char *a, *b;
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
StackPointer
Início da Pilha
&main-#3
localA
localB
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
#include <stdio.h>
char *a, *b;
StackPointer
Início da Pilha
Topo da Memória
int func_A () {
int local1, local2;
…
}
void func_B () {
int localA, localB;
localA = func_A();
localB = func_A();
}
main () {
a = "Essa aula é legal";
b = "Será mesmo?"
func_B();
}
HeapPointer
Início da Área
Alocável
Variáveis estáticas
Código objeto
Constantes
a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
Base da Memória
Alocação Dinâmica em "C"
• A memória alocada pelas funções de
alocação dinâmica é obtida do heap.
– O heap é a região de memória livre que se
encontra entre o programa (com a área de
armazenamento permanente) e a pilha (stack).
– O tamanho do heap é, a princípio, desconhecido
do programa.
• "C" possui duas funções básicas para
gerência de memória:
– malloc(nº de bytes) - aloca memória.
– free(endereço) - libera memória.
Função malloc()
• Protótipo:
void *malloc(size_t número_de_bytes);
• Detalhes:
– devolve um ponteiro do tipo void (sem tipo) para o início (1º
byte) da área de memória alocada;
– isto significa que o valor deste ponteiro pode ser atribuído a
qualquer variável do tipo ponteiro.
• Para isto deve ser utilizado sempre um typecasting.
Ex.: se x é um ponteiro para inteiro, então:
x = (int *) malloc( sizeof(int) );
– número_de_bytes é a quantidade de bytes alocada;
– se a memória for alocada no topo do heap, o heapPointer é
atualizado (incrementado de número_de_bytes);
– o tipo size_t é definido em stdlib.h.
#include <stdlib.h>
#include <stdio.h>
char *p;
int *q;
main() {
// Aloca 1000 bytes de RAM.
p = (char *) malloc(1000);
// Aloca espaço para 50
// inteiros.
q = (int *)
malloc(50*sizeof(int));
}
StackPointer
Topo da Pilha
Topo da Memória
HeapPointer
Topo da Área
Alocável
50*int = 200 bytes
1000 bytes
Variáveis estáticas
Código objeto
p
q
10010101...
Constantes
Sist.Operacional
Base da Memória
Topo da Memória
• malloc devolve:
• um ponteiro para a área alocada;
• o ponteiro nulo (NULL) caso não
seja possível alocar a memória
requisitada.
• Convém verificar se foi possível alocar
a memória.
#include <stdio.h>
#include <stdlib.h>
char *p;
main() {
................
// Tenta alocar 1000 bytes de
// RAM.
p = malloc(1000);
// Testa se p é igual a
// NULL.
if (p == NULL)
printf(“%s”, "Sem memória!");
}
Espaço de variáveis
locais alocado
StackPointer
Topo da Pilha
HeapPointer
Topo da Área
Alocável
Variáveis estáticas
Já alocado antes
p
Código objeto
10010101...
Constantes
"Sem memória"
Sist.Operacional
Base da Memória
Função free()
• Protótipo:
void free( void *p );
• Detalhes:
– devolve memória previamente alocada ao sistema;
– a memória devolvida é aquela que foi alocada com um
ponteiro com o valor de p:
• o valor de p deve ser um valor que foi alguma vez retornado por
malloc();
• não é possível alocar um vetor enorme e depois desalocar a
parte dele que "sobrou“;
– a utilização de free() com um valor de ponteiro qualquer
poder ter resultados catastróficos;
– a gerência de buracos no heap é responsabilidade do
sistema operacional.
Exercício:
Lista com um vetor de Ponteiros para Strings
• Uma lista ordenada pode conter Strings de qualquer
comprimento < 10000;
• esta lista tem um número de elementos máximo fixo (100) e é
implementada como um vetor de ponteiros para Strings;
– utilize as rotinas de lista com vetor que você implementou
para a agenda.
• Um novo String é lido primeiramente para dentro de uma
variável auxiliar qualquer;
– então é alocada memória para exatamente o seu tamanho e
ele é copiado para esta área. Para copiar um String utilize
strcpy();
– por fim um lugar na lista é encontrado para ele. A posição
escolhida do vetor de ponteiros da lista é instanciada através
da atualização dos valores do ponteiro da posição do String
na lista com o endereço do string.
Modelagem da estrutura
1
Strings lidos
do usuário
e alocados
no Heap
S a b ã o \0
C o n s t i t u i r \0
Lista com
Vetor de
Ponteiros
Modelagem da Lista
• Pseudo-código:
constantes MAXLISTA = 100;
tipo tLista {
// Vetor de ponteiros para caracter.
caracter *dados[MAXLISTA];
inteiro último;
};
• Importante: observe que criando uma variável do tipo tLista você
não vai estar alocando memória para os strings a serem lidos,
apenas para os ponteiros para eles.
Topo da Memória
Organização de
memória para o
exercício
StackPointer
Topo da Pilha
Espaço de variáveis
locais alocado
HeapPointer
Topo da Área
Alocável
str4
str3
str2
str1
Var.Estáticas
Código objeto
10010101...
Constantes
Sist.Operacional
Base da Memória
• Para verificar o comprimento de um String:
– utilize a função strlen();
– esta função devolve o comprimento (em caracteres imprimíveis) de
um string.
– Protótipo: int strlen(char *p);
#include <stdio.h>
#include <stdlib.h>
#include <sting.h>
char p[90] = "Carro";
main() {
printf("%i", strlen(p));
}
• Imprime: 5
• Para copiar um String:
– utilize a função strcpy();
– esta função copia o conteúdo de um string (dado por um apontador)
para a posição de memória dada por outro apontador.
– Protótipo: char *strcpy(char *destino, char *fonte);
#include <stdio.h>
#include <stdlib.h>
#include <sting.h>
char p[90] = "Carro";
char lata[20];
main() {
strcpy(lata, p));
printf("s%", lata);
}
• Imprime: Carro
Detalhes:
Lista Ordenada com um vetor de ponteiros para Strings
• Como você não sabe o comprimento do String que o
usuário vai digitar, use primeiro uma variável auxiliar
grande (10000 posições) para guardar o que foi
digitado;
• a lista deve ser passada como parâmetro para todas
as funções que a utilizam, bem como as variáveis de
controle da lista;
• todas as funções de lista ordenada implementadas
anteriormente devem ser reimplementadas para
utilizar estes Strings;
• para a leitura de um String utilize scanf("%s",
entrada).
Exercício 2:
Trabalho com Passagem de Parâmetros
• Agora você vai fazer um programa que manipula mais de uma
lista;
• o programa fará isto com um único conjunto de funções e
passagem das diversas listas como parâmetros;
• como aplicação imaginemos um sistema de contabilidade
simples;
• você vai ter um Plano de Contas constituído por duas listas:
débitos e créditos;
• o mesmo conjunto de funções (que você já implementou) vai
poder ser utilizado para isso: você somente precisa ampliar o
conjunto de parâmetros da função para passar por referência
também a lista que você quer alterar.
– A passagem de parâmetro da lista deve ser por referência porque
você deseja que as alterações sejam persistentes.
Modelagem de um Lançamento
• Cada lista de débitos ou créditos é constituída
por lançamentos. Cada lançamento possui:
– um valor real (positivo);
– um nome. Por exemplo, “Pagar proteção à Mafia”
• Estrutura:
tipo tLançamento {
caracter *nome;
real valor;
};
Modelagem de um tipo Lista para
Débitos ou Créditos
• Pseudo-código:
constantes MAXLISTA = 100;
tipo tListaContábil {
tLançamento dados[MAXLISTA];
inteiro último;
};
• Importante: observe que criando um vetor de
lançamentos, você não vai estar reservando memória
para os nomes destes, pois o campo nome é só um
ponteiro.
Usando (pseudo-código)
• Crie variáveis globais:
tListaContábil débitos, créditos;
• Passe estas variáveis como parâmetros por
referência:
adiciona(&débitos, nomeLanc, valorLanc);
• Cabeçalho:
Inteiro FUNÇÃO adiciona(tListaContábil *plano;
caracter *nome;
real valor);
• Importante: nome é passado como ponteiro para
caracter. Use um buffer global para ler o nome do
lançamento do usuário.
Modelagem da estrutura
R$ 5,00
1
Strings lidos
do usuário
e alocados
no Heap
S a b ã o \0
R$ 505,00
P a s s a g e n s \0
Lista de débitos ou
de créditos com Vetor
de Estruturas do tipo
Lançamento
Usando (código “C”)
• Referencie diferentemente se estiver usando
ponteiros para a lista ou a lista diretamente:
tListaContabil debitos, creditos;
debitos.dados[2].valor = 5.0;
strcpy(debitos.dados[2].nome, buffer);
Dentro das funções:
Suponha: tListaContabil *ponteiro
e ponteiro = &debitos;
ponteiro->dados[2].valor = 5.0;
strcpy(ponteiro->dados[2].nome, buffer);
Headerfile: como garantir Inclusão Única
// Arquivo: pilha.h
#ifndef EstruturaDaPilha
#define EstruturaDaPilha
// Definir uma estrutura para a pilha.
struct estruturaDaPilha {
int topo;
int dados[MAXPILHA];
};
// Define um tipo que tem a estrutura da
// pilha.
typedef struct estruturaDaPilha pilha;
#endif
Headerfiles: Importante
• A diretiva de compilação #ifndef (if not defined) diz que aquela
área de código fonte entre o #ifndef e o #endif somente será
levada em conta pelo compilador se o argumento de #ifndef
ainda não houver sido definido na mesma sessão de compilação
no escopo de um módulo;
• isso garante que código que a gente "por via das dúvidas" inclui
mais de uma vez em um módulo não seja considerado duas
vezes;
• um exemplo de como isto é útil está na diretiva #include
<stdio.h> que está presente tanto em pilha.h quanto em pilha.c e
em aplic.c;
• como aplic.c carrega pilha.h "para dentro" de si mesmo,
carregará também stdio.h. Como está explicitamente também
carregando stdio.h, se não houver uma diretiva #ifndef em
stdio.h, ele terá o mesmo código existente em stdio.h duas
vezes.
Módulo: lista.h
#ifndef Lista
#define Lista
typedef struct estruLista {
char *elemento[30];
int ultimo;
int max;
};
typedef struct estruLista lista;
#endif
Módulo: lista.c
#include <stdio.h>
#include <stdlib.h>
#include “lista.h”
lista *criaLista() {
lista *nova;
nova = malloc( sizeof(lista) );
nova->max = 30;
nova->ultimo = -1;
return (nova);
}
void destroiLista(lista *morta) {
int i;
// Libera memória ocupada pelos Strings.
for (i=0; morta->ultimo; i++)
free( morta->elemento[i] );
// Libera memória da lista.
free( morta );
}
Exercício de Implementação 4
Implementação de um programa com número
variável de filas com vetores alocadas
dinamicamente usando o TAD lista com vetor.
Este trabalho é bem fácil porque você já implementou
praticamente tudo o que precisa. Só tem agora que modificar
um pouco a forma de gerenciar os seus dados.
Tome o TAD Fila com Vetor que você já implementou e crie um
programa para gerenciar uma lista contendo um número
variável e menor do que 20 desses TADs. Cada Fila deverá ter
no máximo 20 posições.
Para tanto adapte o TAD Lista com Vetor que você implementou
de forma que cada elemento da lista seja um tipo ponteiro para
um TAD Fila. Quando você for inserir uma nova fila, você a cria
dinamicamente, alocando memória para ela e inicializando os
seus campos de controle.
Download