Skip to content
Snippets Groups Projects
base_4.md 6.88 KiB
Newer Older
orestis.malaspin's avatar
orestis.malaspin committed
---
title: Base IV
orestis.malaspin's avatar
orestis.malaspin committed
date: 2020-10-07
orestis.malaspin's avatar
orestis.malaspin committed
---
orestis.malaspin's avatar
orestis.malaspin committed

# Les tableaux (1/6)

## Généralités

- `C` offre uniquement des tableaux statiques
    - Un tableau est un "bloc" de mémoire contiguë associé à un nom
        - 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/6)

## Exemple

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

int tab2[] = {1, 2, 3}; // tableau de 3 entiers, 
                        // taille inférée

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;
}
``` -->
# Les tableaux (3/6)

## Itérer sur les éléments d'un tableau

```C
int x[10];
for (int i = 0; i < 10; ++i) {
    x[i] = 0;
}
int j = 0;
while (j < 10) {
    x[j] = 1;
    j += 1;
}
int j = 0;
do {
    x[j] = -1;
    j += 1;
} while (j < 9)
```


# Les tableaux (4/6)

## Les tableaux comme argument

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

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

# Les tableaux (5/6)

## 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/6)

## 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;
}
```

<!-- TODO quiz:  -->

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

# Introduction à `make`
## A quoi ça sert?
- Automatiser le processus de conversion d'un type de fichier à un autre, en *gérant les dépendances*.
- Effectue la conversion des fichiers qui ont changé uniquement.
- Utilisé pour la compilation:
  - Création du code objet à partir des sources.
  - Création de l'exécutable à partir du code objet.
- Tout "gros" projet utilise `make` (pas uniquement en `C`).
# Utilisation de `make`
Le programme `make` exécutera la série d'instruction se trouvant dans un `Makefile` (ou `makefile` ou `GNUmakefile`).
## Le `Makefile`
- Contient une liste de *règles* et *dépendances*.
- Règles et dépendances construisent des *cibles*.
- Ici utilisé pour compiler une série de fichiers sources
    $ gcc -c example.c # + plein d'options..
    $ gcc -o example exemple.o # + plein d'options
:::::::::::::: {.columns}
::: {.column width="55%"}
## `Makefile`
```bash
example: example.o
    gcc -o example example.o
exmaple.o: exmaple.c example.h
    gcc -c example.c
```
:::
::: {.column width="45%"}
## Terminal
```bash
$ make
gcc -c example.c
gcc -o example example.o
```
:::
::::::::::::::
# Syntaxe d'un `Makefile` (1/4)
![Un exemple simple de `Makefile`.](figs/ex_makefile.svg){width=100%}
# Syntaxe d'un `Makefile` (2/4)
![La cible.](figs/ex_makefile_cible.svg){width=100%}
# Syntaxe d'un `Makefile` (3/4)
![Les dépendances.](figs/ex_makefile_dep.svg){width=100%}
# Syntaxe d'un `Makefile` (4/4)
![La règle.](figs/ex_makefile_regle.svg){width=100%}
# Principe de fonctionnement
1. `make` cherche le fichier `Makefile`, `makefile` ou `GNUmakefile` dans le répertoire courant.
2. Par défaut exécute la première cible, ou celle donnée en argument.
3. Décide si une cible doit être régénérée en comparant la date de modification (on recompile que ce qui a été modifié).
4. Regarde si les dépendances doivent être régénérées:
   - Oui: prend la première dépendance comme cible et recommence à 3.
   - Non: exécute la règle.
`make` a un comportement **récursif**.
# Exemple avancé
:::::::::::::: {.columns}
::: {.column width="55%"}
## `Makefile`
```bash
hello: hello.o main.o
    gcc hello.o main.o -o hello
hello.o: hello.c hello.h
    gcc -Wall -Wextra -c hello.c
main.o: main.c
    gcc -Wall -Wextra -c main.c
clean:
    rm -f *.o hello
rebuild: clean hello
```
:::
::: {.column width="45%"}
## Un graph complexe
![`Makefile` complexe.](figs/complex_makefile.svg){width=100%}
:::
::::::::::::::
# Factorisation
:::::::::::::: {.columns}
::: {.column width="55%"}
## Ancien `Makefile`
orestis.malaspin's avatar
orestis.malaspin committed

```bash
hello: hello.o main.o
    gcc hello.o main.o -o hello
hello.o: hello.c hello.h
    gcc -Wall -Wextra -c hello.c
main.o: main.c
    gcc -Wall -Wextra -c main.c
clean:
    rm -f *.o hello
rebuild: clean hello
orestis.malaspin's avatar
orestis.malaspin committed
```
:::
::: {.column width="45%"}

## Nouveau `Makefile`
```bash
CC=gcc -Wall -Wextra
hello: hello.o main.o
    $(CC) $^ -o $@
hello.o: hello.c hello.h
    $(CC) -c $<
main.o: main.c
    $(CC) -c $<
clean:
    rm -f *.o hello

rebuild: clean hello
orestis.malaspin's avatar
orestis.malaspin committed
::::::::::::::

# Variables
## Variables utilisateur
- Déclaration
 
orestis.malaspin's avatar
orestis.malaspin committed
    ```bash
    id = valeur
    id = valeur1 valeur2 valeur3
orestis.malaspin's avatar
orestis.malaspin committed
    ```
- Utilisation
 
orestis.malaspin's avatar
orestis.malaspin committed
    ```bash
orestis.malaspin's avatar
orestis.malaspin committed
    ```
- Déclaration à la ligne de commande
    ```bash
    make CFLAGS="-O3 -Wall"
## Variables internes
- `$@` : la cible
- `$^` : la liste des dépendances
- `$<` : la première dépendance
- `$*` : le nom de la cible sans extension