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.