From bb1a224014059d3fe7a6739b4f44b5f6dc2c223d Mon Sep 17 00:00:00 2001
From: Orestis <orestis.malaspinas@hesge.ch>
Date: Mon, 5 Oct 2020 22:48:40 +0200
Subject: [PATCH] base 4 is now arrays and make

---
 base_4.md | 507 +++++++++++++-----------------------------------------
 1 file changed, 118 insertions(+), 389 deletions(-)

diff --git a/base_4.md b/base_4.md
index 0bef38e..1c59509 100644
--- a/base_4.md
+++ b/base_4.md
@@ -1,6 +1,6 @@
-% Base III
+% Base IV
 % Inspirés des slides de F. Glück
-% 2 octobre 2019
+% 7 octobre 2020
 
 # Les tableaux (1/6)
 
@@ -168,461 +168,190 @@ int main(void) {
 
 <!-- que retourne sizeof(tab[]) -->
 
-# Représentation des variables en mémoire (1/N)
+# Introduction à `make`
 
-## La mémoire
+## A quoi ça sert?
 
-* La mémoire est:
-    - ... un ensemble de bits,
-    - ... accessible via des adresses,
+- 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`).
 
-  +------+----------+----------+------+----------+------+------+
-  | bits | 00110101 | 10010000 | .... | 00110011 | .... | .... |
-  +======+==========+==========+======+==========+======+======+
-  | addr | 2000     | 2001     | .... | 4000     | .... | .... |
-  +------+----------+----------+------+----------+------+------+
+# Utilisation de `make`
 
-    - ... gérée par le système d'exploitation.
-    - ... séparée en deux parties: **la pile** et **le tas**.
+Le programme `make` exécutera la série d'instruction se trouvant dans un `Makefile` (ou `makefile` ou `GNUmakefile`).
 
-## Une variable
+## Le `Makefile`
 
-* Une variable, `type a = valeur`{.C}, possède:
-    - un type (`char`{.C}, `int`{.C}, ...),
-    - un contenu (une séquence de bits qui encode `valeur`{.C}),
-    - une adresse mémoire (accessible via `&a`{.C}),
-    - une portée.
+- 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
 
-
-# Représentation des variables en mémoire (2/N)
-
-![Les variables en mémoire.](figs/memory.svg){#fig:memory width=100%}
-
-# Les pointeurs (1/N)
-
-- Un pointeur est une adresse mémoire.
-
-    ```C
-    type *id;
-    ```
-- Pour interpréter le contenu de ce qu'il pointe, il doit être typé.
-- Un pointeur n'est rien d'autre qu'un entier (64bit sur x86-64, soit 8 octets).
-- Un pointeur peut être **déréférencé**: on accède à la valeur située à l'adresse mémoire sur laquelle il pointe.
-
-    ```C
-    char *c; // déclaration pointeur de char
-    *c = 'a'; // assign. 'a' à valeur pointée par c
-    c = 1000; // on modifie l'adresse pointée par c
-    char d = *c; // on lit la valeur pointée par c. UB!
-    ```
-
-- `NULL`{.C} (ou `0`{.C}) est la seule adresse **toujours** invalide.
-
-# Les pointeurs (2/N)
-
-![Les pointeurs, le déréférencement, et la mémoire.](figs/memory_deref.svg){#fig:memory width=100%}
-
-# 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**.
-
-# Quiz: Les pointeurs
-
-## [Quiz: Les pointeurs](https://cyberlearn.hes-so.ch/mod/evoting/view.php?id=1038526)
-
-# 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.
-
-# Types complexes: `struct`{.C} (1/N)
-
-## Généralités
-
-- Plusieurs variables qu'on aimerait regrouper dans un seul type: `struct`{.C}.
-
-    ```C
-    struct complex { // déclaration
-        double re;
-        double im;
-    };
-
-    struct complex num; // déclaration de num
-    ```
-- Les champs sont accessible avec le sélecteur "`.`{.C}".
-
-    ```C
-    num.re = 1.0;
-    num.im = -2.0;
-    ```
-
-# Types complexes: `struct`{.C} (2/N)
-
-## Simplifications
-
-- `typedef`{.C} permet de définir un nouveau type.
-
-    ```C
-    typedef unsinged int uint;
-    typedef struct complex complex_t;
-    typedef struct complex {
-        double re, im;
-    } complex_t;
-    ```
-- L'initialisation peut aussi se faire avec
-
-    ```C
-    complex_t num = {1.0, -2.0}; // re = 1.0, im = -2.0
-    complex_t num = {.im = 1.0, .re = -2.0};
-    complex_t num = {.im = 1.0}; // argl! .re non initialisé 
-    complex_t num2 = num; // copie
-    ```
-
-# Types complexes: `struct`{.C} (3/N)
-
-## Pointeurs
-
-- Comme pour tout type, on peut avoir des pointeurs vers un `struct`{.C}.
-- Les champs sont accessible avec le sélecteur `->`{.C}
-
-    ```C
-    complex_t *num; // on crée un pointeur
-    num->re = 1.0;  // seg fault...
-    num->im = -1.0; // mémoire pas allouée.
-    ```
-
-![La représentation mémoire de `complex_t`.](figs/pointer_struct.svg){#fig:compilation width=100%}
-
-
-# Allocation dynamique de mémoire (1/N)
-
-- La fonction `malloc`{.C} permet d'allouer dynamiquement (pendant l'exécution du programme) une zone de mémoire contiguë.
-
-    ```C
-    #include <stdlib.h>
-    void *malloc(size_t size);
-    ```
-- `size`{.C} est la taille de la zone mémoire **en octets**.
-- Retourne un pointeur sur la zone mémoire ou `NULL`{.C} en cas d'échec: **toujours vérifier** que la valeur retournée est `!= NULL`{.C}.
-
-# Allocation dynamique de mémoire (2/N)
-
-- Avec l'exemple de tout à l'heure:
-
-    ```C
-    complex_t *num = malloc(sizeof(complex_t));
-    num->re = 1.0;  // maintenant ...
-    num->im = -1.0; // ça marche.
-    ```
-- La zone mémoire **n'est pas** initialisée.
-- La mémoire doit être désallouée explicitement $\Rightarrow$ **fuites mémoires**.
-<!-- - Toujours garder un pointeur sur la mémoire allouée sinon **pointeur pendouillant**. -->
-
-![La représentation mémoire de `complex_t` et fuites.](figs/pointer_struct_ok.svg){#fig:compilation width=100%}
-
-# Allocation dynamique de mémoire (3/N)
-
-- La fonction `free()`{.C} permet de libérer une zone préalablement allouée avec `malloc()`{.C}.
-
-    ```C
-    #include <stdlib.h>
-    void free(void *ptr);
-    ```
-- Pour chaque `malloc()`{.C} doit correspondre exactement un `free()`{.C}.
-- Si la mémoire n'est pas libérée: **fuite mémoire** (l'ordinateur plante quand il y a plus de mémoire).
-- Si la mémoire est **libérée deux** fois: seg fault.
-- Pour éviter les mauvaises surprises mettre `ptr`{.C} à `NULL`{.C}.
-
-# Allocation dynamique de mémoire (4/N)
-
-## Tableaux dynamiques
-
-- Pour allouer un espace mémoire de 50 entiers:
-
-    ```C
-    int *p = malloc(50 * sizeof(int));
-    ```
-- Cette espace peut alors être utilisé comme un tableau de 50 entiers:
-
-    ```C
-    for (int i = 0; i < 50; ++i) {
-        p[i] = 0;
-    }
     ```
-
-## Arithmétique de pointeurs
-
-- On peut parcourir la mémoire différemment qu'avec l'indexation
-    
-    ```C
-    int *p = malloc(50 * sizeof(int));
-    // initialize somehow
-    double a = p[7];
-    double b = *(p + 7); // on avance de 7 "double"
-    p[0] == *p; // le pointeur est le premier élément
+    $ gcc -c example.c # + plein d'options..
+    $ gcc -o example exemple.o # + plein d'options
     ```
 
-# Allocation dynamique de mémoire (5/N)
-
-## Arithmétique de pointeurs
+:::::::::::::: {.columns}
 
-![L'arithmétique des pointeurs.](figs/pointer_arithmetics.svg){#fig:compilation width=100%}
+::: {.column width="55%"}
 
-# Allocation dynamique de mémoire (6/N)
+## `Makefile`
 
-## Pointeur de pointeur
+```bash
+example: example.o
+    gcc -o example example.o
 
-- Tout comme une valeur a une adresse, un pointeur a lui-même une adresse:
+exmaple.o: exmaple.c example.h
+    gcc -c example.c
+```
+:::
+::: {.column width="45%"}
 
-    ```C
-    int a = 2;
-    int *b = &a;
-    int **c = &b;
-    ```
-- Chaque `*`{.C} ou `&`{.C} rajoute une indirection.
+## Terminal
 
-# Allocation dynamique de mémoire (7/N)
+```bash
+$ make
+gcc -c example.c
+gcc -o example example.o
+```
+:::
+::::::::::::::
 
-## Pointeur de pointeur
+# Syntaxe d'un `Makefile` (1/4)
 
+![Un exemple simple de `Makefile`.](figs/ex_makefile.svg){#fig:ex_makefile width=100%}
 
-![L'arithmétique des pointeurs.](figs/double_pointeur.svg){#fig:compilation height=100%}
+# Syntaxe d'un `Makefile` (2/4)
 
-# Allocation dynamique de mémoire (8/N)
+![La cible.](figs/ex_makefile_cible.svg){#fig:ex_makefile_cible width=100%}
 
-- Avec `malloc()`, on peut allouer dynamiquement des tableaux de pointeurs:
+# Syntaxe d'un `Makefile` (3/4)
 
-    ```C
-    int **p = malloc(50 * sizeof(int*));
-    for (int i = 0; i < 50; ++i) {
-        p[i] = malloc(70 * sizeof(int));
-    }
-    int a = p[5][8]; // on indexe dans chaque dimension
-    ```
+![Les dépendances.](figs/ex_makefile_dep.svg){#fig:ex_makefile_dep width=100%}
 
-- Ceci est une matrice (un tableau de tableau).
+# Syntaxe d'un `Makefile` (4/4)
 
-# Prototypes de fonctions (1/N)
+![La règle.](figs/ex_makefile_regle.svg){#fig:ex_makefile_regle width=100%}
 
-## Principes généraux de programmation
+# Principe de fonctionnement
 
-- Beaucoup de fonctionnalités dans un code $\Rightarrow$ Modularisation.
-- Modularisation du code $\Rightarrow$ écriture de fonctions.
-- Beaucoup de fonctions $\Rightarrow$ regrouper les fonctions dans des fichiers séparés.
+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.
 
-## Mais pourquoi?
+`make` a un comportement **récursif**.
 
-- Lisibilité.
-- Raisonnement sur le code.
-- Débogage.
+# Exemple avancé
 
-## Exemple
+:::::::::::::: {.columns}
 
-- Libraire `stdio.h`: `printf()`{.C}, `scanf()`{.C}, ...
+::: {.column width="55%"}
 
-# Prototypes de fonctions (2/N)
+## `Makefile`
 
-- Prototypes de fonctions nécessaires quand:
+```bash
+hello: hello.o main.o
+    gcc hello.o main.o -o hello
 
-    1. Utilisation de fonctions dans des fichiers séparés.
-    2. Utilisation de librairies.
-- Un prototype indique au compilateur la signature d'une fonction.
-- On met les prototypes des fonctions **publiques** dans des fichiers *headers*, extension `.h`.
-- Les *implémentations* des fonctions vont dans des fichier `.c`.
+hello.o: hello.c hello.h
+    gcc -Wall -Wextra -c hello.c
 
-# Prototypes de fonctions (3/N)
+main.o: main.c
+    gcc -Wall -Wextra -c main.c
 
-## Fichier header
+clean:
+    rm -f *.o hello
 
-- Porte l'extension `.h`
-- Contient: 
-    - définitions des types
-    - prototypes de fonctions
-    - macros
-    - directives préprocesseur (cf. plus loin)
-- Utilisé pour décrire **l'interface** d'une librairie ou d'un module.
-- Un fichier `C` (extension `.c`) utilise un header en *l'important* avec la directive `#include`{.C}:
+rebuild: clean hello
+```
+:::
+::: {.column width="45%"}
 
-    ```C
-    #include <stdio.h> // libraire dans LD_LIBRARY_PATH
-    #include "chemin/du/prototypes.h"// chemin explicite
-    ```
+## Un graph complexe
 
-# Génération d'un exécutable (1/N)
+![`Makefile` complexe.](figs/complex_makefile.svg){#fig:complex_makefile width=100%}
 
-## Un seul fichier source
+:::
+::::::::::::::
 
-![Étapes de génération.](figs/compilation.svg){#fig:compilation width=100%}
+# Factorisation
 
-# Génération d'un exécutable (2/N)
+:::::::::::::: {.columns}
 
-## Un seul fichier source
+::: {.column width="55%"}
+## Ancien `Makefile`
 
 ```bash
-gcc proc.c -o prog
-```
-
-1. **Précompilation: ** `gcc` appelle `cpp`, le préprocesseur qui effectue de la substitution de texte (`#define`, `#include`, macros, ...) et génère le code `C` à compiler, portant l'extension `.i` (`prog.i`).
-2. **Compilation assembleur: ** `gcc` compile le code C en code assembleur, portant l'extension `.s` (`prog.s`).
-3. **Compilation code objet: ** `gcc` appelle `as`, l'assembleur, qui compile le code assembleur en code machine (code objet) portant l'extension `.o` (`prog.o`).
-4. **Édition des liens: ** `gcc` appelle `ld`, l'éditeur de liens, qui lie le code objet avec les librairies et d'autres codes objet pour produire l'exécutable final (`prog`).
-
-Les différents codes intermédiaires sont effacés.
-
-# Génération d'un exécutable (3/N)
-
-## Plusieurs fichiers sources
+hello: hello.o main.o
+    gcc hello.o main.o -o hello
 
-![Étapes de génération, plusieurs fichiers.](figs/compilation_plusieurs.svg){#fig:compilation_plusieurs width=100%}
+hello.o: hello.c hello.h
+    gcc -Wall -Wextra -c hello.c
 
-# Génération d'un exécutable (4/N)
+main.o: main.c
+    gcc -Wall -Wextra -c main.c
 
-::: Main
+clean:
+    rm -f *.o hello
 
-## `main.c`
-
-```C
-#include <stdio.h>
-#include "sum.h"
-int main() {
-  int tab[] = {1, 2, 3, 4};
-  printf("sum: %d\n", sum(tab, 4));
-  return 0;
-}
+rebuild: clean hello
 ```
 :::
-
-:::::::::::::: {.columns}
-
 ::: {.column width="45%"}
 
-## `sum.h`
+## Nouveau `Makefile`
 
-```C
-#ifndef _SUM_H_
-#define _SUM_H_
+```bash
+CC=gcc -Wall -Wextra
 
-int sum(int tab[], int n);
+hello: hello.o main.o
+    $(CC) $^ -o $@
 
-#endif
-```
-:::
-::: {.column width="55%"}
+hello.o: hello.c hello.h
+    $(CC) -c $<
 
-## `sum.c`
+main.o: main.c
+    $(CC) -c $<
 
-```C
-#include "sum.h"
-int sum(int tab[], int n) {
-  int s = 0;
-  for (int i = 0; i < n; i++) {
-    s += tab[i];
-  }
-  return s;
-}
+clean:
+    rm -f *.o hello
+
+rebuild: clean hello
 ```
-:::
 
+:::
 ::::::::::::::
 
+# Variables
 
-# Génération d'un exécutable (4/N)
-
-La compilation séparée se fait en plusieurs étapes.
-
-## Compilation séparée
-
-1. Générer séparément les fichiers `.o` avec l'option `-c`.
-2. Éditer les liens avec l'option `-o` pour générer l'exécutable.
-
-## Exemple
-
-- Création des fichiers objets, `main.o` et `sum.o`
+## Variables utilisateur
 
+- Déclaration
+ 
     ```bash
-    $ gcc -Wall -Wextra -std=c11 -c main.c
-    $ gcc -Wall -Wextra -std=c11 -c sum.c
+    id = valeur
+    id = valeur1 valeur2 valeur3
     ```
-- Édition des liens
-
+- Utilisation
+ 
     ```bash
-    $ gcc main.o sum.o -o prog
-    ```
-
-# Préprocesseur (1/N)
-
-## Généralités
-
-- Première étape de la chaîne de compilation.
-- Géré automatiquement par `gcc` ou `clang`.
-- Lit et interprète certaines directives:
-    1. Les commentaires (`//`{.C} et `/* ... */`{.C}).
-    2. Les commandes commençant par `#`{.C}.
-- Le préprocesseur ne compile rien, mais subtitue uniquement du texte.
-
-## La directive `define`{.C}
-
-- Permet de définir un symbole:
-
-    ```C
-    #define PI 3.14159
-    #define _SUM_H_
-    ```
-- Permet de définir une macro.
-
-    ```C
-    #define NOM_MACRO(arg1, arg2, ...) [code]
-    ```
-
-# Préprocesseur (2/N)
-
-## La directive `include`{.C}
-
-- Permet d'inclure un fichier.
-- Le contenu du fichier est ajouté à l'endroit du `#include`{.C}.
-- Inclusion de fichiers "globaux" ou "locaux"
-
-    ```C
-    #include <file.h>       // LD_LIBRARY_PATH
-    #include "other_file.h" // local path
+    $(id)
     ```
-- Les inclusions multiples peuvent poser problème: définitions multiples. Les headers commencent par:
+- Déclaration à la ligne de commande
 
-    ```C
-    #ifndef _VAR_
-    #define _VAR_
-    /*
-    commentaires
-    */
-    #endif
+    ```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
-- 
GitLab