À propos de l'allocation dynamique de mémoire
Rappelez-vous les méthodes d'allocation de mémoire que nous avons apprises précédemment :
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {
0};//在栈空间上开辟10个字节的连续空间
Lors de l’apprentissage du langage C, nous savons que les structures de données sont généralement de taille fixe. Prenons l'exemple du tableau. Une fois le programme compilé, la taille du tableau et le nombre d'éléments sont déterminés . Ensuite, la taille de la structure de données ne peut pas être modifiée sans modifier le programme et recompiler le programme. Le résumé présente les deux caractéristiques suivantes :
- La taille de l'ouverture de l'espace est fixe.
- Lors de la déclaration d'un tableau, la longueur du tableau doit être spécifiée. Une fois la taille de l'espace du tableau déterminée, elle ne peut pas être ajustée.
Mais la demande d’espace ne se limite pas aux situations mentionnées ci-dessus. Parfois, la quantité d'espace dont nous avons besoin ne peut être connue que lorsque le programme est en cours d'exécution, donc la méthode de création d'espace lors de la compilation du tableau n'est pas suffisante.
Ainsi, le langage C a introduit l'allocation dynamique de mémoire, qui permet aux programmeurs de demander et de libérer eux-mêmes de l'espace . Ce qui suit présente comment ouvrir dynamiquement la mémoire.
Introduction aux fonctions malloc et calloc
Voici la définition de la paire cplusplusmalloc
:
void* malloc (size_t size);
Cette fonction s'applique à un espace continu en mémoire et renvoie un pointeur vers cet espace. size
C'est-à-dire que la taille de l'espace mémoire que vous souhaitez demander void*
est l'adresse du premier élément de la mémoire demandée. Parce que vous ne connaissez pas le type, vous l'utilisez void*
. Il y a aussi les points suivants à noter :
- Si l'allocation réussit, un pointeur vers l'espace alloué est renvoyé.
- Si l'allocation échoue, un pointeur NULL sera renvoyé, la valeur de retour de malloc doit donc être vérifiée.
- Le type de la valeur de retour est void*, donc la fonction malloc ne connaît pas le type d'espace à ouvrir, l'utilisateur doit décider lui-même de son utilisation.
- Si le paramètre size est 0, le comportement de malloc n'est pas défini par la norme et dépend du compilateur.
Jetons un coup d'oeil à la définition de la paire cpluspluscalloc
:
void* calloc (size_t num, size_t size);
malloc
En fait, calloc
cela ressemble beaucoup à. ralloc
Le paramètre en size
est la taille de chaque type de données pour lequel vous souhaitez postuler, qui num
est le nombre de types de données pour lesquels vous souhaitez postuler. La taille totale de l'application est num*size
, en fait. , il peut être représenté malloc
par size
. Les autres fonctionnalités sont malloc
similaires, je ne les présenterai donc pas en détail. Bien entendu, il existe des différences
entre les deux , comme suit :
malloc
La seule différence avec la fonction est que chaque octetcalloc
de l'espace demandé est initialisé à 0 avant de renvoyer l'adresse .
Donc, si nous voulons initialiser l’espace mémoire appliqué dynamiquement à 0, c’est ralloc
plus pratique à utiliser.
Recyclage dynamique de la mémoire ---- gratuit
En fait malloc
, calloc
des fonctions telles que l'allocation dynamique de mémoire allouent en fait de la mémoire sur la zone du tas. Étant donné que l'espace mémoire demandé par ces fonctions n'est pas automatiquement récupéré par le système , l'utilisation trop fréquente de ces fonctions pour libérer de l'espace entraînera un épuisement du tas. À ce stade, nous devons libérer activement l'espace ouvert , nous introduisons donc free
une fonction. Le prototype de la fonction est le suivant :
void free (void* ptr);
Le pointeur ici ptr
pointe vers la première adresse de notre mémoire allouée dynamiquement . Ce n'est qu'en pointant vers la première adresse que l'espace mémoire alloué dynamiquement peut être complètement libéré. Il existe deux cas particuliers concernant ptr
les pointeurs ;
- Si
ptr
l'espace pointé par le paramètre n'est pas alloué dynamiquement,free
le comportement de la fonction n'est pas défini.- Si l'argument
ptr
estNULL
un pointeur, la fonction ne fait rien.
Deux autres choses à noter :
- Après avoir libéré l'espace ouvert, le pointeur d'origine pointant vers cet espace
ptr
stocke toujours l'adresse ici. Afin d'éviter d'attribuer ou de déréférencer accidentellement ce pointeur plus tard, provoquant un problème de pointeur sauvage , après avoir libéré l'espace, nous avons également Ce pointeur doit être attribuéNULL
. - Il est préférable de toujours avoir un pointeur vers cet espace lors de l'écriture du code. S'il n'y a pas de pointeur vers cet espace, alors cet espace ne sera pas accessible et libéré. Pour les programmes, l'espace inaccessible est également appelé poubelle, et les programmes qui laissent des ordures ont des fuites de mémoire .
Comme le montre la figure ci-dessus, le pointeur d'origine p
pointe vers le premier bloc mémoire et, après l'opération, p
il pointe vers le deuxième bloc mémoire. Donc comme il n'y a pas de pointeur pointant vers le premier bloc mémoire, ce bloc mémoire ne peut plus être utilisé. C'est la poubelle mentionnée ci-dessus, provoquant une fuite de mémoire .
introduction à la fonction de réallocation
Parfois, nous constatons que l'espace que nous avons demandé dans le passé est trop petit, et parfois nous pensons que l'espace que nous avons demandé est trop grand. Afin de demander de la mémoire de manière raisonnable, nous devons apporter des ajustements flexibles à la taille de la mémoire. À ce stade, realloc
la fonction peut ajuster la taille de la mémoire allouée dynamiquement .
Le prototype de la fonction est le suivant :
void* realloc (void* ptr, size_t size);
Le pointeur ptr
pointe vers l'adresse mémoire à ajuster size
et la taille de la mémoire ajustée. La valeur de retour est l'adresse de départ de la mémoire ajustée.
Cas 1 : Il y a suffisamment d'espace après l'espace d'origine
. Dans le cas 1, si vous souhaitez étendre la mémoire, ajoutez simplement de l'espace directement après la mémoire d'origine. Les données dans l'espace d'origine ne changeront pas.
Cas 2 : Il n'y a pas assez d'espace après l'espace d'origine
. Lorsqu'il s'agit du cas 2 et qu'il n'y a pas assez d'espace après l'espace d'origine, la méthode d'expansion est la suivante : trouver un autre espace continu de taille appropriée sur l'espace de tas à utiliser. De cette façon, la fonction renvoie une nouvelle adresse mémoire. Dans ce cas, pour les données déjà présentes dans la mémoire d'origine, cette fonction copiera ces données dans la nouvelle mémoire et la mémoire d'origine sera libérée .
Il existe également le problème de l'impossibilité de retrouver l'espace mémoire d'origine realloc
en raison de l'affectation du pointeur lorsque l'espace mémoire dynamique ne parvient pas à être ouvert . NULL
Nous créons généralement un nouveau pointeur pour recevoir l'adresse , NULL
puis l'attribuons au pointeur d'origine après avoir jugé qu'il n'est pas correct, comme suit :
int main()
{
int* ptr=(int*)malloc(5*sizeof(int));
int* p=(int*)realloc(10*sizeof(int));
if(p != NULL)
ptr = p;
//......(代码)
free(ptr);
ptr = NULL;
return 0;
}
Erreurs courantes de mémoire dynamique
- Opération de déréférencement sur le pointeur NULL :
void test()
{
int *p = (int *)malloc(INT_MAX/4);
*p = 20;
free(p);
}
Si la valeur de ce code p
est NULL
, il y aura un problème.
- Accès hors limites à l'espace alloué dynamiquement :
void test()
{
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}
- Utilisez la version gratuite pour la mémoire allouée non dynamiquement :
void test()
{
int a = 10;
int *p = &a;
free(p);
}
Celui ici a
est ouvert dans la zone de pile. Si vous utilisez free
le système de release, une erreur sera signalée.
- Libérez la même mémoire dynamique plusieurs fois :
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
malloc
L'espace mémoire dynamique ouvert ici est libéré à plusieurs reprises et le système signalera également une erreur.
- Utilisez free pour libérer une partie de la mémoire allouée dynamiquement :
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);
}
Parce que ++
le symbole changera la valeur de la variable, il p
ne pointe plus vers la position de départ de la mémoire dynamique. À ce stade, l'utilisation free
de release ne libérera pas toute la mémoire dynamique.
- Mémoire allouée dynamiquement et oubli de la libérer (fuite de mémoire) :
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
Après avoir appelé test()
la fonction, l'espace mémoire ouvert n'est pas activement libéré.Le même espace mémoire dans la zone de pile sera recyclé après l'appel de cette fonction, de sorte que l'espace ouvert int* p
ne peut pas être trouvé.Ce une fuite de mémoire . N'oubliez donc pas que la mémoire ouverte dynamiquement doit être libérée !malloc