Programmation en C :

14. Allocations dynamiques en C

codecommentaire
/* prog14a.c */
#include <stdio.h>
#include <stdlib.h>

int i;
int *T;

int main() {
 T=NULL;
 printf("avant malloc T=%p\n",T);
 T=malloc(10*sizeof(int));
 if (T==NULL) return(-1);
 printf("apres malloc T=%p\n",T);
 for (i=0;i<10;i++) T[i]=i;
 for (i=0;i<10;i++) printf("%d ",T[i]);
 free(T); T=NULL;
 return(0);
}
Les fonctions d'allocation dynamique nécessitent la librairie stdlib.

La fonction malloc réserve l'espace en mémoire pour 10 entiers et place l'adresse du premier élément de ce tableau dans la variable T. On s'assure que cette allocation a bien été réalisée en vérifiant que T est différent de NULL. Comme pour les tableaux, il faudra s'assurer de rester dans les limites de l'espace alloué.
N.B. on programmera généralement une conséquence moins brutale que return(-1) si l'allocation a échoué.

Remarque. A la place de la fonction malloc, on aurait pu utiliser la fonction calloc à deux paramètres : T=calloc(10,sizeof(int));

free(T) libère la mémoire allouée pour le pointeur T. A titre de précaution, on place la valeur NULL dans T.
Remarque. Quand le programme se termine, toutes les allocations de mémoire de ce programme sont théoriquement libérées, mais il est toujours important d'éviter le risque de fuite de mémoire.
exécutioncommentaire
→ ./executable14a
avant malloc T=0x0
apres malloc T=0x6000484b0
0 1 2 3 4 5 6 7 8 9
codecommentaire
/* prog14b.c */
#include <stdio.h>
#include <stdlib.h>

int*T,*U,i;

main() {
 T=NULL;
 T=malloc(10*sizeof(int));
 if (T!=NULL) {
   for (i=0;i<10;i++) T[i]=i;
   U=T;
   for (i=0;i<10;i++) printf("%d ",U[i]);
   free(T); T=NULL;
 }
 else printf("bye!");
}
Ici aussi, le travail sur le tableau d'entier n'est effectué que si l'allocation a réussi, mais ce programme peut gérer un souci d'allocation.

T et U sont des pointeurs sur des entiers, i est un entier : le compilateur associe * au nom de variable qui le suit. Il aurait été bien évidemment plus lisible de séparer :  int *T,*U; int i;

U prend la valeur de pointeur de T et donne donc accès au même espace mémoire.
exécutioncommentaire
→ ./executable14b
0 1 2 3 4 5 6 7 8 9
codecommentaire
/* prog14c.c */
#include <stdio.h>
#include <stdlib.h>

int i;
int *T;

main() {
 T=NULL;
 T=malloc(5*sizeof(int));
 if (T==NULL) return;
 for (i=0;i<5;i++) T[i]=i;
 for (i=0;i<5;i++) printf("%d ",T[i]);
 T=realloc(T,10*sizeof(int));
 if (T==NULL) return;
 printf("\n");
 for (i=5;i<10;i++) T[i]=i;
 for (i=0;i<10;i++) printf("%d ",T[i]);
 free(T); T=NULL;
}
La fonction realloc augmente l'espace alloué à un pointeur.
N.B. Cette réallocation se fait soit en agrandissant l'espace réservé pour le pointeur, soit en réservant un nouvel espace plus grand où les anciennes données sont transférées.
exécutioncommentaire
→ ./executable14c
0 1 2 3 4
0 1 2 3 4 5 6 7 8 9
Les cinq valeurs de la première allocation ont bien été conservées par la réallocation.
codecommentaire
/* prog14d.c */
#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int jour;
  int mois;
  int an;
  } T_date;

T_date d;
T_date *T=NULL;

afficherdate(T_date d) {
 printf("%d/%d/%d\n",d.jour,d.mois,d.an);
}
T_date extraire(T_date *t, int n) {
 return t[n];
}
main() {
 T=malloc(2*sizeof(T_date));
 if (T==NULL) return;
 T[0].jour=31; T[0].mois=12; T[0].an=1999;
 T[1].jour=1; T[1].mois=1; T[1].an=2000;
 printf("T[0]=");
 afficherdate(T[0]);
 printf("T[1]=");
 afficherdate(T[1]);
 d=extraire(T,0);
 free(T); T=NULL;
 printf("copie de T[0] : ");
 afficherdate(d);
}
On peut allouer un espace mémoire pour un type structuré.

La variable d conserve la date qui a été copiée à partir du tableau T, même après que ce tableau ait été désalloué.
N.B. Par contre, après free(T); T=NULL; une tentative d'accès à T[0] donnerait une erreur (segmenttion fault).
exécutioncommentaire
→ ./executable14d
T[0]=31/12/1999
T[1]=1/1/2000
copie de T[0] : 31/12/1999
codecommentaire
/* prog14e.c */
#include <stdio.h>
#include <stdlib.h>
#define lignes 3
#define colonnes 5

int i,j;
int ** T;

main () {
 T=malloc(lignes*sizeof(int*));
 for (i=0;i<lignes;i++)
   T[i]=malloc(colonnes*sizeof(int));
 for (i=0;i<lignes;i++)
   for (j=0;j<colonnes;j++)
     T[i][j]=i+j;
 for (i=0;i<lignes;i++) {
   for (j=0;j<colonnes;j++) printf("%d ",T[i][j]);
   printf("\n");
   }
for (i=0;i<lignes;i++) free(T[i]);
free(T); T=NULL;
}
L'allocation d'un tableau à deux dimensions se fait en deux étapes, de même que sa désallocation.

Important.
Ce programme ne gère pas les défauts d'allocation :
– ni pour la première étape, où il faudrait ajouter un contrôle  if (T==NULL) ...
– ni pour la deuxième étape, où il faudrait ajouter des contrôles  if (T[i]==NULL) ...
exécutioncommentaire
→ ./executable14e
0 1 2 3 4
1 2 3 4 5
2 3 4 5 6