En programmation réelle, il arrive souvent que l'espace mémoire requis dépende des données d'entrée réelles et ne puisse pas être prédéterminé. Pour de tels problèmes, il est difficile de les résoudre à l'aide de tableaux statiques. Pour résoudre les problèmes ci-dessus, le langage C fournit certaines fonctions de gestion de la mémoire. Ces fonctions de gestion de la mémoire, combinées aux pointeurs, peuvent allouer dynamiquement de l'espace mémoire selon les besoins pour construire des tableaux dynamiques, et peuvent également récupérer l'espace inutilisé pour une utilisation ultérieure, offrant ainsi un moyen d'utiliser efficacement les ressources mémoire.

Un tableau dynamique est relatif à un tableau statique. La longueur d'un tableau statique est prédéfinie, et une fois sa taille donnée dans le programme, elle ne peut pas être modifiée. Un tableau dynamique est différent ; il peut être redimensionné selon les besoins du programme. L'espace mémoire d'un tableau dynamique est alloué depuis le tas (allocation dynamique). L'espace de stockage lui est alloué en exécutant du code. Il n'est alloué que lorsque le programme exécute ces instructions. Le programmeur est responsable de libérer la mémoire.

Pourquoi utiliser des tableaux dynamiques ?

En programmation réelle, il arrive souvent que l'espace mémoire requis dépende des données d'entrée réelles et ne puisse pas être prédéterminé. Pour de tels problèmes, il est difficile de les résoudre à l'aide de tableaux statiques. Pour résoudre les problèmes ci-dessus, le langage C fournit certaines fonctions de gestion de la mémoire. Ces fonctions de gestion de la mémoire, combinées aux pointeurs, peuvent allouer dynamiquement de l'espace mémoire selon les besoins pour construire des tableaux dynamiques, et peuvent également récupérer l'espace inutilisé pour une utilisation ultérieure, offrant ainsi un moyen d'utiliser efficacement les ressources mémoire.

Comparaison des tableaux dynamiques et des tableaux statiques

Pour les tableaux statiques, leur création est très pratique, ils n'ont pas besoin d'être libérés après utilisation, et leur référencement est simple. Cependant, leur faiblesse fatale est qu'une fois créés, leur taille ne peut pas être modifiée !

Pour les tableaux dynamiques, leur création est contraignante, et après utilisation, ils doivent être libérés par le programmeur lui-même ; sinon, cela peut sérieusement provoquer des fuites de mémoire. Cependant, leur utilisation est très flexible, car ils peuvent allouer dynamiquement leur taille selon les besoins du programme.

Comment construire un tableau dynamique

Principes à suivre

Lors de l'allocation, allouez couche par couche de la couche externe vers la couche interne.

Lors de la libération, libérez couche par couche de la couche interne vers la couche externe.

Pointeurs nécessaires à la construction

Pour construire un tableau dynamique à une dimension, un pointeur à une dimension est nécessaire.

Pour deux dimensions, des pointeurs à une dimension et à deux dimensions sont nécessaires.

Trois dimensions nécessitent des pointeurs à une, deux et trois dimensions.

Et ainsi de suite.

Fonctions nécessaires à la construction

Prototype de fonctionRetourDescription de la fonction
void *malloc(unsigned int size); Succès : renvoie l'adresse de début de l'espace alloué. Échec : renvoie un pointeur nul.Alloue auprès du système un espace de tas de size octets.
void *calloc(unsigned int num,  unsigned int size); Succès : renvoie l'adresse de début de l'espace alloué. Échec : renvoie un pointeur nul.Alloue un espace de tas pour num éléments de size octets selon le type.
void free(void *p); Aucune valeur de retourLibère l'espace de tas pointé par p.
void *realloc(void *p,unsigned int  size); Succès : renvoie l'adresse de début du nouvel espace alloué. Échec : renvoie un pointeur nul.Modifie la taille de l'espace de tas pointé par p à size.

