Baixe o app para aproveitar ainda mais
Prévia do material em texto
5/17/2017 Linguagem C: malloc e free https://www.ime.usp.br/~pf/algoritmos/aulas/aloca.html 1/6 Projeto de Algoritmos | Linguagem C | Índice Alocação dinâmica de memória As declarações abaixo alocam espaço de memória para diversas 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]; Às vezes, 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 é gerenciada pelas funções malloc, realloc e free, que estão na biblioteca stdlib. Para usar essa biblioteca, você deve incluir a correspondente interface no seu programa por meio de #include <stdlib.h> A função malloc A função malloc (o nome é uma abreviatura de memory allocation) aloca 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 é atribuído ao 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);.) Para alocar um objeto que ocupa mais de 1 byte, é preciso recorrer ao operador sizeof, que diz quantos bytes o tipo de objeto desejado tem: typedef struct { int dia, mes, ano; } data; data *d; d = malloc (sizeof (data)); d->dia = 31; d->mes = 12; d->ano = 2014; 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 objetos, caso em que os parênteses são redundantes: se var é uma variável então sizeof var é o número de bytes ocupado por var. (Note que sizeof não uma função mas um operador, tal como return, por exemplo.) 5/17/2017 Linguagem C: malloc e free https://www.ime.usp.br/~pf/algoritmos/aulas/aloca.html 2/6 Overhead. Cada invocação de malloc aloca um bloco de bytes consecutivos maior que o solicitado: os bytes adicionais são usados para guardar informações administrativas sobre o bloco de bytes (essas informações permitem que o bloco seja corretamente desalocado, mais tarde, pela função free). O número de bytes adicionais pode ser grande, qualquer que seja o número o número de bytes solicitado no argumento de malloc. Não é eficiente, portanto, alocar pequenos blocos; é preferível alocar um grande bloco e dele retirar pequenas porções na medida do necessário. Felizmente, malloc faz isso de maneira automática, sem que o programador/usuário perceba. Exercícios 1 1. Escreva uma função que receba um caractere c e transforme-o em uma string (cadeia de caracteres), ou seja, devolva uma string de comprimento 1 tendo c como único elemento. 2. Discuta, passo a passo, o efeito do seguinte fragmento de código: int *v; v = malloc (10 * sizeof (int)); 3. Discuta o efeito do seguinte fragmento de código: x = malloc (10 * sizeof *x); A função free 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á livre e disponível para reciclagem. A próxima chamada de malloc poderá tomar posse desses bytes. A função free não deve ser aplicada a uma parte de um bloco de bytes alocado por malloc. Aplique free apenas ao bloco todo. Convém não deixar ponteiros "soltos" (= dangling pointers) no seu programa, pois isso pode ser explorado por hackers para atacar o seu computador. Portanto, depois de cada free (ptr), atribua NULL a ptr: free (ptr); ptr = NULL; (Atribuir um valor a um ponteiro que se tornou inútil é decididamente deselegante, mas não há como lidar com hackers de maneira elegante…) Para não cansar o leitor com detalhes repetitivos, estas notas ignoram essa recomendação de segurança. Exercícios 2 1. Discuta, passo a passo, o efeito do seguinte fragmento de código: 5/17/2017 Linguagem C: malloc e free https://www.ime.usp.br/~pf/algoritmos/aulas/aloca.html 3/6 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 2. O que há de errado com o seguinte fragmento de código? int *v; v = malloc (10 * sizeof (int)); v[0] = 999; free (v+1); 3. A seguinte função promete devolver um vetor com os 4 primeiros números primos maiores que 1000. Onde está o erro? int *primos (void) { int v[3]; v[0] = 1009; v[1] = 1013; v[2] = 1019; return v; } 4. A seguinte função promete acrescentar uma célula-cabeça à lista encadeada lst e devolver o endereço da nova lista. Onde está o erro? celula *acrescentaCabeca (celula *lst) { celula cabeca; cabeca.prox = lst; return &cabeca; } Vetores e matrizes 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, i; scanf ("%d", &n); v = malloc (n * sizeof (int)); for (i = 0; i < n; ++i) scanf ("%d", &v[i]); . . . free (v); (A propósito, veja a observação sobre vetores e 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]; A propósito, a norma ANSI não permite escrever "int v[n]" a menos que n seja uma constante, definida por um #define. Matrizes. Matrizes (bidimensionais) são implementadas como vetores de vetores. Uma matriz com m linhas e n colunas é um vetor cada um de cujos m elementos é um vetor de n elementos. O seguinte fragmento de código faz a alocação dinâmica de uma tal matriz: int **M; int i; M = malloc (m * sizeof (int *)); 5/17/2017 Linguagem C: malloc e free https://www.ime.usp.br/~pf/algoritmos/aulas/aloca.html 4/6 for (i = 0; i < m; ++i) M[i] = malloc (n * sizeof (int)); O elemento de M que está no cruzamento da linha i com a coluna j é denotado por M[i][j]. Exercícios 3 1. Escreva uma função que desaloque a matriz M alocada acima. Quais devem ser os parâmetros da função? Redimensionamento e a função realloc Às vezes é necessário alterar, durante a execução do programa, o tamanho do bloco de bytes 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 recebe o endereço de um bloco previamente alocado por malloc (ou realloc) e o número de bytes que o bloco redimensionado deve ter. A função aloca o novo bloco, transfere para ele o conteúdo do bloco original, e devolve o endereço do novo bloco. (O novo bloco pode ser uma simples extensão do bloco original, caso em que o conteúdo do bloco original não precisa ser copiado para o novo bloco. Se, entretanto, um novo bloco for efetivamente alocado, realloc libera o blocooriginal, recorrendo à função free, depois de copiar seu conteúdo para o novo bloco. A propósito, o tamanho do novo bloco pode ser menor que o tamanho do original.) Suponha, por exemplo, que alocamos um vetor de 1000 inteiros e depois decidimos que precisamos de duas vezes mais espaço: int *v; v = malloc (1000 * sizeof (int)); for (i = 0; i < 990; i++) scanf ("%d", &v[i]); v = realloc (v, 2000 * sizeof (int)); for (i = 990; i < 2000; i++) scanf ("%d", &v[i]); Para efeito desse exemplo apenas, realloc poderia ser implementada assim: int *realloc (int *v, unsigned int N) { int *novo, i; novo = malloc (N); for (i = 0; i < 1000; i++) novo[i] = v[i]; free (v); return novo; } É claro que a implementação oficial de realloc na biblioteca stdlib é mais geral e mais eficiente. 5/17/2017 Linguagem C: malloc e free https://www.ime.usp.br/~pf/algoritmos/aulas/aloca.html 5/6 Exercícios 4 1. Suponha dado um arquivo que contém uma sequência de números inteiros. O comprimento da sequência é desconhecido . Escreva uma função que imprima esses números em ordem inversa. É claro que você terá que ler os números todos, armazenar os números na memória, e depois imprimí-los em ordem inversa. A dificuldade esté em alocar espaço para uma quantidade de números que só será conhecida quando chegarmos ao fim do arquivo. A memória é finita 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 codificação frequente e repetida desse teste é cansativa. Por isso, usaremos, nestas notas, 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. Em muitos computadores, size_t é equivalente a unsigned int. Da mesma forma, podemos preparar uma função-embalagem reallocc para cuidar das situações em que realloc devolve NULL. Valgrind: ferramenta para detetar problemas com alocação/desalocação de memória e segmentation faults no seu programa Atualizado em 2016-04-07 http://www.ime.usp.br/~pf/algoritmos/ Paulo Feofiloff DCC-IME-USP 5/17/2017 Linguagem C: malloc e free https://www.ime.usp.br/~pf/algoritmos/aulas/aloca.html 6/6
Compartilhar