Eric Thé
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
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
int main()
{ char * nom;
clrscr();
gotoxy(10,5);
printf("Entrez une chaîne de caractères : ");
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"
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.
491
TAS (HEAP)
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));
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);
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]);
typedef char * Tptrch; //type POINTEUR global pour les chaines dynamiques
typedef Tptrch Ttablo[MAXELE]; //type global pour le tableau de 10 noms
system("cls");
init_tab(tab);
lire_tab(tab);
tri_tab(tab);
system("pause");
aff_tab(tab);
5
void init_tab(Ttablo tb) //mets tous les noms du tableau = vide
{ for (int i=0; i<MAXELE; i++)
tb[i] = NULL; // = pointeur vide
}
void swap(Tptrch s1, Tptrch s2) //échange de deux chaines avec "strcpy()"
{ char tmp[MAXCH+1]; //la variable-tampon DOIT ETRE DE TYPE "char []"
strcpy(tmp, s1); strcpy(s1, s2); strcpy(s2, tmp);
}
6
#define N 15 //dimension du tableau
void BULLE(int *, int); //tri-bulle (sans optimisation)
void BULLE2(int *, int); //tri-bulle avec drapeau
void main(void)
{ int i, x[N];
system("cls");
srand(time(NULL)); //initialiser le "random"
gotoxy( 1, 1); printf("Avant le tri");
gotoxy(31, 1); printf("Apres le tri");
for (i=0; i<N; ++i) {
x[i] = rand()%250; //remplir le tableau
gotoxy( 1, i+3); printf("(%4d)", x[i]); //afficher verticalement
}
BULLE(x, N); //faire le tri des valeurs
for (i=0; i<N; ++i) {
gotoxy(31, i+3); printf("(%4d)", x[i]); //afficher verticalement
}
system("pause");
}
void permute(int *a, int *b) //va échanger deux valeurs du
tableau
{ int temp = *a;
*a = *b;
*b = temp;
}
7
}
2. Tri par Sélection
Chercher la liste au complet pour trouver le plus petit élément et le mettre à sa place.
Chercher ensuite les N-1 autres éléments et mettre le plus petit à la deuxième position
du tableau. Répéter ainsi pour les N-2 autres éléments, les N-3 autres éléments, etc...
(voir exemple TABDYNAM.CPP et aussi SELECT.CPP)
8
nom complet avec le chemin
d’accès si nécessaire
Les « modes » d’ouverture de fichier avec « fopen » :
"r" ouvrir le fichier pour la lecture seulement
"w" ouvrir le fichier pour écriture à partir du début
"a" ouvrir le fichier pour écriture à partir de la fin (ajouter au fichier)
"r+"
"w+" ouvrir le fichier pour la lecture + écriture
Ex. fentree = fopen("c:\\donnees.txt", "r");
fsortie = fopen("c:\\bidon\\result.txt", "w");
Ensuite, pour faire la lecture de données à partir du pointeur fentree on utilise :
char fgetc(FILE *ptr); renvoie le prochain caractère à lire dans le fichier *ptr.
char *fgets(char *str, int n, FILE *ptr); va lire une chaîne de caractères
de longueur maximale (n-1) dans la
chaîne *str à partir du fichier *ptr
NOTE : la fonction renvoie également un pointeur sur le début de la chaîne lue.
int fputs(const char *str, FILE *ptr); va écrire la chaîne de caractères *str
à la fin du fichier pointé par *ptr. La
fonction renvoie le nombre de
caractères ajoutés au fichier.
9
while (fscanf(fentree, "%d", &val)==1)
somme += val;
10
printf("%d ",n); //..écrire l'entier lu a l'écran
fscanf(entree,"%d",&n); //..et lire le prochain entier
}
fclose(entree); //fermer le fichier
}
IOTEST.CPP : Exemple de gestion de fichiers texte avec plusieurs types
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <assert.h> //pour la fonction de validation automatique “assert()”
void copie (FILE *, char *); //pour copier d'un fichier vers un autre
void echoe (char *); //pour afficher contenu d'un fichier a l'écran
char message[] = " TOUT EST OK! ";
/*-----------------------------------------------------------------------*/
int main(void)
{ FILE *stream; //variable pointeur-fichier
int i = 100;
char c = 'C';
float f = 1.234;
int t[3] = { 11, 22, 33 };
stream = fopen("c:\\bidon\\temp1.txt", "w"); /* mode: écrire */
assert(stream); /* validation du fopen */
fclose(stream);
stream = fopen("c:\\temp\\temp1.txt", "r"); /* mode: lecture */
assert(stream); /* validation du fopen */
11
while (!feof(out)) //tant que c'est pas la fin du
fichier..
{ putchar(fgetc(out)); } //..écrire a l'écran le caractère de "out"
fclose(out);
}
/*-----------------------------------------------------------------------*/
LECTFICH.C : Exemple pour lire une matrice de nombres réels à partir d'un fichier
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<assert.h>
#define MAXDIM 20 //ceci est une taille MAXIMALE, pas la taille actuelle!
/*----------------------------------------------------------------------*/
/* Fonction pour lire une matrice d'un fichier texte.
Elle renvoie la taille réelle de la matrice lue.
Le format du fichier est standard : la première ligne nous donne la
taille, et les lectures suivantes sont les éléments de la matrice. */
int flire_mat(char * nom_fichier, matrice M)
{ FILE * fichier;
int i, j, taille;
double x;
fichier = fopen(nom_fichier,"r");
assert(fichier);
/*------------------------------------------------------------------------*/
int main()
12
{ matrice valeurs; /* la matrice des valeurs */
int taille;
13