1) O documento discute alocação dinâmica de memória em C usando as funções malloc() e free();
2) malloc() aloca memória no heap de forma dinâmica durante a execução do programa e retorna um ponteiro para essa área, enquanto free() libera a memória alocada anteriormente;
3) A alocação dinâmica permite que programas C alocam memória de forma flexível de acordo com as necessidades em tempo de execução.
2. 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).
3. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
P
r
o
g
r
a
m
a
Variáveis estáticas
Código objeto
Constantes
#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();
}
4. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
5. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
6. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
7. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
8. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
&func_B-#2
local1
local2
9. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
&func_B-#2
local1
local2
10. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
11. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
&func_B-#3
local1
local2
12. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
&func_B-#3
local1
local2
13. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
&main-#3
localA
localB
14. a
b
10010101...
"Essa aula é ...
"Será mesmo..
Sistema Operacional
HeapPointer
Início da Área
Alocável
StackPointer
Início da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
#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();
}
15. 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.
16. 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.
17. #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));
}
Sist.Operacional
10010101...
p
q
HeapPointer
Topo da Área
Alocável
StackPointer
Topo da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
1000 bytes
50*int = 200 bytes
18. • 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!");
}
Sist.Operacional
"Sem memória"
10010101...
p
HeapPointer
Topo da Área
Alocável
StackPointer
Topo da Pilha
Topo da Memória
Base da Memória
Variáveis estáticas
Código objeto
Constantes
Já alocado antes
Espaço de variáveis
locais alocado
19. 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.
20. 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.
21. Modelagem da estrutura
1
Lista com
Vetor de
Ponteiros
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
22. 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.
23. Organização de
memória para o
exercício
Sist.Operacional
10010101...
HeapPointer
Topo da Área
Alocável
StackPointer
Topo da Pilha
Topo da Memória
Base da Memória
Código objeto
Constantes
str4
str3
str2
str1
Espaço de variáveis
locais alocado
Var.Estáticas
24. • 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
25. • 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
26. 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).
27. 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.
28. 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;
};
29. 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.
30. 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.
31. Modelagem da estrutura
1
Lista de débitos ou
de créditos com Vetor
de Estruturas do tipo
Lançamento
Strings lidos
do usuário
e alocados
no Heap
S a b ã o 0
P a s s a g e n s 0
R$ 5,00
R$ 505,00
32. 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);
33. 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
34. 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.
35. Módulo: lista.h
#ifndef Lista
#define Lista
typedef struct estruLista {
char *elemento[30];
int ultimo;
int max;
};
typedef struct estruLista lista;
#endif
36. 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 );
}
37. 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.