As declarações abaixo alocam espaço na memória para algumas variáveis. A alocação é estática (nada a ver com a palavra-chave static), ou seja, acontece antes que o programa comece a ser executado:
char c; int i; int v[10];
Em muitas aplicações, a quantidade de memória a alocar só se torna conhecida durante a execução do programa. Para lidar com essa situação é preciso recorrer à alocação dinâmica de memória. A alocação dinâmica é administrada pelas funções malloc, realloc e free, que estão na biblioteca stdlib. Para usar essa biblioteca, inclua a correspondente interface no seu programa:
#include <stdlib.h>
Sumário:
A função malloc (o nome é uma abreviatura de memory allocation) aloca espaço para um bloco de bytes consecutivos na memória RAM (= random access memory) do computador e devolve o endereço desse bloco. O número de bytes é especificado no argumento da função. No seguinte fragmento de código, malloc aloca 1 byte:
char *ptr; ptr = malloc (1); scanf ("%c", ptr);
O endereço devolvido por malloc é do tipo genérico void *. O programador armazena esse endereço num ponteiro de tipo apropriado. No exemplo acima, o endereço é armazenado no ponteiro ptr, que é do tipo ponteiro-para-char. A transformação do ponteiro genérico em ponteiro-para-char é automática; não é necessário escrever ptr = (char *) malloc (1);.
A fim de alocar espaço para um objeto que precisa de mais que 1 byte, convém usar o operador sizeof, que diz quantos bytes o objeto em questão tem:
typedef struct { int dia, mes, ano; } data; data *d; d = malloc (sizeof (data)); d->dia = 31; d->mes = 12; d->ano = 2016;
Note que sizeof não é uma função mas um operador, tal como return, por exemplo. Os parênteses na expressão sizeof (data) são necessários porque data é um tipo-de-dados (os parênteses são análogos aos do casting). O operador sizeof também pode ser aplicado diretamente a uma variável: se var é uma variável então sizeof var é o número de bytes ocupado por var. Poderíamos ter escrito d = malloc (sizeof *d) no exemplo acima.
int *v; v = malloc (10 * sizeof (int));
x = malloc (10 * sizeof *x);
As variáveis alocadas estaticamente dentro de uma função, também conhecidas como variáveis automáticas ou locais, desaparecem assim que a execução da função termina. Já as variáveis alocadas dinamicamente continuam a existir mesmo depois que a execução da função termina. Se for necessário liberar a memória ocupada por essas variáveis, é preciso recorrer à função free.
A função free desaloca a porção de memória alocada por malloc. A instrução free (ptr) avisa ao sistema que o bloco de bytes apontado por ptr está disponível para reciclagem. A próxima invocação de malloc poderá tomar posse desses bytes.
Nunca aplique a função free a uma parte de um bloco de bytes alocado por malloc (ou realloc). Aplique free apenas ao bloco todo.
int *v; v = malloc (100 * sizeof (int)); v[0] = 999; free (v+1);
int *primos (void) { int v[3]; v[0] = 1009; v[1] = 1013; v[2] = 1019; return v; }
celula *acrescentaCabeca (celula *lst) { celula cabeca; cabeca.prox = lst; return &cabeca; }
int *p, *q; p = malloc (sizeof (int)); *p = 123; q = malloc (sizeof (int)); *q = *p; q = p; free (p); free (q); // má ideia... q = NULL; // boa ideia
Eis como um vetor (= array) com n elementos inteiros pode ser alocado (e depois desalocado) durante a execução de um programa:
int *v; int n; scanf ("%d", &n); v = malloc (n * sizeof (int)); for (int i = 0; i < n; ++i) scanf ("%d", &v[i]); . . . free (v);
(A propósito, veja a seção Aritmética de endereços no capítulo Endereços e ponteiros.) Do ponto de vista conceitual, mas apenas desse ponto de vista, a instrução
v = malloc (100 * sizeof (int));
tem efeito análogo ao da alocação estática
int v[100];
Matrizes. Matrizes bidimensionais são implementadas como vetores de vetores. Uma matriz com m linhas e n colunas é um vetor de m elementos cada um dos quais é um vetor de n elementos. O seguinte fragmento de código faz a alocação dinâmica de uma tal matriz:
int **M; M = malloc (m * sizeof (int *)); for (int i = 0; i < m; ++i) M[i] = malloc (n * sizeof (int));
Portanto, M[i][j] é o elemento de M que está no cruzamento da linha i com a coluna j.
Às vezes é necessário alterar, durante a execução do programa, o tamanho de um bloco de bytes que foi alocado por malloc. Isso acontece, por exemplo, durante a leitura de um arquivo que se revela maior que o esperado. Nesse caso, podemos recorrer à função realloc para redimensionar o bloco de bytes.
A função realloc aumenta o tamanho de um bloco de memória que tenha sido alocado dinamicamente. A função recebe o endereço de um bloco anteriormente alocado por malloc (ou pelo próprio realloc) bem como o número de bytes que o novo bloco deve ter. A função aloca um bloco do tamanho desejado, copia para ele o conteúdo do bloco original, e devolve o endereço do novo bloco. A função libera o bloco original invocando free.
A função realloc também pode diminuir o tamanho de um bloco de memória alocado dinamicamente. É o que acontece se o número de bytes solicitado for menor que o tamanho do bloco original. Nesse caso, não há alocação de memória nem movimentação do conteúdo do bloco, apenas redução do tamanho do bloco. O endereço do novo bloco é o mesmo do original.
Suponha, por exemplo, que alocamos um vetor de 1000 inteiros e depois decidimos que precisamos de duas vezes mais espaço. Veja um caso concreto:
int *v;
v = malloc (1000 * sizeof (int));
for (int i = 0; i < 990; i++)
scanf ("%d", &v[i]);
// ops! precisamos de mais
v = realloc (v, 2000 * sizeof (int));
for (int i = 990; i < 2000; i++)
scanf ("%d", &v[i]);
Nesse exemplo, poderíamos usar a seguinte implementação ad hoc de realloc:
int *realloc (int *v, unsigned int N) { int *novo = malloc (N); for (int i = 0; i < 1000; i++) novo[i] = v[i]; free (v); return novo; }
É claro que a implementação de realloc na biblioteca stdlib é mais geral e mais eficiente.
Se a memória do computador já estiver toda ocupada, malloc não consegue alocar mais espaço e devolve NULL. Convém verificar essa possibilidade antes de prosseguir:
ptr = malloc (sizeof (data)); if (ptr == NULL) { printf ("Socorro! malloc devolveu NULL!\n"); exit (EXIT_FAILURE); }
A digitação frequente e repetida desse teste é cansativa. Por isso, usaremos, neste sítio, a seguinte função-embalagem (= wrapper function) de malloc:
void *mallocc (size_t nbytes) { void *ptr; ptr = malloc (nbytes); if (ptr == NULL) { printf ("Socorro! malloc devolveu NULL!\n"); exit (EXIT_FAILURE); } return ptr; }
O parâmetro de mallocc é do tipo size_t (abreviatura de size data type); em muitos computadores, size_t é o mesmo que unsigned int.
Da mesma forma, podemos preparar uma função-embalagem reallocc para cuidar das situações em que realloc devolve NULL.
Resposta:
Cada invocação de malloc
aloca um bloco de bytes maior que o solicitado;
os bytes adicionais são usados para
guardar informações administrativas sobre o bloco
(essas informações permitem que o bloco seja corretamente desalocado,
mais tarde,
pela função free).
Assim, não é eficiente alocar
blocos muito pequenos repetidamente;
é melhor alocar um bloco grande
e dele retirar pequenas porções na medida do necessário.
A boa notícia é que o programador não precisa fazer isso pessoalmente
pois malloc implementa essa política por baixo do pano
sem que o programador perceba.
int v[n];
se o valor de n só se torna conhecido durante a execução do programa?
Resposta: Não. É melhor não fazer isso.
Resposta: Sim. Convém não deixar ponteiros soltos (= dangling pointers) no seu programa. Mas o presente sítio ignora essa recomendação de segurança para não cansar o leitor com detalhes repetitivos,
Resposta: Talvez. A chamada calloc (a, b) aloca um bloco de a*b bytes e atribui valor zero a todos esses bytes. Veja o artigo Why does calloc exist? de Nathaniel J. Smith (2016).