En programmation C, vous pouvez passer n'importe quel type de pointeur comme argument pour libérer, comment sait-il la taille de la mémoire allouée pour libérer? Chaque fois que je passe un pointeur sur une fonction, je dois également passer la taille (c'est-à-dire qu'un tableau de 10 éléments doit en recevoir 10 en tant que paramètre pour connaître la taille du tableau), mais je n'ai pas à passer la taille au fonction libre. Pourquoi pas et puis-je utiliser cette même technique dans mes propres fonctions pour ne pas avoir à cartographier la variable supplémentaire de la longueur du tableau?
Lorsque vous appelez malloc()
, vous spécifiez la quantité de mémoire à allouer. La quantité de mémoire réellement utilisée est légèrement supérieure à celle-ci et inclut des informations supplémentaires qui enregistrent (au moins) la taille du bloc. Vous ne pouvez pas (de manière fiable) accéder à ces autres informations - et vous ne devriez pas non plus :-).
Lorsque vous appelez free()
, il se contente de consulter les informations supplémentaires pour déterminer la taille du bloc.
La plupart des implémentations de fonctions d'allocation de mémoire C stockent des informations de comptabilité pour chaque bloc, en ligne ou séparément.
Un moyen typique (en ligne) consiste à allouer à la fois un en-tête et la mémoire demandée, avec une taille minimale. Ainsi, par exemple, si vous avez demandé 20 octets, le système peut allouer un bloc de 48 octets:
L'adresse qui vous est alors donnée est l'adresse de la zone de données. Ensuite, lorsque vous libérez le bloc, free
prendra simplement l'adresse que vous lui avez donnée et, en supposant que vous n'ayez pas renseigné cette adresse ou la mémoire qui l'entoure, vérifiez les informations de comptabilité immédiatement avant. Graphiquement, cela ressemblerait à:
____ The allocated block ____
/ \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
^
|
+-- The address you are given
Gardez à l'esprit que la taille de l'en-tête et le remplissage sont totalement définis par l'implémentation (en réalité, le tout est défini par l'implémentation (une) mais l'option de comptabilisation en ligne est commune).
Les sommes de contrôle et les marqueurs spéciaux qui existent dans les informations de comptabilité sont souvent la cause d'erreurs telles que "arène de mémoire corrompue" ou "double libération" si vous les écrasez ou les libérez deux fois.
Le padding (pour rendre l'allocation plus efficace) est la raison pour laquelle vous pouvez parfois écrire un peu au-delà de la fin de l'espace demandé sans causer de problèmes (ne faites pas ça, c'est un comportement indéfini et, juste parce que ça marche parfois, ça ne marche pas t signifie que c’est bien de le faire).
(une) J'ai écrit les implémentations de malloc
dans les systèmes embarqués où vous avez 128 octets, peu importe ce que vous avez demandé (taille de la plus grande structure du système), en supposant que vous ayez demandé 128 octets ou moins (demandes pour plus serait rencontré avec une valeur de retour NULL). Un masque de bits très simple (c'est-à-dire, pas en ligne) a été utilisé pour décider si un bloc de 128 octets était alloué ou non.
D'autres que j'ai développées avaient différents groupes pour des morceaux de 16 octets, des morceaux de 64 octets, des morceaux de 256 octets et des morceaux de 1K, en utilisant à nouveau un masque binaire pour décider quels blocs étaient utilisés ou disponibles.
Ces deux options ont réussi à réduire les frais généraux des informations comptables et à augmenter la vitesse de malloc
et free
(inutile de fusionner des blocs adjacents lors de la libération), ce qui est particulièrement important dans l'environnement dans lequel nous travaillions. dans.
Du comp.lang.c
FAQ list: Comment savoir combien d'octets sont gratuits?
L'implémentation malloc/free garde en mémoire la taille de chaque bloc lors de son affectation. Il n'est donc pas nécessaire de lui rappeler la taille lors de la libération. (En règle générale, la taille est stockée à côté du bloc alloué, ce qui explique pourquoi les choses se cassent mal si les limites du bloc alloué sont même légèrement dépassées)
Cette réponse est déplacée de comment free () sait-il combien de mémoire il faut désallouer? où une question apparemment en double m'empêchait de répondre. Cette réponse devrait alors être pertinente pour ce doublon:
Dans le cas de malloc
, l'allocateur de segment de mémoire stocke un mappage du pointeur renvoyé d'origine avec les détails pertinents nécessaires pour free
plus tard dans la mémoire. Cela implique généralement le stockage de la taille de la région de mémoire sous la forme pertinente pour l'allocateur utilisé, par exemple la taille brute, ou un nœud dans une arborescence binaire utilisée pour suivre les allocations, ou un nombre d'unités de mémoire utilisées.
free
n'échouera pas si vous "renommez" le pointeur ou si vous le dupliquez de quelque manière que ce soit. Cependant, la référence n'est pas comptée et seul le premier free
sera correct. Des free
supplémentaires sont des erreurs "double libre".
Essayer de free
n’importe quel pointeur avec une valeur différente de celles renvoyées par les précédents malloc
, et non encore libéré est une erreur. Il n'est pas possible de libérer partiellement les régions de mémoire renvoyées par malloc
.
Sur une note connexe GLib , la bibliothèque a des fonctions d’allocation de mémoire qui ne sauvegardent pas la taille implicite - et il vous suffit ensuite de passer le paramètre size à free. Cela peut éliminer une partie des frais généraux.
malloc()
et free()
dépendent du système et du compilateur, il est donc difficile de donner une réponse spécifique.
Plus d'informations sur cette autre question .
La technique originale consistait à allouer un bloc légèrement plus grand et à stocker la taille au début, puis à donner à l'application le reste du blog. L'espace supplémentaire contient une taille et peut-être des liens pour relier les blocs libres en vue de leur réutilisation.
Ces astuces posent toutefois certains problèmes, tels que le mauvais comportement de la mémoire cache et de la gestion de la mémoire. Utiliser de la mémoire directement dans le bloc a tendance à faire des recherches inutilement dans les pages et à créer des pages sales qui compliquent le partage et la copie sur écriture.
Donc, une technique plus avancée consiste à garder un répertoire séparé. Des approches exotiques ont également été développées dans lesquelles des zones de mémoire utilisent la même puissance de deux tailles.
En général, la réponse est: ne structure de données séparée est allouée pour conserver l'état.
Le gestionnaire de tas a stocké la quantité de mémoire appartenant au bloc alloué quelque part lorsque vous avez appelé malloc
.
Je n'ai jamais mis en œuvre un moi-même, mais j'imagine que la mémoire située juste devant le bloc alloué pourrait contenir les méta-informations.
Pour répondre à la seconde partie de votre question: oui, vous pouvez, et un schéma assez courant en C est le suivant:
typedef struct {
size_t numElements
int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;
#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;
Lorsque nous appelons malloc, nous consommons simplement plus d'octets que nécessaire. Cette consommation supplémentaire d'octets contient des informations telles que la somme de contrôle, la taille et d'autres informations supplémentaires. Lorsque nous appelons gratuitement à ce moment-là, il est directement dirigé vers cette information supplémentaire où il est trouvé l'adresse et également combien de bloc sera libre.