#include #include #include #include /* * Cette fonction ne fait rien du tout ! * Si on veut échanger le contenu des variables passées en paramètres, * il faut utiliser des pointeurs : void echange(int32_t *a, int32_t *b) */ void echange(int32_t a, int32_t b) { int32_t t; t = a; a = b; b = t; } /* * Cette fonction renvoie l'adresse d'une variable locale. * Or les variables locales "n'existent plus" (l'espace mémoire est désalloué) * une fois qu'on sort de la fonction. Valgrind n'est pas content que l'on * fasse ça, et nous dit qu'on utilise dans le main une case mémoire * non-initialisée (ou plus précisement : qui n'est plus initialisée car * désallouée). */ int32_t *fct1(void) { int32_t x = 5; int32_t *p = &x; return p; } /* * Cette fonction ne sert qu'à écraser la mémoire utilisée par la fonction * fct1 pour mettre en évidence le fait que la mémoire désallouée peut * être ré-utilisée par une autre fonction. */ void fct2(void) { int32_t tab[] = {1, 2, 3, 4}; (void)tab; } /* * La fonction malloc alloue (i.e. réserve) de la mémoire, mais elle ne * l'initialise pas ! En pratique, il est fort probable que la mémoire * contienne des 0 par défaut, mais ce n'est pas garanti. Valgrind n'aime * vraiment pas qu'on utilise des zones mémoire sans les avoir initialisées * avant. */ void fct3(void) { int32_t *ptr = malloc(sizeof(int32_t)); assert(ptr != NULL); printf("Valeur de l'entier alloue : %" PRId32 "\n", *ptr); free(ptr); } /* * Dans cette fonction, on déclare un tableau de 10 pointeurs vers des entiers. * On utilise ensuite malloc pour allouer 10 zones mémoire de 4 octets chacune. * Le problème, c'est qu'on ne libère jamais ces zones : Valgrind détecte * la fuite de mémoire en nous disant que ces zones sont "perdues" car en * sortant de la fonction, le tableau est désalloué donc les zones ne sont * plus accessibles. */ void fct4(void) { int32_t *tab[10]; for (int i = 0; i < 10; i++) { tab[i] = malloc(sizeof(int32_t)); assert(tab[i] != NULL); } } /* * Dans cette fonction, on crée ce qu'on appelle un "alias" : un pointeur qui * qui pointe sur la même zone qu'un autre pointeur. Puis on désalloue la * zone mémoire et on essaie d'y re-accéder : c'est faux, une fois la zone * désallouée par free, on n'a plus le droit d'y accéder (via ptr2 ou même * via ptr1 en l'occurrence : le fait d'avoir un alias ne change rien ici). */ void fct5(void) { int32_t *ptr1 = malloc(sizeof(int32_t)); int32_t *ptr2 = ptr1; free(ptr1); *ptr2 = 5; printf("Valeur de ptr2 : %" PRId32 "\n", *ptr2); } /* * On veut vraisemblablement allouer un tableau de 10 entiers 32 bits. * Mais en fait, on alloue 10 octets (alors qu'il en faudrait 40). * On voulait surement écrire : malloc(taille * sizeof(uint32_t)) */ void fct6(void) { const uint32_t taille = 10; int32_t *tab = malloc(taille); for (uint32_t i = 0; i < taille; i++) { tab[i] = 5 - i; } } /* * Ici, il ne s'agit pas d'une erreur en soit, mais on met en évidence : * - que dans le programme principal, sizeof(tab) renvoie bien 40 octets comme * on s'y attend ; * - mais que dans la fonction, sizeof(tab) renvoie 8 car le tableau est passé * par pointeur. */ void fct7(int32_t tab[]) { printf("Taille du tableau (en octets) : %" PRIu32 "\n", (uint32_t)sizeof(tab)); } int main(void) { int32_t a = 5; int32_t b = 7; printf("Valeurs initiales : a = %" PRId32 ", b = %" PRId32 "\n", a, b); echange(a, b); printf("Valeurs echangees : a = %" PRId32 ", b = %" PRId32 "\n", a, b); puts(""); int32_t *x = fct1(); printf("Valeur x = %" PRId32 "\n", *x); fct2(); printf("Valeur x = %" PRId32 "\n", *x); puts(""); fct3(); puts(""); fct4(); fct5(); puts(""); fct6(); int32_t tab[10]; printf("Taille du tableau (en octets) : %" PRIu32 "\n", (uint32_t)sizeof(tab)); fct7(tab); puts(""); return 0; }