Classe 1 CSI2572
Autres modificateurs de déclaration de variables: & volatile & register & static & auto & extern & const volatile Indique au compilateur que la variable risque d'être modifiée par des facteurs exterieurs au programme en train d'éxécuter (thread). Les variables déclarées volatile sont laissé à l'extérieur du processus d'optimization par le compilateur. volatile int a = 4; register Indique au compilateur que la variable sera très utilisée, et qu'elle devrait donc être placée directement dans un des registres du CPU pour accès direct. Très peu utilisé, et réduira vitesse d'éxécution si mal utilisé. register char b = 'a'; static Indique au compilateur que la variable doit être placée dans le texte du programme (loadé avant le début de l'éxécution). La variable devient une variable globale. static double c = 1.3; auto Jamais (ou rarement) indiqué. C'est le défaut. Indique au compilateur que la variable doit être placée dans le programe stack. auto float d = 4.3; float d = 4.3; extern Variable globale, que on peut accéder d'un autre fichier (aussi placées dans le texte du programe). extern int e; const Déclare une constante. Ne peut être assignée après initialisation. const int e = 4;
Autres Structures de données: Les tableaux D int a[12]; Tableau de 12 éléments, typés entiers D char a[]; Tableau d'éléments typés charactères, taille détérminée à l'initialisation (chaine de caractères si tableau terminé par '\0') D float a[5][4]; Matrice de nombres point flottants (5x4) Les tableaux sont indexés de 0 à n-1 (n étant le nombre d'éléments ds le tableau) /*INITIALISATION D'1 TABLEAU */ #define ROW 2 #define COLUMN 3 int main(){ int n[100], ctr; for(ctr = 0; ctr < 99; ctr++){ n[ctr] = 0; } int w[5] = {32, 21, 56, 32, 45}; char a[] = "Hello"; char b[6] = {'H','e','l','l','o','\0'}; int mat[ROW][COLUMN] = {{1,2}, {2,6}, {5,7}}; mat[0][2] = 3; }
Autres Structure de données ( enum ) enum ¢ pour créer des constantes énumérées int main(){ enum day{ Mon=10, Tues, Wed=45, Thurs, Fri, Sat=70, Sun}; day T1 = Tues; day T2 = Fri; day T3 = Sun; printf("%d\n",T1); printf("%d\n",T2); printf("%d\n",T3); return 0; }
Autres Structure de données ( enum ) struct 5 pour créer des structures + complexes #include int main(){ struct Point3D{ int x; int y; int z; }; struct Point3D point = {3,3,6}; printf("x = %d\n", point.x); printf("y = %d\n", point.y); printf("z = %d\n", point.z); point.y = 5; return 0; } typedef struct { int x; int y; int z; } Point3D; Point3D point = {3, 3, 6}; struct point { double x; double y; }; struct point p; p.x = 13.2; p.y = -4.2; void f(struct point p) {... } struct point f() { struct point p;... return p; } struct point p1, p2;... p1 = p2;
pointeurs Une variable qui pointe vers une autre variable Une adresse mémoire 0X0A2BB 0X0A2B9 0X0A2BA 0X0A2B8 0X0A2BC 0X0A2BD 0X0A2BE … int* ptr; pas encore défini (garbage) int* ptr; int a; int* ptr; int a; ptr = &a; 0X0A2BD ptr est maintenant initialisé int* ptr; int a; ptr = &a; a = 123; 123 ptr a
0X0A2BB 0X0A2B9 0X0A2BA 0X0A2B8 0X0A2BC 0X0A2BD 0X0A2BE … 0X0A2BD 123 ptr a &a ptr &ptr *ptr a == 0x0A2BD == 0x0A2B8 == 123
Pointeurs (encore): Les pointeurs aussi sont typés. Mais il existe un pointeur de type générique. C'est le pointeur de type: void* int a; char c; int* pi; char* pc; void* gp; pi = &a; /* OK */ pi = &c; /* TYPE MISMATCH */ pc = &c; /* OK */ pc = &a; /* TYPE MISMATCH */ gp = &a; /* OK */ gp = &c; /* OK */
Next: E Allocation de mémoire E Déallocation de mémoire E Tableaux (n dimensions) E Arithmetique des pointeurs E Pointeurs sur fonctions
Allocation de mémoire: Dans C comme dans C++, il y à 3 manières d’allouer de l’espace mémoire: 3 Mémoire Statique: La mémoire est allouée par le linker au début du programme, et est libérée lorsque le programme à fini d'éxécuter. 3 Mémoire Automatique: La mémoire est automatiquement allouée, gérée et libérée pendant l'éxecution du programme. Les arguments des fonctions et les variables locales obtiennent de l'espace de cette manière 3 Mémoire Dynamique: La mémoire est requise explicitement par le programme(ur). Le programme(ur) gère et libère la memoire (en principe).
Où se trouve la variable? compile-time program-text i variables globales i variables static automatic stack i variables locales i parametres de fonctions i valeur de retour des fonctions run-time heap i malloc i calloc i realloc
Les espaces mémoire alloués de manière statiques ou automatiques ne sont généralement pas 1 problème pour le programmeur (qui n'a généralement pas besoin de s'en occuper). Il faut cependant en être conscient pour pouvoir optimiser ces programmes. int x; /* global */ int f(int n) { int x; /* local to f */ if (n > 3) { int x; /* local to if */... } { /* a local scope * "out of the blue" */ int x; } S T A T I Q U E Constantes Variables globales Variables déclarées: static A U T O M A T I Q U E Variables Locales Paramétres de fonctions Retours de fonctions
Allocation de mémoire dynamique: C L'allocation de mémoire dynamique a par contre tendance à être 1 peu + problématique pour le programmeur. C lui (ou L) qui l'alloue, qui la gère et qui n'oubli pas de la rendre au systeme quand il n'en a + besoin. Si la job est mal faite, attendez vous à des problèmes!!! C Le heap sert à l'allocation dynamique de blocs de mémoire de taille variable. C De nombreuses structures de données emploient tout naturellement l'allocation de mémoire dans le heap, comme par exemple les arbres et les listes. C Le seul risque est la fragmentation du heap, par allocation et libération successives. Il n'existe pas en C de mécanisme de "ramasse-miettes" (garbage collector).
int * x = (int*)malloc(sizeof(int)); int * a = (int*)calloc(10,sizeof(int)); *x = 3; a[2] = 5; free(a); a = 0; free(x); x = 0; Fonctions de gestion de mémoire (C) void* malloc(size_t size); void* calloc(size_t n, size_t size); void* realloc(void * ptr,size_t size); void free(void * ptr);
Demande d'allocation de mémoire ( malloc ) malloc demande l'allocation d'un bloc de mémoire de size octets consécutifs dans la zone de mémoire du heap. Syntaxe : #include void *malloc(size_t size); Valeur retournée : Si l'allocation réussit, malloc retourne un pointeur sur le début du bloc alloué. Si la place disponible est insuffisante ou si size vaut 0, malloc retourne NULL. Attention : Les fonctions d'allocation dynamique retournent des pointeurs sur des void. Il faut donc opérer des conversions de types explicites pour utiliser ces zones mémoire en fonction du type des données qui y seront mémorisées. #include main() { char *ptr; struct s_fiche { char nom[30]; int numero; struct s_fiche *suiv; } *fiche; ptr = (char *) malloc(80); /* demande d'allocation de 80 octets */ if ( ptr == NULL) {printf("Allocation mémoire impossible\n"); exit(1);} if (fiche = (struct s_fiche *) malloc(sizeof(struct s_fiche)) == NULL) {printf("Allocation mémoire impossible\n"); exit(1);} free(fiche); /* libération de la mémoire */ free(ptr); }
Demande d'allocation de mémoire ( calloc ) La fonction calloc réserve un bloc de taille nelem x elsize octets consécutifs. Le bloc alloué est initialisé à 0. Syntaxe : #include void *calloc(size_t nelem, size_t elsize); Valeur retournée : Si succès, calloc retourne un pointeur sur le début du bloc alloué. Si échec, calloc retourne NULL s'il n'y a plus assez de place ou si nelem ou elsize valent 0. Attention : Les fonctions d'allocation dynamique retournent des pointeurs sur des void. Il faut donc opérer des conversions de types explicites pour utiliser ces zones mémoire en fonction du type des données qui y seront mémorisées. #include int main() { int *str = NULL; str = (int *) calloc(10, sizeof(int)); printf("%d\n", str[9]); free(str); return 0; }
Demande d'allocation de mémoire ( realloc ) La fonction realloc ajuste la taille d'un bloc à size octets consécutifs. Syntaxe : #include void *realloc(void *ptr, size_t size); Valeur retournée : Si succès, retourne l'adresse de début du bloc réalloué. Cette adresse peut avoir changé par rapport à celle fournie en argument. Dans ce cas, le contenu de l'ancien bloc est copié à la nouvelle adresse et l'ancienne zone est automatiquement libérée. Si échec, (pas assez de place en mémoire ou size à 0), realloc retourne la valeur NULL Arguments : ptr : pointeur sur le début d'un bloc mémoire créé par malloc, calloc, ou realloc. Si ce pointeur est NULL, realloc équivaut à malloc. size : nouvelle taille du bloc en octets.
2 dimensions (matrices): double ** alloc_matrix(int n, int m) { double ** M = (double**)calloc(n,sizeof(double*)); int i; for(i=0; i<n; ++i) M[i] = (double*)calloc(m,sizeof(double)); return M; }
Libération!! La fonction free libère un bloc mémoire d'adresse de début ptr. J Syntaxe : #include void free(void *ptr); J Ce bloc mémoire a été précédemment alloué par une des fonctions malloc, calloc, ou realloc. J Attention : Il n'y a pas de vérification de la validité de ptr. Ne pas utiliser le pointeur ptr après free, puisque la zone n'est plus réservée. J A tout appel de la fonction malloc ( ou calloc ) doit correspondre un et un seul appel à la fonction free. #include int main() { char *str; str = (char *) malloc(100 * sizeof(char)); gets(str); /* saisie d'une chaine de caracteres */ /* suppression des espaces en tete de chaine */ while ( *str == ' ') str++; /* free ne libere pas toute la zone allouee car ptr ne designe plus le debut de la zone memoire allouee par malloc */ free(str); return 0; }
Déallocation de la mémoire de notre matrice 2 dimensions: void free_matrix(double** M, int n) { int i; for(i = 0; i<n; ++i) free(M[i]); free(M); }... free_matrix(M, 5); M = 0;
Arithmetique des pointeurs: H Il est possible de déplacer la position d'un pointeur en lui ajoutant ou en lui retranchant une valeur entière. Par exemple: ptr_str + 1 Le compilateur aqvance d'un ou plusieur octets par rapport à la position de ptr_str. H Les entiers ajoutés ou retranchés ont des tailles scalaires différentes selon le type du pointeur. En d'autres termes, ajouter ou soustraire 1 à un pointeur n'équivaut pas à se déplacer d'un octet, mais à aller à l'adresse suivante ou précédente.
+ Le format général du déplacement d'un pointeur est le suivant: pointeur + n n est un entier positif ou négatif. pointeur est le nom de la variable déclarée ainsi: type* pointeur; + Le compilateur interprète l'expression pointeur + n comme suit: pointeur + n * sizeof(type) Arithmetique des pointeurs: p[1][2] + 3 == (*(p + 1))[2] + 3 == *(p[1] + 2) + 3 == *(*(p + 1) + 2) + 3
pointeurs sur fonctions: H Il est possible de déclarer un pointeur initialisé avec la valeur gauche (c'est à dire l'adresse) d'une fonction. On peut ensuite utilisé de pointeur pour appeler la fonction référencée: K int f(); K int (*fp)(); K int *g(); …… int f1(); int f2(); int (*fp)(); fp = &f1; (*fp)(); ……… void array_apply(double*, int, void (*)(double*)); void array_apply(double* a, int n, void (*f)(double*)) { int i; for(i=0; i<n; ++i) (*f)(a+i); } void triple(double *x) { *x *= 3; } void negate(double *x) { *x = - *x; }... double a[10] = {... }; array_apply(a,10,&triple); array_apply(a,10,&negate);