AULA 20 - Programação 1 - Engenharia
ALOCAÇÃO DINÂMICA DE MEMóRIA
Objetivos
- apresentar a anatomia de um programa em execução;
- apresentar a alocação dinâmica de memória.
A área de heap e o layout de memória de um programa C
Neste link podemos ter uma ideia da anatomia de um programa na memória do computador.
http://shivacherukuri.blogspot.com.br/2011/03/memory-layout-in-cdata-segmentbss-code.html
Podemos observar que existe as seguintes áreas:
- TEXT: área onde está o código;
- BSS: dados estaticamente alocados e não inicializados;
- DATA: dados estaticamente alocados e inicializados;
- STACK: área de pilha (variáveis locais);
- HEAP: área de dados alocados dinamicamente.
Quando declaramos uma variável global da forma:
int x;
a variável x é alocada em uma área chamada BSS (dados não inicializados). Note que x possui uma área de memória reservada a ela (4 bytes) e cuja existência é o tempo de vida do programa em execução. Esta aŕea de BSS é zerada no início do programa de forma que todas as variáveis ali definidas começarão com o valor 0.
Da mesma forma, uma variável global da forma:
int y=10;
é alocada na área de DATA. A inicialização é definida normalmente na carga do programa. Os valores de inicialização são copiados para a área de DATA na carga do programa, a partir do arquivo executável.
Por vezes, o tamanho dos dados não são conhecidos antes da execução do programa. Neste caso, pode ser interessante criá-los dinamicamente e é neste ponto que entra a área de HEAP. Trata-se de uma área de memória, gerenciada a partir de funções da biblioteca do C.
As funções mais conhecidas são (http://en.wikipedia.org/wiki/C_dynamic_memory_allocation):
http://www.linuxjournal.com/article/4681 http://www.cs.cmu.edu/~guna/15-123S11/Lectures/Lecture08.pdf
Exemplo 1: Alocação dinâmica de números inteiros (exercício puramente didático):
#include <stdlib.h>
main()
{
int *px, *py; /*variáveis ponteiros para inteiro criadas no STACK */
int resultado; /* variável reasultado criada no STACK */
px = (int *) malloc(sizeof(int)); /* alocada área para um inteiro na área de HEAP */
/* ponteiro aponta para esta área */
*px = 5; /* colocado 5 na área apontada por px */
py = (int *) malloc(sizeof(int)); /* similara anterior para ponteiro py */
*py = 2;
resultado = *px + *py; /* soma os dois valores inteiros que estava na área de HEAP
e coloca o valor na variável resultado */
free (px); /* libera a área no HEAP que estava apontada por px */
px = NULL; /* zero o ponteiro para deixá-lo apontando para área indevida */
free (py);
py = NULL;
}
Observe o uso do operador sizeof para captura do tamanho do inteiro. Esta abordagem garante portabilidade.
Exemplo 2 - Retornando um ponteiro para uma área alocada dentro da função.
#include <stdio.h>
#include <stdlib.h>
/* a função retorna um ponteiro para inteiro, contendo o resultado da soma de a com b */
/* programa de caráter puramente demonstrativo */
int *soma_int(int a, int b)
{
int *p;
p = (int *)malloc(sizeof(int));
*p = a+b;
return p;
}
main()
{
int *meu_ptr;
meu_ptr = soma_int(10,15);
if (meu_ptr != NULL) {
printf("valor de soma 10 + 15 = %d\n", *meu_ptr);
free (meu_ptr);
meu_ptr=NULL;
}
}
Observe na sequência o problema que pode acontecer caso ja retorno para uma área indevida de memória.
A função retorna um ponteiro para uma área alocada na PILHA (STACK) que é uma espécie de área de rascunho para criação de variáveis locais... O C gera um warning mas gera um executável que produz resultados inesperados.
#include <stdio.h>
#include <stdlib.h>
/* a função retorna um ponteiro para inteiro, contendo o resultado da soma de a com b */
/* programa de caráter puramente demonstrativo */
int *soma_int(int a, int b)
{
int x; /* variável local criada no STACK */
x = a+b;
return &x; /* retorna um ponteiro para uma área que será liberada após a chamada da função */
}
main()
{
int *meu_ptr1, *meu_ptr2;
meu_ptr1 = soma_int(10,15);
meu_ptr2 = soma_int(20,35);
printf("valor de soma 10 + 15 = %d\n", *meu_ptr1);
printf("valor de soma 20 + 35 = %d\n", *meu_ptr2);
}
Alocando uma estrutura
No exemplo seguinte é alocada uma área dinâmica para uma estrutura.
#include <stdio.h>
#include <stdlib.h>
void main()
{
struct TTeste{
int x;
int y;
};
struct TTeste *teste;
teste = (struct TTeste *) malloc (sizeof(struct TTeste));
if (teste==NULL) {
printf("erro de alocação");
exit(1);
}
teste->x=10;
free(teste);
teste=NULL;
Observe que o retorno da função malloc é void, para torná-la genérica. O retorno deve ser convertido devidamente usando uma operação de casting (struct TTeste *)
Usando o typedef para ajudar na definição e declaração de estruturas
A palavra chave typedef no C permite a criação de APELIDOS para tipos (ver https://en.wikipedia.org/wiki/Typedef):
#include <stdio.h>
#include <stdlib.h>
void main()
{
typedef struct {
int x;
int y;
} TTeste;
TTeste *teste;
teste = (TTeste *) malloc (sizeof(TTeste));
if (teste==NULL) {
printf("erro de alocação");
exit(1);
}
teste->x=10;
free(teste);
teste=NULL;
}
Note que o uso do typedef simpificou o uso da estrutura no código.
Alocando dinamicamente uma tabela de estruturas
#include <stdio.h>
#include <stdlib.h>
void main()
{
struct TTeste{
int x;
int y;
} *teste;
if ((teste = (struct TTeste *) malloc (100*sizeof(struct TTeste)))==NULL) {
printf("erro de alocação");
exit(1);
}
teste[10].x= 5;
if ((teste = realloc(teste, 10000*sizeof(struct TTeste)))==NULL) {
printf("erro de alocação");
exit(1);
}
teste[9000].x=20;
free(teste);
}
Exercícios:
- Criar uma função que recebe dois números complexos como parâmetro e retorna um ponteiro para uma área dinamicamente criada, contendo a soma destes números. A função retorna NULL caso não tenha sido possível alocar a área para a sma.
- Criar uma função que recebe uma string passada como parâmetro e retorna um ponteiro para uma string (armazenada em uma área dinâmicamente criada) contendo uma cópia da string passada mas com caracteres capitalizados.