Como excluir [] “sabe” o tamanho da matriz de operandos?


250
Foo* set = new Foo[100];
// ...
delete [] set;

Você não passa os limites da matriz para delete[]. Mas onde essas informações são armazenadas? É padronizado?


sourceforge.net/projects/fastmm é de código aberto e substitui o gerenciador de memória. Aqui você pode descobrir como o gerenciamento de memória funciona e de onde vêm as informações para alocar e excluir memórias.

1
Observe que o FastMM é específico apenas aos compiladores Delphi / C ++ Builder, não é um gerenciador de memória de uso geral para C ++. Nem é escrito em C ++.
Remy Lebeau

Respostas:


181

Quando você aloca memória no heap, seu alocador acompanhará quanta memória você alocou. Isso geralmente é armazenado em um segmento "head" logo antes da memória que você recebe. Dessa forma, quando é hora de liberar a memória, o desalocador sabe exatamente quanta memória deve ser liberada.


4
Observe que isso se aplica apenas às alocações de matriz em C ++. Todas as outras alocações dependem do tamanho do tipo. Algumas bibliotecas armazenam todos os tamanhos de alocação, geralmente apenas no modo de depuração.
Zan Lynx

97
Não há absolutamente nenhuma razão para que essas informações não estejam disponíveis para o programador. Posso passar um ponteiro para uma função e liberá-la, mas para obter o tamanho nessa mesma função, preciso passar um parâmetro extra. Isso faz algum sentido?
Mark ruzon

26
@ Mark, faz um pouco de sentido, porque, em teoria, libera o alocador para sempre armazenar o tamanho do bloco alocado (que pode diferir do tamanho do bloco solicitado ). Certos designs de alocador podem precisar dessas informações para seus próprios propósitos, ou podem não ser sofisticados o suficiente para usar informações de tipo para rastrear o tamanho de alocações de heap que não são da matriz, etc. Forçar o alocador a armazenar o tamanho solicitado (para que você não precise precisa passar o tamanho da matriz você mesmo) pode ser um pequeno ônus, mas pode ter impactos no desempenho de projetos de alocadores concebíveis.
Doug McClean

33
Desculpe, mas esta resposta está errada. O que o QuantumPete descreveu é basicamente "Como freesabe quanta memória desalocar". Sim, o tamanho do bloco de memória é armazenado "em algum lugar" por malloc(normalmente no próprio bloco), e é assim que se freesabe. No entanto, new[]/ delete[]é uma história diferente. Este último trabalha basicamente em cima de malloc/ free. new[]também armazena o número de elementos criados no bloco de memória (independentemente de malloc), para que mais tarde delete[]possa recuperar e usar esse número para chamar o número adequado de destruidores.
AnT

26
Ou seja, fisicamente dois contadores são armazenados no bloco: tamanho do bloco (por malloc) e contagem de elementos (por new[]). Observe que o primeiro não pode ser usado para calcular o último, pois, em geral, o tamanho do bloco de memória pode ser maior do que o realmente necessário para a matriz do tamanho solicitado. Observe também que o contador do elemento da matriz é necessário apenas para tipos com destruidor não trivial. Para tipos com destruidor trivial, o contador não é armazenado new[]e, é claro, não é recuperado por delete[].
AnT

23

Uma das abordagens para compiladores é alocar um pouco mais de memória e armazenar uma contagem de elementos em um elemento principal.

Exemplo de como isso pode ser feito:

Aqui

int* i = new int[4];

O compilador alocará sizeof(int)*5bytes.

int *temp = malloc(sizeof(int)*5)

Armazenará "4" nos primeiros sizeof(int)bytes

*temp = 4;

E definir i

i = temp + 1;

O imesmo aponta para uma matriz de 4 elementos, não 5.

E exclusão

delete[] i;

será processado da seguinte maneira:

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)

9

A informação não é padronizada. No entanto, nas plataformas em que trabalhei com essas informações, elas são armazenadas na memória antes do primeiro elemento. Portanto, você poderia acessá-lo e inspecioná-lo, no entanto, não vale a pena.

Também é por isso que você deve usar delete [] ao alocar memória com o novo [], pois a versão do array de delete sabe que (e onde) precisa procurar liberar a quantidade certa de memória - e chamar o número apropriado de destruidores para os objetos.


5

Basicamente, está organizado na memória como:

[info] [mem você pediu ...]

Onde info é a estrutura usada pelo seu compilador para armazenar a quantidade de memória alocada e quais não.

Isso depende da implementação.


4

Isso não é algo que está na especificação - depende da implementação.


3

É definido no padrão C ++ como específico do compilador. O que significa mágica do compilador. Ele pode romper com restrições de alinhamento não triviais em pelo menos uma plataforma principal.

Você pode pensar em possíveis implementações percebendo que isso delete[]é definido apenas para ponteiros retornados por new[], que podem não ser o mesmo ponteiro retornado por operator new[]. Uma implementação na natureza é armazenar a contagem de matrizes no primeiro int retornado por operator new[]e new[]retornar um deslocamento de ponteiro além disso. (É por isso que os alinhamentos não triviais podem quebrar new[].)

Tenha em mente que operator new[]/operator delete[]! = new[]/delete[].

Além disso, isso é ortogonal à forma como C sabe o tamanho da memória alocada por malloc.


2

Como a matriz a ser 'excluída' deveria ter sido criada com um único uso do operador 'novo'. A operação 'nova' deveria ter colocado essas informações na pilha. Caso contrário, como os usos adicionais de novos saberão onde o heap termina?


0

Não é padronizado. No tempo de execução da Microsoft, o novo operador usa malloc () e o operador de exclusão usa free (). Portanto, nessa configuração, sua pergunta é equivalente ao seguinte: Como free () sabe o tamanho do bloco?

Há alguma contabilidade acontecendo nos bastidores, ou seja, no tempo de execução C.


5
Não é verdade. Antes de ligar gratuitamente, o delete [] precisa chamar primeiro os destruidores. Para esse conhecimento, o tamanho total das alocações não é suficiente. Na verdade novo [] e delete [] funcionam de maneira diferente para tipos simples e destruídos no VC ++.
Suma

0

Esse é um problema mais interessante do que você imagina a princípio. Esta resposta é sobre uma possível implementação.

Em primeiro lugar, enquanto em algum nível seu sistema precisa saber como "liberar" o bloco de memória, o malloc / free subjacente (que new / delete / new [] / delete [] geralmente chamam) nem sempre se lembra exatamente da quantidade de memória Se você solicitar, ele poderá ser arredondado (por exemplo, quando estiver acima de 4K, ele é arredondado para o próximo bloco de tamanho 4K).

Portanto, mesmo que possa obter o tamanho do bloco de memória, isso não nos diz quantos valores existem na nova memória [] ed, pois ela pode ser menor. Portanto, precisamos armazenar um número inteiro extra nos dizendo quantos valores existem.

EXCETO, se o tipo que está sendo construído não possui um destruidor, delete [] não precisa fazer nada além de liberar o bloco de memória e, portanto, não precisa armazenar nada!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.