Na programação C, você pode transmitir qualquer tipo de ponteiro que desejar como argumento para liberar, como ele sabe o tamanho da memória alocada para liberar? Sempre que passo um ponteiro para alguma função, também preciso passar o tamanho (ou seja, uma matriz de 10 elementos precisa receber 10 como parâmetro para saber o tamanho da matriz), mas não preciso passar o tamanho para o função livre. Por que não, e posso usar essa mesma técnica em minhas próprias funções para evitar que eu precise carregar a variável extra do comprimento da matriz?
385
Respostas:
Ao ligar
malloc()
, você especifica a quantidade de memória a alocar. A quantidade de memória realmente usada é um pouco mais do 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 deve :-).Quando você liga
free()
, simplesmente analisa as informações extras para descobrir o tamanho do bloco.fonte
malloc_size()
acessar de forma confiável o tamanho do bloco a partir de ummalloc()
ponteiro ed. Mas não há uma maneira confiável e portátil.free()
exigido que o programador relate com precisão o tamanho domalloc()
bloco? Os vazamentos de memória já são ruins o suficiente.malloc()
efree()
, mas você precisa armazenar o tamanho de uma matriz? Por que eles não tornariam possível fazer algo comoblockSize(ptr)
se estivessem armazenando as informações de qualquer maneira?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:
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: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
efree
(não há necessidade de coalescer blocos adjacentes ao liberar), particularmente importante no ambiente em que estávamos trabalhando.fonte
malloc
é fornecer, para o caso de sucesso, um bloco de memória pelo menos tão grande quanto o que você solicitou. Blocos individuais são contíguos em termos de como você acessa elementos dentro deles, mas não é necessário que as arenas de onde os blocos vêm sejam contíguas.void *x = malloc(200); free(x, 500);
é não vai acabar bem :-) Em qualquer caso, para a eficiência, o real tamanho do buffer pode ser maior (você só não pode contar com isso).Na
comp.lang.c
lista de perguntas frequentes: como o free sabe quantos bytes o free?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 adjacente ao bloco alocado, e é por isso que as coisas geralmente quebram mal se os limites do bloco alocado são levemente ultrapassados)
fonte
free
. Talvez a resposta não é satisfatória você, mas eu não acho que você vai ter um com mais informações genericamente aplicável :-)Esta resposta é realocada em Como free () sabe quanta memória desalocar? onde fui abruptamente impedido de responder por uma pergunta duplicada aparente. Esta resposta deve ser relevante para esta duplicata:
No caso de
malloc
, o alocador de heap armazena um mapeamento do ponteiro retornado original, para detalhes relevantes necessários parafree
a memória posteriormente. Isso geralmente envolve o armazenamento do tamanho da região da memória em qualquer forma relevante para o alocador em uso, por exemplo, tamanho bruto ou um nó em uma árvore binária usada para rastrear alocações ou uma contagem de "unidades" de memória em uso.free
não falhará se você "renomear" o ponteiro ou duplicá-lo de qualquer forma. No entanto, não é contada a referência e apenas a primeirafree
estará correta.free
S adicionais são erros "duplos".Tentativa de
free
qualquer ponteiro com um valor diferente dos retornados pormalloc
s anteriores e ainda não liberada é um erro. Não é possível liberar parcialmente as regiões de memória retornadasmalloc
.fonte
Em uma nota relacionada, a biblioteca GLib possui funções de alocação de memória que não salvam o tamanho implícito - e você passa o parâmetro size para liberar. Isso pode eliminar parte da sobrecarga.
fonte
malloc()
efree()
dependem do sistema / compilador, por isso é difícil dar uma resposta específica.Mais informações sobre essa outra questão .
fonte
O gerenciador de heap armazenou a quantidade de memória pertencente ao bloco alocado em algum lugar quando você ligou
malloc
.Eu nunca implementei um, mas acho que a memória bem na frente do bloco alocado pode conter as meta informações.
fonte
A técnica original era alocar um bloco um pouco maior e armazenar o tamanho no início e depois dar ao aplicativo o resto do blog. O espaço extra possui um tamanho e, possivelmente, links para encadear os blocos livres para reutilização.
Existem alguns problemas com esses truques, no entanto, como mau comportamento do cache e do gerenciamento de memória. O uso da memória no bloco tende a paginar as coisas desnecessariamente e também cria páginas sujas que complicam o compartilhamento e a cópia na gravação.
Portanto, uma técnica mais avançada é manter um diretório separado. Abordagens exóticas também foram desenvolvidas onde áreas da memória usam o mesmo tamanho de dois tamanhos.
Em geral, a resposta é: uma estrutura de dados separada é alocada para manter o estado.
fonte
Para responder à segunda metade da sua pergunta: sim, você pode, e um padrão bastante comum em C é o seguinte:
fonte
Quando chamamos malloc, ele simplesmente consome mais bytes do que é requisito. Esse consumo de mais bytes contém informações como soma de verificação, tamanho e outras informações adicionais. Quando ligamos gratuitamente naquele momento, ele acessa diretamente as informações adicionais, onde encontra o endereço e também descobre quanto bloco será gratuito.
fonte
Para responder à segunda pergunta, sim, você poderia (mais ou menos) usar a mesma técnica,
malloc()
simplesmente atribuindo a primeira célula dentro de cada matriz ao tamanho da matriz. que permite enviar a matriz sem enviar um argumento de tamanho adicional.fonte