Foo* set = new Foo[100];
// ...
delete [] set;
Vous ne passez pas les limites du tableau à delete[]
. Mais où sont stockées ces informations? Est-ce standardisé?
Foo* set = new Foo[100];
// ...
delete [] set;
Vous ne passez pas les limites du tableau à delete[]
. Mais où sont stockées ces informations? Est-ce standardisé?
Réponses:
Lorsque vous allouez de la mémoire sur le tas, votre allocateur gardera une trace de la quantité de mémoire que vous avez allouée. Ceci est généralement stocké dans un segment "tête" juste avant la mémoire qui vous est allouée. De cette façon, quand il est temps de libérer la mémoire, le désallocateur sait exactement la quantité de mémoire à libérer.
free
sait combien de mémoire à désallouer". Oui, la taille du bloc de mémoire est stockée "quelque part" par malloc
(normalement dans le bloc lui-même), c'est ainsi que l'on free
sait. Cependant, new[]
/ delete[]
est une autre histoire. Ces derniers fonctionnent essentiellement sur malloc
/ free
. new[]
stocke également le nombre d'éléments qu'il a créés dans le bloc de mémoire (indépendamment de malloc
), afin que plus tard, il delete[]
puisse récupérer et utiliser ce numéro pour appeler le nombre approprié de destructeurs.
malloc
) et le nombre d'éléments (par new[]
). Notez que le premier ne peut pas être utilisé pour calculer le second, car en général, la taille du bloc de mémoire peut être plus grande que ce qui est vraiment nécessaire pour le tableau de la taille demandée. Notez également que le compteur d'éléments de tableau n'est nécessaire que pour les types avec destructeur non trivial. Pour les types avec destructeur trivial, le compteur n'est pas stocké par new[]
et, bien sûr, n'est pas récupéré par delete[]
.
L'une des approches pour les compilateurs est d'allouer un peu plus de mémoire et de stocker un nombre d'éléments dans un élément head.
Exemple comment cela pourrait être fait:
Ici
int* i = new int[4];
le compilateur allouera des sizeof(int)*5
octets.
int *temp = malloc(sizeof(int)*5)
Stockera "4" dans les premiers sizeof(int)
octets
*temp = 4;
Et mettre i
i = temp + 1;
Il en i
va de même pour un tableau de 4 éléments, pas 5.
Et suppression
delete[] i;
seront traitées de la manière suivante:
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)
Les informations ne sont pas standardisées. Cependant, dans les plates-formes sur lesquelles j'ai travaillé, ces informations sont stockées en mémoire juste avant le premier élément. Par conséquent, vous pourriez théoriquement y accéder et l'inspecter, mais cela n'en vaut pas la peine.
C'est aussi pourquoi vous devez utiliser delete [] lorsque vous avez alloué de la mémoire avec new [], car la version de tableau de delete sait que (et où) elle doit chercher à libérer la bonne quantité de mémoire - et appeler le nombre approprié de destructeurs pour les objets.
Fondamentalement, il est organisé en mémoire comme:
[info] [mem que vous avez demandé ...]
Où info est la structure utilisée par votre compilateur pour stocker la quantité de mémoire allouée, et quoi d'autre.
Cela dépend cependant de l'implémentation.
Il est défini dans la norme C ++ pour être spécifique au compilateur. Ce qui signifie la magie du compilateur. Il peut rompre avec des restrictions d'alignement non triviales sur au moins une plate-forme majeure.
Vous pouvez penser aux implémentations possibles en réalisant que cela delete[]
n'est défini que pour les pointeurs retournés par new[]
, qui peuvent ne pas être le même pointeur que retourné par operator new[]
. Une implémentation dans la nature consiste à stocker le nombre de tableaux dans le premier entier retourné par operator new[]
, et à new[]
renvoyer un pointeur décalé au-delà. (C'est pourquoi les alignements non triviaux peuvent se rompre new[]
.)
Gardez à l'esprit que operator new[]/operator delete[]
! = new[]/delete[]
.
De plus, cela est orthogonal à la façon dont C connaît la taille de la mémoire allouée par malloc
.
Parce que le tableau à «supprimer» aurait dû être créé avec une seule utilisation de l'opérateur «nouveau». La «nouvelle» opération aurait dû mettre ces informations sur le tas. Sinon, comment les utilisations supplémentaires de new sauraient-elles où se termine le tas?
Ce n'est pas standardisé. Dans le runtime de Microsoft, le nouvel opérateur utilise malloc () et l'opérateur de suppression utilise free (). Ainsi, dans ce paramètre, votre question est équivalente à la suivante: comment free () connaît-il la taille du bloc?
Il y a une comptabilité en cours dans les coulisses, c'est-à-dire dans le runtime C.
C'est un problème plus intéressant que vous ne le pensez au début. Cette réponse concerne une implémentation possible.
Premièrement, alors qu'à un certain niveau votre système doit savoir comment «libérer» le bloc de mémoire, le malloc / free sous-jacent (que new / delete / new [] / delete [] appellent généralement) ne se souvient pas toujours exactement de la quantité de mémoire vous demandez, il peut être arrondi (par exemple, une fois que vous êtes au-dessus de 4K, il est souvent arrondi au bloc de taille 4K suivant).
Par conséquent, même si pourrait obtenir la taille du bloc de mémoire, cela ne nous dit pas combien de valeurs sont dans la nouvelle mémoire [], car elle peut être plus petite. Par conséquent, nous devons stocker un entier supplémentaire nous indiquant le nombre de valeurs.
SAUF, si le type en cours de construction n'a pas de destructeur, alors delete [] n'a rien d'autre à faire que de libérer le bloc mémoire, et donc n'a rien à stocker!