Skip to content
Snippets Groups Projects
Commit b9e36c7d authored by yassin.elhakoun's avatar yassin.elhakoun
Browse files

Merge branch cours:master into master

parents 12f12fe4 0a28287b
No related branches found
No related tags found
No related merge requests found
Pipeline #33269 failed
---
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);
}
```
---
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.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment