diff --git a/slides/genericite.md b/slides/genericite.md new file mode 100644 index 0000000000000000000000000000000000000000..448f3cb47be67de05abbf01941e032603ef2603a --- /dev/null +++ b/slides/genericite.md @@ -0,0 +1,180 @@ +--- +title: "La généricité" +date: "2024-03-25" +--- + +# Problématique + +* En C on doit écrire chaque algorithme/structures de données pour des types + précis (`int`, `double`, `char`, ...). + + ``` + void int_sort(int size, int tab[size]); // tri d'entiers + void double_sort(int size, int tab[size]); // tri de double + void char_sort(int size, char tab[size]); // tri de char + ``` +* Duplication du code pour chaque type possible et imaginable. +* On aimerait un moyen pour pouvoir représenter "n'importe quel type" sans + réécrire tout le code. + +# La généricité + +## Une "solution": `void *`{.C} + +* En général, un pointeur connaît son **adresse** et le **type** des données sur lesquelles il pointe. + + ```C + int *a = malloc(sizeof(*a)); + int *b = malloc(sizeof(int)); + ``` +* Un `void *`{.C} le connaît **que** son adresse, au programmeur de pas faire n'importe quoi. +* Vous avez déjà utilisé des fonctions utilisant des `void *`{.C} + + ```C + void *malloc(size_t size); + void free(void *); + ``` + +# Attention danger + +* Ne permet pas au compilateur de vérifier les types. +* Les données pointées n'ayant pas de type, il faut déréférencer avec précaution: + + ```C + int a = 2; + void *b = &a; //jusqu'ici tout va bien + double c = *b; // argl! + ``` +* Une attention accrue est nécessaire. + +# Cas particuier: on sait pas comment libérer la mémoire + +## Exemple + +```C +struct tab { + int *t; +} +struct tab *tmp = malloc(sizeof(*tmp)); +tmp->t = malloc(10 * sizeof(*(tmp->t))); +free(tmp); // memory leak of tmp->t... +``` + +. . . + +## Solution: tout faire à la main + +```C +free(tmp->t); +free(tmp); +``` + +# Exemple simple + +* On souhaite échanger deux pointeurs + + ```C + int *a = malloc(); + int *b = malloc(); + swap(&a, &b); + ``` +* Comment écrire `swap()` pour que le code ci-dessus marche pour n'importe quel + type? + +. . . + +```C +void swap(void **a, void **b) { + void *tmp = *a; + *a = *b; + *b = tmp; +} +``` + + + +# Cas d'utilisation (1/4) + +\footnotesize + +* La somme d'un tableau de type arbitraire (facile non?) + + ```C + void sum(void *tab, int length, size_t size_elem, void *zero, + void (*add)(void *, void *)) { + for (int i = 0; i < length; ++i) { + void *rhs = (void *)((char *)tab + i * size_elem); + add(zero, rhs); + } // de combien on "saute" avec un void *? + } + ``` +* Pour des entiers + + ```C + void int_add(void *lhs, void *rhs) { + *((int *)lhs) += *((int *)rhs); // cast d'entiers + } + int zero = 0; + int tab[] = {1, -2, 4, 5}; + sum(tab, 4, sizeof(int), &zero, int_add); + printf("%d\n", zero); + ``` + +# Cas d'utilisation (2/4) + +## Que fait cette fonction? + +\footnotesize + +```C +void *foo(void *tab, int n_items, int s_items, + bool (*bar)(void *, void *)) { + if (n_items <= 0 || s_items <= 0 || NULL == tab) { + return NULL; + } + void *elem = tab; + for (int i = 1; i < n_items; ++i) { + // void pointer arithmetics is illegal in C + // (gcc is ok though) + void *tmp_elem = (void *)((char *)tab + i*s_items); + + if (bar(elem, tmp_elem)) { + elem = tmp_elem; + } + } + return elem; +} +``` + +# Cas d'utilisation (3/4) + +## Avec un tableau de `int`{.C} + +```C +bool cmp_int(void *a, void *b) { + return (*(int *)a < *(int *)b); +} + +int main() { + int tab[] = {-1, 2, 10, 3, 8}; + int *a = foo(tab, 5, sizeof(int), cmp_int); + printf("a = %d\n", *a); +} +``` + +# Cas d'utilisation (4/4) + +## Avec un tableau de `double`{.C} + +```C +bool cmp_dbl(void *a, void *b) { + return (*(double *)a < *(double *)b); +} + +int main() { + double tab[] = {-1.2, 2.1, 10.5, 3.6, 18.1}; + double *a = foo(tab, 5, sizeof(double), cmp_dbl); + printf("a = %f\n", *a); +} +``` + diff --git a/slides/pointeurs_avances.md b/slides/pointeurs_avances.md new file mode 100644 index 0000000000000000000000000000000000000000..3cfd92a6a506bf22523e78f92ff0ef4a60275093 --- /dev/null +++ b/slides/pointeurs_avances.md @@ -0,0 +1,92 @@ +--- +title: "Pointeurs avancés" +date: "2024-03-24" +--- + +# Pointeurs et `const` + +\footnotesize + +- Le mot-clé `const` permet de déclarer des valeurs **constantes** qui ne changeront plus en cours d'exécution du programme. + + ```C + const int a = 1; + a = 2; // interdit, erreur de compilation! + ``` + +## Deux niveaux de constance + +- Mais qu'est-ce que cela veut dire pour les pointeurs? +* Constance de la valeur de l'adresse? de la valeur pointée? des deux? + + ```C + int n = 12; + const int *p = &n; // la valeur *p est const, p non + int const *p = &n; // la valeur *p est const, p non + int *const p = &n; // la valeur p est const, *p non + const int *const p = &n; // la valeur p et *p sont const + ``` + +# Pointeurs et `const` + +## Exemples + +```C +int n = 12; int m = 13; +const int *p = &n; // la valeur *p est const, p non +*p = m; // erreur de compilation. +p = &m; // OK +int const *p = &n; // la valeur *p est const, p non +*p = m; // erreur de compilation. +p = &m; // OK +int *const p = &n; // la valeur p est const, *p non +*p = m; // OK +p = &m; // erreur de compilation. +const int *const p = &n; // la valeur p et *p sont const +*p = m; // erreur de compilation. +p = &m; // erreur de compilation. +``` + +# Rappel: pointeurs et fonction + +## Faites un dessin de ce qui se passe en mémoire + +```C +void foo(int *a) { + *a = 3; +} +void bar(int a) { + a = 12; +} +int main() { + int a = 1; + foo(&a); // Que vaut a? + bar(a); // Que vaut a? +} +``` + +# Pointeurs et `const` + +## Fonctions + +```C +void foo(int *a); +void foo(const int *a); // on pourra pas changer *a +void foo(int *const a); // inutile on peut pas changer a +void foo(const int *const a); // identique à ci-dessus +``` + +## Mais..... + +```C +const int a = 0; +int *b = (int *)&a; +*b = 7; +printf("a = %d\n", a); // affiche quoi? +``` + +# Utilité + +* Permet d'empêcher une mauvaise utilisation des arguments, +* Permet de documenter le code: on sait que la variable ne sera pas modifiée. +