Aula 03 - 2 - Alocação Dinâmica de Memória em C++

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 dois operadores básicos para
gerência de memória:
– new(nº de bytes) - aloca memória.
– delete(endereço) - libera memória.
Operador new
• Protótipo:
void* operator new(size_t número_de_bytes);
• Detalhes:
– devolve um ponteiro do tipo do tipo alocado para o início (1º
byte) da área de memória alocada;
– 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.
StackPointer
Topo da Pilha
Topo da Memória
char *p;
int *q;
main() {
// Aloca 1000 bytes de RAM.
p=(char *) operator new(1000);
// Aloca espaço para 50
// inteiros.
q = new int[50];
}
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
Operador delete
• Protótipo:
void delete( 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
new;
• não é possível alocar um vetor enorme e depois desalocar a
parte dele que "sobrou“;
– 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;
Classe Lista {
// Vetor de ponteiros para caracter.
caracter *dados[MAXLISTA];
inteiro último;
};
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;
• Todas os métodos 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;
• 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 Classes (que você já implementou) vai
poder ser utilizado para isso.
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:
Classe Lançamento {
caracter *nome;
real valor;
};
Modelagem de um tipo Lista para
Débitos ou Créditos
• Pseudo-código:
constantes MAXLISTA = 100;
Classe ListaContábil {
Lançamento dados[MAXLISTA];
inteiro último;
};
Usando (pseudo-código)
• Crie variáveis globais:
ListaContá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:
ListaContabil debitos, creditos;
debitos.dados[2].valor = 5.0;
strcpy(debitos.dados[2].nome, buffer);
Dentro das funções:
Suponha: ListaContabil *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.
class estruturaDaPilha {
int topo;
int dados[MAXPILHA];
};
// Define um tipo que tem a estrutura da
// 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.cpp e em aplic.cpp;
• como aplic.cpp 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.
Classe: lista.h
#ifndef Lista
#define Lista
class Lista {
char *elemento[30];
int ultimo;
int max;
};
#endif
Módulo: lista.cpp
#include <stdio.h>
#include <stdlib.h>
#include “lista.h”
Lista::Lista() {
max = 30;
ultimo = -1;
}
Lista::~Lista() {
// Libera memória ocupada pelos Strings.
delete[] elemento;
}
Exercício de Implementação 4
Implementação de um programa com número
variável de filas com vetores alocadas
dinamicamente usando classe 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 a classe 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 objetos. Cada Fila deverá
ter no máximo 20 posições.
Download