Explication :

  • (1) Il est spécifié comme étant de type void *. Cela ne signifie pas que la fonction n'a pas de valeur de retour après l'appel, mais plutôt qu'elle renvoie l'adresse d'un nœud. Le type de cette adresse est void (sans type ou type indéterminé), c'est-à-dire l'adresse de début d'une zone mémoire. Son type spécifique ne peut être déterminé qu'au moment où il est utilisé, en fonction des données de chaque champ. Il peut être converti en d'autres types à l'aide d'un transtypage. Par exemple : double *pd=NULL; pd=(double *)calloc(10,sizeof(double)); Cela signifie demander au système 10 espaces de stockage consécutifs de type double, et utiliser le pointeur pd pour pointer vers l'adresse de début de cet espace consécutif. De plus, (double) est utilisé pour convertir le type de retour de calloc() afin que l'adresse de données de type double puisse être affectée au pointeur pd.

  • (2) Le but de l’utilisation de sizeof est de calculer le nombre d’octets occupés par un type, afin de s’adapter à différents compilateurs.

    (3) Comme l’allocation dynamique ne réussit pas toujours, une routine de gestion des exceptions doit être ajoutée pour éviter que le programme ne s’arrête et laisse l’utilisateur désemparé. Habituellement, on utilise un segment de gestion des exceptions comme ceci : if(p==NULL) /* orif(!p)*/ { printf("Dynamic申pleaseMemoryfailure!\n"); exit(1); //ExceptionLogout }

  • (4) Les fichiers d’en-tête de ces quatre fonctions sont tous inclus dansdans.

  • (5) L’espace de tas alloué n’a pas de nom ; il ne peut être trouvé que par l’intermédiaire du pointeur retourné.

  • (6) N’utilisez jamais free sur un bloc de stockage non alloué dynamiquement. Vous ne pouvez pas non plus libérer deux fois la même zone mémoire avec free. Par exemple : free(p);free(p);

  • (7) Lors de l’appel à free(), la mémoire pointée par le pointeur passé est libérée, mais la valeur du pointeur dans la fonction appelante peut rester inchangée, car p est passé à la fonction comme paramètre formel. À proprement parler, la valeur du pointeur libéré est invalide, car il ne pointe plus vers la zone mémoire allouée. Toute utilisation de celui-ci à ce moment-là peut causer des problèmes.

La différence entre malloc et calloc

Pour la région mémoire allouée avec malloc, si elle n’a pas été utilisée auparavant, chaque bit qu’elle contient peut être à 0 ; inversement, si cette partie de mémoire a déjà été allouée, libérée, puis réallouée, elle peut contenir toutes sortes de données résiduelles. Autrement dit, un programme utilisant malloc() peut s’exécuter normalement au début (lorsque l’espace mémoire n’a pas encore été réalloué), mais après un certain temps (après que l’espace mémoire a été réalloué), des problèmes peuvent survenir. Par conséquent, il doit être initialisé avant utilisation (memset peut être utilisé pour l’initialiser à 0). Cependant, l’espace alloué en appelant calloc() est déjà initialisé à 0 au moment de l’allocation. Lorsque vous choisissez entre calloc() et malloc(), vous devez déterminer s’il faut initialiser l’espace mémoire alloué, puis choisir la fonction correspondante.

Méthode de construction spécifique

Prenons comme exemple le tableau d'entiers tridimensionnel array[n1][n2][n3].

Suivez d'abord le principe d'allocation couche par couche, de la couche externe vers la couche interne :

Le pointeur le plus externe est array, qui est un pointeur tridimensionnel. Ce vers quoi il pointe est array[], qui est un pointeur bidimensionnel. Donc pour array

l'allocation de mémoire doit être :

array=(int***)calloc(n1,sizeof(int**));

Le pointeur de la couche suivante est array[], qui est un pointeur bidimensionnel. Ce vers quoi il pointe est array[][], qui est un pointeur unidimensionnel. Donc pour array[]

l'allocation de mémoire doit être :

for(i=0;i<n1;i++)
{
    array[i]=(int**)calloc(n2,sizeof(int*));
}

Le pointeur le plus interne est array[][], qui est un pointeur unidimensionnel. Ce vers quoi il pointe est array[][][], qui est une constante entière. Donc pour allouer de la mémoire à array[][], vous devez :

for(i=0;i<n1;i++)
{
    for(j=0;j<n2;j++)
    {
        array[i][j]=(int*)calloc(n3,sizeof(int));
    }
}

Bien sûr, vous pouvez les combiner ensemble comme suit :

int i,j,k;
int n1,n2,n3;
int ***array;
scanf("%d%d%d",&n1,&n2,&n3);
array=(int***)calloc(n1,sizeof(int**));
for(i=0;i<n1;i++)
{
    array[i]=(int**)calloc(n2,sizeof(int*));
    for(j=0;j<n2;j++)
    {
        array[i][j]=(int*)calloc(n3,sizeof(int));
        for(k=0;k<n3;k++)
        {
            array[i][j][k]=i+j+k+1;
        }
    }
}

Enfin, n'oubliez pas de libérer cette mémoire. Cela doit suivre le principe de libération couche par couche, de la couche interne vers la couche externe.

Pour le processus d'analyse, reportez-vous à la solution ci-dessus ; je n'entrerai pas dans les détails ici. Je donnerai simplement le code :

for(i=0;i<n1;i++)
{
    for(j=0;j<n2;j++)
    {
        free(array[i][j]);//释放第三维指针
    }
}
for(i=0;i<n1;i++)
{
    free(array[i]);//释放第二维指针
}
free(array);//释放第一维指针

Le processus de création pour d'autres dimensions, comme en quatre dimensions, est à peu près le même, donc je n'entrerai pas dans les détails ici.