[Langage C] Explication détaillée de la gestion dynamique de la mémoire

À 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 :

  1. La taille de l'ouverture de l'espace est fixe.
  2. 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. sizeC'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);

mallocEn fait, calloccela ressemble beaucoup à. rallocLe paramètre en sizeest la taille de chaque type de données pour lequel vous souhaitez postuler, qui numest 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é mallocpar size. Les autres fonctionnalités sont mallocsimilaires, 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 rallocplus pratique à utiliser.

Recyclage dynamique de la mémoire ---- gratuit

En fait malloc, callocdes 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 freeune fonction. Le prototype de la fonction est le suivant :

void free (void* ptr);

Le pointeur ici ptrpointe 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 ptrles pointeurs ;

  • Si ptrl'espace pointé par le paramètre n'est pas alloué dynamiquement, freele comportement de la fonction n'est pas défini.
  • Si l'argument ptr est NULLun pointeur, la fonction ne fait rien.

Deux autres choses à noter :

  1. Après avoir libéré l'espace ouvert, le pointeur d'origine pointant vers cet espace ptrstocke 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.
  2. 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 .
    Insérer la description de l'image ici

Comme le montre la figure ci-dessus, le pointeur d'origine ppointe vers le premier bloc mémoire et, après l'opération, pil 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, reallocla 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 ptrpointe vers l'adresse mémoire à ajuster sizeet 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.
Insérer la description de l'image ici

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 .
Insérer la description de l'image ici
Il existe également le problème de l'impossibilité de retrouver l'espace mémoire d'origine reallocen raison de l'affectation du pointeur lorsque l'espace mémoire dynamique ne parvient pas à être ouvert . NULLNous créons généralement un nouveau pointeur pour recevoir l'adresse , NULLpuis 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

  1. 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 pest NULL, il y aura un problème.

  1. 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);
 }
  1. Utilisez la version gratuite pour la mémoire allouée non dynamiquement :
void test()
{
    
    
     int a = 10;
     int *p = &a;
     free(p);
}

Celui ici aest ouvert dans la zone de pile. Si vous utilisez freele système de release, une erreur sera signalée.

  1. Libérez la même mémoire dynamique plusieurs fois :
void test()
 {
    
    
      int *p = (int *)malloc(100);
      free(p);
      free(p);//重复释放
 }

mallocL'espace mémoire dynamique ouvert ici est libéré à plusieurs reprises et le système signalera également une erreur.

  1. 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 pne pointe plus vers la position de départ de la mémoire dynamique. À ce stade, l'utilisation freede release ne libérera pas toute la mémoire dynamique.

  1. 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* pne 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

Je suppose que tu aimes

Origine blog.csdn.net/2301_77404033/article/details/133247008
conseillé
Classement