Skip to content
Snippets Groups Projects
base_2.md 10.2 KiB
Newer Older
orestis.malaspin's avatar
orestis.malaspin committed
% Base II
% Inspirés des slides de F. Glück
% 25 septembre 2019

orestis.malaspin's avatar
orestis.malaspin committed
# Représentation des variables en mémoire (1/N)

## La mémoire

* La mémoire est:
    - ... un ensemble de bits,
    - ... accessible via des adresses,
orestis.malaspin's avatar
orestis.malaspin committed

  +------+----------+----------+------+----------+------+------+
  | bits | 00110101 | 10010000 | .... | 00110011 | .... | .... |
  +======+==========+==========+======+==========+======+======+
  | addr | 2000     | 2001     | .... | 4000     | .... | .... |
  +------+----------+----------+------+----------+------+------+
orestis.malaspin's avatar
orestis.malaspin committed

    - ... gérée par le système d'exploitation.
    - ... séparée en deux parties: **la pile** et **le tas**.
orestis.malaspin's avatar
orestis.malaspin committed

## Une variable
* Une variable, `type a = valeur`{.C}, possède:
orestis.malaspin's avatar
orestis.malaspin committed
    - un type (`char`{.C}, `int`{.C}, ...),
    - un contenu (une séquence de bits qui encode `valeur`{.C}),
orestis.malaspin's avatar
orestis.malaspin committed
    - une adresse mémoire (accessible via `&a`{.C}),
    - une portée.

orestis.malaspin's avatar
orestis.malaspin committed

