A maioria das implementações das funções de alocação de memória C armazenará informações contábeis para cada bloco, em linha ou separadamente.
Uma maneira típica (em linha) é realmente alocar o cabeçalho e a memória solicitada, preenchidos com algum tamanho mínimo. Por exemplo, se você solicitou 20 bytes, o sistema pode alocar um bloco de 48 bytes:
- Cabeçalho de 16 bytes contendo tamanho, marcador especial, soma de verificação, ponteiros para o bloco seguinte / anterior e assim por diante.
- Área de dados de 32 bytes (seus 20 bytes aumentam para um múltiplo de 16).
O endereço então fornecido a você é o endereço da área de dados. Então, quando você liberar o bloco, free
simplesmente pegará o endereço que você deu e, supondo que você não tenha enchido o endereço ou a memória em torno 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
Lembre-se de que o tamanho do cabeçalho e o preenchimento são totalmente definidos pela implementação (na verdade, a coisa toda é definida pela implementação (a), mas a opção de contabilidade em linha é comum).
As somas de verificação e marcadores especiais que existem nas informações contábeis geralmente causam erros como "Arena de memória corrompida" ou "Liberação dupla" se você as substituir ou liberar duas vezes.
O preenchimento (para tornar a alocação mais eficiente) é o motivo pelo qual você pode escrever um pouco além do final do espaço solicitado sem causar problemas (ainda assim, não faça isso, é um comportamento indefinido e, só porque funciona às vezes, não funciona) significa que não há problema em fazê-lo).
(a) Eu escrevi implementações malloc
em sistemas embarcados em que você obteve 128 bytes, independentemente do que solicitou (que era o tamanho da maior estrutura do sistema), supondo que você solicitasse 128 bytes ou menos (solicitações de mais seja atendido com um valor de retorno NULL). Uma máscara de bits muito simples (isto é, não alinhada) foi usada para decidir se um pedaço de 128 bytes foi alocado ou não.
Outros que 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 eram usados ou disponíveis.
Ambas as opções conseguiram reduzir a sobrecarga das informações contábeis e aumentar a velocidade malloc
e free
(não há necessidade de coalescer blocos adjacentes ao liberar), particularmente importante no ambiente em que estávamos trabalhando.