INF155 – COURS 08 Les Classes d’Allocation, L’Allocation Dynamique de Mémoire,
INF155 – COURS 08 Les Classes d’Allocation, L’Allocation Dynamique de Mémoire, Les Algorithmes de Tri, Et les Fichiers Textes Eric Thé Service des Enseignements Généraux École de Technologie Supérieure Hiver 2014 8.1 La Notion de « Storage Class » : En langage C, une variable a toujours 2 attributs : le type de données et le type d’allocation de mémoire (ou storage class). Il existe 4 types d’allocation de mémoire différents. ( auto ) c’est le type d’allocation par défaut, donc on utilise jamais ce préfixe. Les variables auto sont des variables locales aux blocs-fonctions et elles sont automatiquement détruites à la sortie du bloc de sa déclaration. extern ce type d’allocation est utilisé pour indiquer qu’une variable à déjà été déclarée ailleurs globalement dans le projet actuel. Donc, le préfixe extern dit au compilateur « va chercher cette déclaration de variable ailleurs ». Exemple : Voici un petit projet à 2 fichiers-source. Fich1.cpp Fich2.cpp #include<....> int fnc(void) int fnc(void); //prototype { extern int a; int a=1, b=2; //variables globales! int b; void main(){ a = b = 4; printf("%d %d %d", a, b, fnc()); return (a+b); } } Va afficher : 4 2 8 register indique une allocation mémoire dans un registre de donnée du micro- processeur (mémoire haute-vitesse). Ce type d’allocation est en fait une demande au compilateur, il n’est pas garanti que cette demande puisse être réalisée. Il est utilisé uniquement pour optimiser la performance de code. Ex. register long i; for (i=0; i < 2000000000; i++) . . . static va forcer une variable locale à demeurer vivante après la fin de son bloc de déclaration. L’espace mémoire de cette variable locale demeure réservé pour la durée entière du programme (contrairement au type d’allocation auto). L’initialisation, si elle est présente, ne sera faite qu’à la première rencontre de la variable, sinon la variable statique est initialisée à zéro. Exemple : int plus1(void) //implémente un compteur cyclique 0, 1, 2, .., 99, 0, 1.. { static int cnt=0; //initialisation pour la 1 ière fois seulement ! ++cnt; if (cnt==100) cnt = 0; return cnt; } Si on enlève le préfixe static, la fonction renvoie toujours « 1 »!! Attention!! Dans cet exemple, « cnt » n’est PAS une variable globale! 1 GET_NOM2.C : Fonction qui saisi un nom dans un tableau de char #include<stdio.h> #include<conio.h> #include<string.h> #include"WinConsole.h" //pour "clrscr", "clreol" et "gotoxy" #define BKSPACE 8 #define RETURN 13 #define MAXL 60 char * getnom(int, int); //prototype de la fonction de saisie int main() { char * nom; clrscr(); gotoxy(10,5); printf("Entrez une chaîne de caractères : "); nom = getnom(45, 5); gotoxy(10,7); printf("chaine = [%s] longueur = %d\n", nom, strlen(nom)); system("pause"); return 0; } /* GET_NOM : va saisir un nom a partir de la position-écran (px, py) */ char * getnom(int px, int py) { static char buff[MAXL+1]; //variable locale "static" ne sera pas détruite char ch; int l = 0, fini = 0; do { ch = getch(); switch (ch) { //selon le caractère saisi au clavier.. case BKSPACE : //BKSPACE: on efface une lettre dans la chaine if (l) { --l; buff[l] = '\0'; --px; //on recule px de -1 à l'écran gotoxy(px, py); clreol(); } break; case RETURN : fflush(stdin); fini = 1; //vider le buffer "stdin" break; default : //tout autre char: on ajoute une lettre if (l<MAXL) { //si il reste de la place.. buff[l] = ch; l++; buff[l] = '\0'; gotoxy(px,py); printf("%c", ch); px++; //on déplace px de +1 a l'écran } } } while (!fini); //boucler jusqu'a la touche "ENTER" return buff; //on renvoie l'adresse (char *) du tableau local "buff" } 2 8.2 L’Allocation Dynamique de Mémoire : En C, l’espace-mémoire est divisée en deux morceaux: l’espace de la pile (stack size) et l’espace-tas (heap size). L’espace de la pile est réservé pour entreposer les appels de fonctions en incluant toutes les variables locales de ces fonctions. Cet espace est assez limité et avec Visual C++ il est presque impossible de savoir la dimension de l’espace-pile disponible. Donc, lorsqu’on utilise des tableaux, on risque parfois de “défoncer” la pile! Pour éviter ces problèmes, le programmeur qui pense utiliser des structures énormes dans ses programmes fait appel à l’espace disponible dans le tas (heap). Les variables qui permettent d’accéder à l’espace du tas sont des pointeurs. En fait, ce sont des pointeurs simples qui se trouvent dans l’espace de la pile et leurs valeurs sont en fait une adresse d’un emplacement plus gros situé, lui, dans le tas. L’allocation dynamique, c’est la capacité d’obtenir du système d’exploitation (ou SE) des blocs de mémoire qui viennent du « tas » augmentant donc la mémoire déjà alloué au programme en compilation. T 491 491 TAS (HEAP) PILE (STACK) Le SE possède une réserve d’octets libres (heap size) dans laquelle le programmeur puise grâce à une demande explicite. Nous verrons comment on peut l’utiliser pour permettre l’utilisation de “très gros tableaux” dans nos programmes, et aussi comment créer des structures à dimension dynamique. En C, les fonctions malloc, calloc et realloc remplissent cet usage et renvoient des pointeurs génériques de type void * sur le premier octet du bloc offert. Lorsque le pointeur retourné par le SE est NULL, ceci indique que l’allocation dynamique a échouée. Les Fonctions malloc et calloc : Avec les fonctions malloc et calloc de la librairie <stdlib.h>, on peut obtenir de l’allocation dynamique de mémoire durant l’exécution (contrairement aux tableaux statiques). Syntaxe générale : pointeur = (type *) calloc(n, sizeof(type)); pointeur = (type *) malloc(n * sizeof(type)); nombre d’éléments désirés dimension d’un élément du tableau dynamique Exemple : int *tab, n; /* Au départ, la variable « tab » n’est PAS un tableau. Elle est une “possibilité” de tableau. Pour réserver l’espace-mémoire de « n » cellules, il faut utiliser la fonction « calloc » ou « malloc ». Dans les cas que nous allons voir, nous utiliserons UNE et UNE SEULE FOIS le « calloc » ou « malloc » sur la variable-pointeur. */ 3 printf("Entrez la dimension du tableau : "); scanf("%d", &n); Soit a) tab = (int *) calloc(n, sizeof(int)); Ou b) tab = (int *) malloc(n * sizeof(int)); /* Le tableau est maintenant prêt à être utilisé. */ NOTE : La fonction « calloc » va initialiser toutes les cases de l’espace-mémoire réservé à 0. « malloc » ne nettoiera pas l’espace-mémoire réservé. Un bloc d’octets obtenu en allocation dynamique n’a pas de « storage class » et la remise en liberté d’un bloc de mémoire est donc à la charge du programmeur. Quand on n’a plus besoin d’une variable créée dans le tas avec « calloc » ou « malloc », il faut libérer l’espace-mémoire avant la fin du bloc de définition du pointeur. Sinon cette partie de mémoire est irrémédiablement perdue jusqu’à la fin du programme ! La fonction free en C remplie cette tâche. Mais attention, tenter de libérer explicitement, avec free, une zone de mémoire non précédemment allouée ou déjà “désallouée”, est une erreur et rendra le comportement du SE imprévisible. Pour plus de sureté, une fois la mémoire libérée, on assigne explicitement au pointeur la valeur NULL. free(tab); //libérer la mémoire réservée tab = NULL; Remarquez qu’un tableau statique ou dynamique s’utilise de la même façon . La forme dynamique n’est visible qu’à travers le calloc / malloc et le free. En fait, pointeurs et tableaux sont intimement reliés. Obtenir, se servir, puis détruire un tableau dynamique est juste un peu plus technique que de travailler avec un tableau statique. INVERSE.CPP : Programme qui va remplir et inverser le contenu d'un tableau dynamique. #include <stdio.h> #include <stdlib.h> void main(void) { void inverse(int *, int); //prototype local de la fonction int *tab, i, n; system("cls"); do { printf("\n n = "); //demander et valider la dimension désirée du tableau scanf("%d", &n); } while (n <= 1); tab = (int *) malloc(n*sizeof(int)); //allocation dynamique pour le tableau for (i=0; i<n; ++i) { printf("tab[ %d ] = ", i); scanf("%d", &tab[i]); //remplir le tableau } printf("\n\n"); for (i=0; i<n; ++i) printf("%d, ", tab[i]); 4 /* inversion du tableau */ inverse(tab, n); printf("\n\n"); for (i=0; i<n; ++i) printf("%d, ", tab[i]); free(tab); tab = NULL; //libérer l'espace-mémoire system("pause"); } //fonction qui inverse le contenu du tableau « x » de taille « n » void inverse(int x[], int n) { int i, temp; for (i=0; i<(n/2); ++i) { //on va jusqu'a la moitié du tableau seulement! temp = x[i]; x[i] = x[n-i-1]; x[n-i-1] = temp; } } TABDYNAM.CPP : Version dynamique du programme TABNOMS.CPP. #include<stdio.h> #include<stdlib.h> //nécessaire pour la fonction malloc() #include<assert.h> //nécessaire pour la fonction assert() #include<string.h> #define MAXCH 50 //nombre MAX de lettres dans une chaine #define MAXELE 10 //nombre de noms dans le tableau typedef char * Tptrch; //type POINTEUR global pour les chaines dynamiques typedef Tptrch Ttablo[MAXELE]; //type global pour le tableau de 10 noms void init_tab(Ttablo); //mets tous les pointeurs du tableau a NULL void lire_tab(Ttablo); //va saisir les noms du tableau en interactif uploads/Ingenierie_Lourd/ cours08-c.pdf
Documents similaires
-
19
-
0
-
0
Licence et utilisation
Gratuit pour un usage personnel Attribution requise- Détails
- Publié le Jul 21, 2021
- Catégorie Heavy Engineering/...
- Langue French
- Taille du fichier 0.2615MB