# Représentation des variables en mémoire (2/N)
orestis.malaspin's avatar
orestis.malaspin committed
![Les variables en mémoire.](figs/memory.svg){#fig:memory width=100%}
orestis.malaspin's avatar
orestis.malaspin committed
# Les pointeurs (1/N)

orestis.malaspin's avatar
orestis.malaspin committed
- Un pointeur est une adresse mémoire.
orestis.malaspin's avatar
orestis.malaspin committed

    ```C
    type *id;
    ```
orestis.malaspin's avatar
orestis.malaspin committed
- Pour interprêter le contenu de ce qu'il pointe, il doit être typé.
orestis.malaspin's avatar
orestis.malaspin committed
- Un pointeur n'est rien d'autre qu'un entier (64bit sur x86-64, soit 8 octets).
orestis.malaspin's avatar
orestis.malaspin committed
- Un pointeur peut être **déréférencé**: on accède à la valeur située à l'adresse mémoire sur laquelle il pointe.
orestis.malaspin's avatar
orestis.malaspin committed
    char *c; // déclaration pointeur de char
orestis.malaspin's avatar
orestis.malaspin committed
    *c = 'a'; // assign. 'a' à valeur pointée par c
orestis.malaspin's avatar
orestis.malaspin committed
    c = 1000; // on modifie l'adresse pointée par c
orestis.malaspin's avatar
orestis.malaspin committed
    char d = *c; // on lit la valeur pointée par c. UB!
orestis.malaspin's avatar
orestis.malaspin committed
- `NULL`{.C} (ou `0`{.C}) est la seule adresse **toujours** invalide.

orestis.malaspin's avatar
orestis.malaspin committed
# Les pointeurs (2/N)

![Les pointeurs, le déréférencement, et la mémoire.](figs/memory_deref.svg){#fig:memory width=100%}

orestis.malaspin's avatar
orestis.malaspin committed
# Les pointeurs (3/N)

- Permettent d'accéder à une valeur avec une indirection.

    ```C
    int a = 2;
    int *b = &a;
    *b = 7; // on met 7 dans la case pointée par b
            // ici  a == 7 aussi
    a = -2; // ici *b == -2 aussi
    ```

- Permettent d'avoir plusieurs chemins d'accès à une valeur.
- Lire **et** écrire en même temps dans un bout de mémoire devient possible: **danger**.

orestis.malaspin's avatar
orestis.malaspin committed
# Quiz: Les pointeurs

orestis.malaspin's avatar
orestis.malaspin committed
## [Quiz: Les pointeurs](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1038526)
orestis.malaspin's avatar
orestis.malaspin committed

orestis.malaspin's avatar
orestis.malaspin committed
# La fonction `sizeof()` (1/N)

- La fonction `sizeof()`{.C} permet de connaître la taille en octets:
    - d'une valeur,
    - d'un type,
    - d'une variable.
- Soit `int a = 2`{.C}, sur l'architecture x86_64 que vaut:
    - `sizeof(a)`{.C}?
    - `sizeof(&a)`{.C}?
- Soit `char b = 2`{.C},
    - `sizeof(b)`{.C}?
    - `sizeof(&b)`{.C}?

# La fonction `sizeof()` (2/N)

- Réponses:
    - `sizeof(a) == 4`{.C}, `int`{.C} entier 32 bits.
    - `sizeof(&a) == 8`{.C}, une adresse est de 64 bits.
    - `sizeof(b) == 1`{.C}, `char`{.C} entier 8 bits.
    - `sizeof(&b) == 8`{.C}, une adresse est de 64 bits.

# Les fonctions (1/N)

orestis.malaspin's avatar
orestis.malaspin committed
- Les parties indépendantes d'un programme.
- Permettent de modulariser et compartimenter le code.
orestis.malaspin's avatar
orestis.malaspin committed
    type identificateur(paramètres) { 
        // variables optionnelles
        instructions;
orestis.malaspin's avatar
orestis.malaspin committed
        // type expression == type
        return expression; 
orestis.malaspin's avatar
orestis.malaspin committed

# Les fonctions (2/N)

## Exemple
```C
int max(int a, int b) {
    if (a > b) {
        return a;
    } else {
        return b;
int main() {
    int c = max(4, 5);
}
orestis.malaspin's avatar
orestis.malaspin committed
```
orestis.malaspin's avatar
orestis.malaspin committed
# Les fonctions (3/N)

- Il existe un type `void`{.C}, "sans type", en C.
- Il peut être utilisé pour signifier qu'une fonction ne retourne rien, ou qu'elle n'a pas d'arguments.
- `return`{.C} utilisé pour sortir de la fonction.
- Exemple:

    ```C
    void show_text(void) { // second void optionnel
        printf("Aucun argument et pas de retour.\n");
        return; // optionnel
    }

    void show_text_again() { // c'est pareil
        printf("Aucun argument et pas de retour.\n");
    }
    ```

orestis.malaspin's avatar
orestis.malaspin committed
# Les fonctions (4/N)

## Prototypes de fonctions

- Le prototype donne la **signature** de la fonction, avant qu'on connaisse son implémentation.
- L'appel d'une fonction doit être fait **après** la déclaration du prototype.

    ```C
    int max(int a, int b); // prototype

    int max(int a, int b) { // implémentation
        if (a > b) {
            return a;
        } else {
            return b;
        }
    }
    ```

orestis.malaspin's avatar
orestis.malaspin committed
# Les fonctions (5/N)
orestis.malaspin's avatar
orestis.malaspin committed

## Arguments de fonctions

- Les arguments d'une fonction sont toujours passés **par copie**.
- Les arguments d'une fonction ne peuvent **jamais** être modifiés.

    ```C
orestis.malaspin's avatar
orestis.malaspin committed
    void set_to_two(int a) { // a: nouvelle variable
        // valeur de a est une copie de x
orestis.malaspin's avatar
orestis.malaspin committed
        // lorsque la fonction est appelée, ici -1

        a = 2; // la valeur de a est fixée à 2
    } // a est détruite

    int main() {
        int x = -1;
        set_to_two(x); // -1 est passé en argument
        // x vaudra toujours -1 ici
    }
    ```

orestis.malaspin's avatar
orestis.malaspin committed
# Les fonctions (6/N)
orestis.malaspin's avatar
orestis.malaspin committed

## Arguments de fonctions: pointeurs

- Pour modifier un variable, il faut passer son **adresse mémoire**.
- L'adresse d'une variable, `x`{.C}, est accédé par `&x`{.C}.
- Un **pointeur** vers une variable entière a le type, `int *x`{.C}.
- La sytaxe `*x`{.C} sert à **déréférencer** le pointeur (à accéder à la mémoire pointée).
orestis.malaspin's avatar
orestis.malaspin committed

# Les fonctions (7/N)
orestis.malaspin's avatar
orestis.malaspin committed

## Exemple

```C
void set_to_two(int *a) { 
    // a contient une copie de l'adresse de la
    // variable passée en argument

    *a = 2; // on accède à la valeur pointée par a,
            // et on lui assigne 2
} // le pointeur est détruit, pas la valeur pointée
int main() {
    int x = -1;
    set_to_two(&x); // l'adresse de x est passée
    // x vaudra 2 ici
}
```
orestis.malaspin's avatar
orestis.malaspin committed

orestis.malaspin's avatar
orestis.malaspin committed
# Quiz: Les fonctions

orestis.malaspin's avatar
orestis.malaspin committed
## [Quiz: Les fonctions](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1038560)
orestis.malaspin's avatar
orestis.malaspin committed

orestis.malaspin's avatar
orestis.malaspin committed
<!-- TODO quiz;
```C
void set_to_two(int *a) { 
    a = 2;
}
int main() {
    int x = -1;
    set_to_two(&x);
}


void add_two(int *a) { 
    *a += 2;
}

int main() {
    int x = -1;
    add_two(&x);
}

void add_two(int a) { 
    a += 2;
    printf("%d", a);
}
int main() {
    int x = -1;
    add_two(&x);
}
``` -->

# La fonction `main()` (1/N)
## Généralités

- Point d'entrée du programme.
- Retourne le code d'erreur du programme:
    - 0: tout s'est bien passé.
    - Pas zéro: problème.
- La valeur de retour peut être lue par le shell qui a exécuté le programme.
- `EXIT_SUCCESS`{.C} et `EXIT_FAILURE`{.C} (de `stdlib.h`) sont des valeurs de retour **portables** de programmes C. 

# La fonction `main()` (2/N)

## Exemple

```C
int main() {
    // ...
    if (error)
	    return EXIT_FAILURE;
    else
	    return EXIT_SUCCESS;
}
```

- Le code d'erreur est lu dans le shell avec `$?`{.bash}

```bash
$ ./prog
$ echo $?
0 # tout s'est bien passé par exemple
$ if [ $? -eq 0 ]; then echo "OK" ; else echo "ERROR"; fi
ERROR # si tout s'est mal passé
```
orestis.malaspin's avatar
orestis.malaspin committed

# Les tableaux (1/N)

## Généralités

orestis.malaspin's avatar
orestis.malaspin committed
- `C` offre uniquement des tableaux statiques
    - Un tableau est un "bloc" de mémoire contiguë associé à un nom
orestis.malaspin's avatar
orestis.malaspin committed
        - taille fixe déterminée à la déclaration du tableau
        - la taille ne peut pas être changée.
    - Pas d’assignation de tableaux.
    - Un tableau déclaré dans une fonction ou un bloc est détruit à la sortie de celle/celui-ci
        - $\Rightarrow$ Un tableau local à une fonction ne doit **jamais être retourné** (aussi valable pour toute variable locale)!
- Les éléments d’un tableau sont accédés avec `[i]`{.C} où `i`{.C} est l’index de l’élément.
- Le premier élément du tableau à l’index `0`{.C}!
- Lorsqu’un tableau est déclaré, la taille de celui-ci doit toujours être spécifiée, sauf s’il est initialisé lors de sa déclaration.

# Les tableaux (2/N)

## Exemple

```C
float tab1[5]; // tableau de floats à 5 éléments
               // ses valeurs sont indéfinies

orestis.malaspin's avatar
orestis.malaspin committed
int tab2[] = {1, 2, 3}; // tableau de 3 entiers, 
                        // taille inférée
orestis.malaspin's avatar
orestis.malaspin committed

int val = tab2[1]; // val vaut 2 à présent

int w = tab1[5]; // index hors des limites du tableau
                 // comportement indéfini!
                 // pas d'erreur du compilateur
```

<!-- TODO QUIZ:

```C
int a1[5]; // OK
int a2[] = { 1, 2, 3 }; // OK
int a3[4][5]; // OK
int [] a4;  // Erreur
int a5[];   // Erreur

int[] function(void) {  // Erreur
	int array[5];   // OK
	return array;   // Erreur
}

void foo(int a[]) {  // OK
	a[3] = 0;  // OK
}

void bar(void) {
	int a[5]; // OK
	foo(a);   // OK
	a = a5;   // Erreur
}
``` -->

<!-- ```C
#include <stdio.h>

int main(void) {
	char i;
	char a1[] = { 100,200,300,400,500 };
	char a2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	a2[10] = 42;

	for (i = 0; i < 5; i++) {
		printf("a1[%d] = %d\n", i, a1[i]);
	}

	return 0;
}
``` -->
orestis.malaspin's avatar
orestis.malaspin committed
# Les tableaux (3/N)
orestis.malaspin's avatar
orestis.malaspin committed

orestis.malaspin's avatar
orestis.malaspin committed
## Itérer sur les éléments d'un tableau
orestis.malaspin's avatar
orestis.malaspin committed

orestis.malaspin's avatar
orestis.malaspin committed
```C
int x[10];
for (int i = 0; 0 < 10; ++i) {
    x[i] = 0;
}
int j = 0;
while (j < 10) {
    x[j] = 1;
    j += 1;
}
orestis.malaspin's avatar
orestis.malaspin committed
int j = 0;
do {
    x[j] = -1;
    j += 1;
} while (j < 9)
orestis.malaspin's avatar
orestis.malaspin committed
```


# Les tableaux (4/N)
orestis.malaspin's avatar
orestis.malaspin committed

## Les tableaux comme argument

- Un tableau est le pointeur vers sa première case.
orestis.malaspin's avatar
orestis.malaspin committed
- Pas moyen de connaître sa taille: `sizeof()`{.C} inutile.
- Toujours spécifier la taille d'un tableau passé en argument.
orestis.malaspin's avatar
orestis.malaspin committed

    ```C
    void foo(int tab[]) { // sans taille...
orestis.malaspin's avatar
orestis.malaspin committed
        for (int i = 0; i < ?; ++i) {
            // on sait pas quoi mettre pour ?
orestis.malaspin's avatar
orestis.malaspin committed
            printf("tab[%d] = %d\n", i, tab[i]);
        }
    }
orestis.malaspin's avatar
orestis.malaspin committed
    // n doit venir avant tab, [n] opionel
    void bar(int n, int tab[n]) {
orestis.malaspin's avatar
orestis.malaspin committed
        for (int i = 0; i < n; ++i) {
            printf("tab[%d] = %d\n", i, tab[i]);
        }
    }
    ```

orestis.malaspin's avatar
orestis.malaspin committed
# Les tableaux (5/N)

## Quels sont les bugs dans ce code?

```C
#include <stdio.h>

int main(void) {
	char i;
	char a1[] = { 100,200,300,400,500 };
	char a2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	a2[10] = 42;

	for (i = 0; i < 5; i++) {
		printf("a1[%d] = %d\n", i, a1[i]);
	}

	return 0;
}
```

# Les tableaux (6/N)

## Quels sont les bugs dans ce code?

```C
#include <stdio.h>

int main(void) {
	char i;
    // 200, .., 500 char overflow
	char a1[] = { 100,200,300,400,500 };
	char a2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	a2[10] = 42; // [10] out of bounds

	for (i = 0; i < 5; i++) {
		printf("a1[%d] = %d\n", i, a1[i]);
	}

	return 0;
}
```

orestis.malaspin's avatar
orestis.malaspin committed
<!-- TODO quiz:  -->

<!-- que retourne sizeof(tab[]) -->