MC102MN

Introdução a Algoritmos e programação de computadores

Aula XVI, Alocação dinâmica de memória

Sempre que manipulamos ponteiros, vetores e matrizes, tínhamos que saber em tempo de compilação qual seria o tamanho dos vetores e matrizes, e caso não soubéssemos o programa não poderia ser escrito. Na aula de hoje veremos a primeira parte de alocação dinâmica de memória.

Heap

Até agora, todas as variáveis que usamos em C ocupavam um espaço na memória que pertencia à sua função. Por isso sempre tínhamos que saber o tamanho do vetor na hora de definir uma função (senão o compilador não tinha como saber a posição das outras variáveis daquela função em tempo de compilação), não podíamos retornar um vetor de uma função (já que o espaço daquele vetor estava associado àquela função e deixaria de existir, etc). Esse tipo de memória é chamado de stack, porque funciona como se fosse uma pilha; a cada função que é entrada pelo código mais espaço é criado em cima da pilha de espaço utilizado, e quando se sai de uma função o espaço dela é jogado fora.

A memória utilizada por alocação dinâmica vem de um lugar chamado heap (o nome provavelmente foi escolhido por analogia com stack, mas não faz o menor sentido na prática). A heap é desordenada, e memória pode ser reservada e liberada nela a qualquer momento. A memória da heap, por isso, tem menos restrições que a de stack, e pode ser retornada de função e coisas assim. O único detalhe para o qual se tem que prestar atenção é que a memória da heap tem que ser liberada após o uso, e não se pode usar mais a memória que já foi liberada.

Como reservar e liberar memória da heap

No arquivo de cabeçalho

#include <stdlib.h>
estão as funções principais para lidar com alocação dinâmica de memória. A primeira é malloc. Ela funciona assim:
int *vetor_de_inteiros = malloc(Nelementos * sizeof(int));
Após fazer isso, a variável vetor_de_inteiros aponta para um espaço de memória no qual cabem Nelementos inteiros. O valor de Nelementos pode ser determinado em tempo de execução. Após usar esse vetor, antes de sair do programa, o programa tem que fazer
free(vetor_de_inteiros);
se ele não fizer isso acontece uma coisa chamada "vazamento de memória" que pode deixar o computador muito lento.

Outro ponto importante é o parâmetro passado para malloc. Ele é o espaço total de memória que tem que ser alocado. Normalmente, você vai alocar memória para um vetor de elementos de algum tipo, e para calcular o tamanho ocupado por esse vetor você deve fazer

<número de elementos> * sizeof(<tipo>)
por exemplo, um vetor com 100 struct pos (da aula passada) pode ser alocado assim:
struct pos *vetor = malloc(100 * sizeof(struct pos));

Como usar memória da heap

A memória da heap reservada com malloc sempre terá que ser acessada através de um ponteiro. Esse ponteiro se comporta como qualquer outro ponteiro. Por exemplo, podemos usar malloc para alocar vetores de tamanho arbitrário:

int *aloca_vetor(int n) {
  int *vetor = malloc(n*sizeof(int));
  return vetor;
}

e no main pode ser usado assim

int main(int argc, char *argv) {
  int n, *vetor, i;
  printf("Digite o numero de elementos: ");
  scanf("%d", &n);
  vetor = aloca_vetor(n);
  for (i = 0; i < n; ++i) {
    printf("digite o elemento %d: ", i);
    scanf("%d", &vetor[i]);
  }
  for (i = 0; i < n; ++i) {
    printf("o elemento %d é %d\n", i, vetor[i]");
  }
  free(vetor);
  return 0;
}

Com alocação dinâmica podemos definir estruturas com vetores de tamanho arbitrário, como por exemplo

struct vetor {
   int nelementos;
   double *v;
}

ou

struct matriz {
  int linhas, colunas;
  double *m;
}

e aí podemos fazer funções para alocar essas estruturas

struct matriz nova_matriz(int l, int c) {
   struct matriz m;
   m.linhas = l;
   m.colunas = c;
   m.m = malloc(l*c*sizeof(double))
   return m;
}

e fazer funções que utilizem essas estruturas

struct matriz multiplica(struct matriz a, struct matriz b) {
  struct matriz m;
  int i, j, k;
  m = nova_matrix(a.linhas, b.colunas);
  if (a.colunas != b.linhas) printf("ERRO!");
  ....
  return m;
}

onde a parte com … foi colocada lá para não entregar o exercício da semana passada; eu completarei depois.

Lidando com esses vetores alocados

Às vezes você quer copiar memória de um vetor pra outro; existe uma função para fazer isso e se chama memcpy,

memcpy(a, b, n);
copia os primeiros n bytes de b pra a. Se a e b são pedaços diferentes do mesmo vetor existe a função
memmove(a, b, n);
para fazer a mesma coisa.

Sugestões

Adaptar o código de vetores (soma, produto interno, etc) que fizemos lá no começo do semestre para lidar com a struct vetor ali em cima.

Slides

Os slides do rodolfo estão aqui.

Author: Alexandre Tachard Passos <alexandre.tp@gmail.com>

Date: 2010-10-07 17:34:50 BRT

HTML generated by org-mode 6.21b in emacs 23