Como o livre sabe quanto libertar?

Na programação C, você pode passar qualquer tipo de ponteiro que você gosta como um argumento para liberar, como ele sabe o tamanho da memory alocada para liberar? Sempre que eu passar um ponteiro para alguma function, eu também tenho que passar o tamanho (ou seja, uma matriz de 10 elementos precisa receber 10 como um parâmetro para saber o tamanho da matriz), mas eu não tenho que passar o tamanho para o function livre. Por que não, e posso usar essa mesma técnica em minhas próprias funções para me impedir de precisar carregar a variável extra do tamanho da matriz?

Quando você chama malloc() , especifica a quantidade de memory a ser alocada. A quantidade de memory realmente usada é um pouco maior que isso e inclui informações extras que registram (pelo menos) o tamanho do bloco. Você não pode (de forma confiável) acessar essas outras informações – e nem deveria :-).

Quando você chama free() , ele simplesmente observa as informações extras para descobrir o tamanho do bloco.

A maioria das implementações de funções de alocação de memory C armazenará informações contábeis para cada bloco, seja em linha ou separadamente.

Uma maneira típica (in-line) é realmente alocar tanto um header quanto a memory que você pediu, preenchidos com algum tamanho mínimo. Por exemplo, se você pediu 20 bytes, o sistema pode alocar um bloco de 48 bytes:

  • Cabeçalho de 16 bytes contendo tamanho, marcador especial, sum de verificação, pointers para o bloco seguinte / anterior e assim por diante.
  • Área de dados de 32 bytes (seus 20 bytes foram preenchidos para um múltiplo de 16).

O endereço então dado a você é o endereço da área de dados. Então, quando você liberar o bloqueio, o free arbítrio simplesmente pegará o endereço que você forneceu e, supondo que você não tenha preenchido esse endereço ou a memory em volta dele, verifique as informações contábeis imediatamente antes dele. Graficamente, isso seria ao longo das linhas de:

  ____ The allocated block ____ / \ +--------+--------------------+ | Header | Your data area ... | +--------+--------------------+ ^ | +-- The address you are given 

Tenha em mente que o tamanho do header e o preenchimento são totalmente definidos pela implementação (na verdade, tudo é definido pela implementação (a), mas a opção de contabilidade em linha é comum).

As sums de verificação e os marcadores especiais que existem nas informações contábeis são geralmente a causa de erros como “Arena de memory corrompida” ou “Dupla livre”, se você sobrescrevê-los ou liberá-los duas vezes.

O padding (para tornar a alocação mais eficiente) é por que você pode escrever um pouco além do final do seu espaço requisitado sem causar problemas (ainda assim, não faça isso, é um comportamento indefinido e, só porque funciona algumas vezes, não funciona) significa que não há problema em fazer isso).


(a) Eu escrevi implementações de malloc em sistemas embarcados onde você tem 128 bytes não importando o que você pediu (esse era o tamanho da maior estrutura do sistema), assumindo que você pediu 128 bytes ou menos (pedidos de mais seria atingido com um valor de retorno NULL). Uma máscara de bits muito simples (ou seja, não em linha) foi usada para decidir se um pedaço de 128 bytes foi alocado ou não.

Outros que eu desenvolvi tinham pools diferentes para blocos de 16 bytes, blocos de 64 bytes, blocos de 256 bytes e blocos de 1K, novamente usando uma máscara de bits para decidir quais blocos foram usados ​​ou disponíveis.

Ambas as opções conseguiram reduzir a sobrecarga das informações contábeis e aumentar a velocidade do malloc e do free (não é necessário aglutinar blocos adjacentes ao liberar), particularmente importante no ambiente em que estávamos trabalhando.

Da lista FAQ do comp.lang.c : Como o free sabe quantos bytes liberar?

A implementação malloc / free lembra o tamanho de cada bloco à medida que é alocado, portanto não é necessário lembrá-lo do tamanho ao liberar. (Normalmente, o tamanho é armazenado ao lado do bloco alocado, e é por isso que as coisas geralmente quebram mal se os limites do bloco alocado forem levemente ultrapassados)

Esta resposta é realocada de Como o free () sabe quanta memory desalocar? onde eu fui abruptamente impedido de responder por uma aparente pergunta duplicada. Essa resposta, então, deve ser relevante para essa duplicata:


Para o caso de malloc , o alocador de heap armazena um mapeamento do ponteiro retornado original, para detalhes relevantes necessários para free a memory posteriormente. Isso normalmente envolve armazenar o tamanho da região de memory em qualquer forma relevante para o alocador em uso, por exemplo, tamanho bruto ou um nó em uma tree binária usada para rastrear alocações ou uma contagem de “unidades” de memory em uso.

free não falhará se você “renomear” o ponteiro ou duplicá-lo de alguma forma. No entanto, não é contada a referência e somente o primeiro free arbítrio estará correto. S free adicionais são erros “duplos livres”.

A tentativa de free qualquer ponteiro com um valor diferente daqueles retornados pelos malloc s anteriores e ainda não decifrado é um erro. Não é possível liberar parcialmente as regiões de memory retornadas do malloc .

Em uma nota relacionada, a biblioteca GLib possui funções de alocação de memory que não salvam o tamanho implícito – e então você simplesmente passa o parâmetro size para liberar. Isso pode eliminar parte da sobrecarga.

malloc() e free() dependem do sistema / compilador, então é difícil dar uma resposta específica.

Mais informações sobre esta outra questão .

O gerenciador de heap armazenou a quantia de memory pertencente ao bloco alocado em algum lugar quando você chamou malloc .

Eu nunca implementei um, mas acho que a memory na frente do bloco alocado pode conter a meta informação.

Para responder à segunda metade da sua pergunta: sim, você pode, e um padrão bastante comum em C é o seguinte:

 typedef struct { size_t numElements int elements[1]; /* but enough space malloced for numElements at runtime */ } IntArray_t; #define SIZE 10 IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int)); myArray->numElements = SIZE; 

A técnica original era alocar um bloco um pouco maior e armazenar o tamanho no início, depois dar ao aplicativo o restante do blog. O espaço extra contém um tamanho e possivelmente links para encadear os blocos livres juntos para reutilização.

No entanto, há alguns problemas com esses truques, como o mau comportamento do cache e do gerenciamento de memory. Usar a memory diretamente no bloco tende a exibir as coisas desnecessariamente e também cria páginas sujas que complicam o compartilhamento e o copy-on-write.

Portanto, uma técnica mais avançada é manter um diretório separado. Abordagens exóticas também foram desenvolvidas onde áreas de memory usam o mesmo poder de dois tamanhos.

Em geral, a resposta é: uma estrutura de dados separada é alocada para manter o estado.

memory alocada usando malloc () ou calloc () ou realloc ().

void free (void * ptr);

a function livre não aceita tamanho como parâmetro. Como a function free () sabe quanta memory é liberada, dado apenas um ponteiro?

A seguir está a maneira mais comum de armazenar o tamanho da memory para que o free () saiba o tamanho da memory a ser desalocada.

Quando a alocação de memory é feita, o espaço de heap real alocado é uma palavra maior que a memory solicitada. A palavra extra é usada para armazenar o tamanho da alocação e é usada mais tarde por free ()

Quando chamamos malloc, é simplesmente consumir mais byte do requisito. Este consumo de mais bytes contém informações como sum de cheques, tamanho e outras informações adicionais. Quando ligamos gratuitamente naquele momento, ele vai diretamente para essa informação adicional, onde encontra o endereço e também descobre quantos blocos serão gratuitos.