diff --git a/Algorithmique/Cours/02-intro-c.md b/Algorithmique/Cours/02-intro-c.md deleted file mode 100644 index 116489ce45e5c5b00b8cc6728148ba74cf97d44b..0000000000000000000000000000000000000000 --- a/Algorithmique/Cours/02-intro-c.md +++ /dev/null @@ -1,683 +0,0 @@ ---- -title: "Module Algorithmie et programmation" -patat: - eval: - tai: - command: fish - fragment: false - replace: true - ccc: - command: fish - fragment: false - replace: true - images: - backend: auto -subtitle: "Introduction au langage C" -author: "Pierre Künzli" -institute: "Inspiré des cours de Paul Albuquerque, Guido Bologna et Orestis Malaspinas" -lang: fr-CH -revealjs-url: /reveal.js -mathjaxurl: "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_HTML" ---- - -## Le langage C, historique - -- Conçu initialement pour la programmation des systèmes d’exploitation (UNIX). -- Créé par Dennis Ritchie à Bell Labs en 1972 dans la continuation de CPL, BCPL et B. -- Standardisé entre 1983 et 1988 (ANSI C). -- La syntaxe de C est devenue la base d’autres langages comme C++, Objective-C, Java, Go, C#, Rust, etc. -- Révisions plus récentes, notamment C99, C11, puis C18. - -## Le langage C, historique - -- Développement de C lié au développement d’UNIX. -- UNIX a été initialement développé en assembleur: - - instructions de très bas niveau, - - instructions spécifiques à l’architecture du processeur. -- Pour rendre UNIX portable, un langage de *haut niveau* (en 1972) était nécessaire. -- Comparé à l’assembleur, le C est : - - Un langage de "haut niveau": C offre des fonctions, des structures de données, des constructions de contrôle de flots (`while`{.C}, `for`{.C}, etc). - - Portable: un programme C peut être exécuté sur un *très grand nombre* de plateformes (il suffit de recompiler le *même code* pour l’architecture voulue). - -## Qu'est-ce que le C? - -- "Petit langage simple" (en 2022). -- Langage compilé, statiquement (et faiblement) typé, procédural, portable, très efficace. -- Langage "bas niveau" (en 2022): gestion explicite et manuelle de la mémoire (allocation/désallocation), grande liberté pour sa manipulation. -- Pas de structures de haut niveau: chaînes de caractères, vecteurs dynamiques, listes, ... -- Aucune validation ou presque sur la mémoire (pointeurs, overflows, ...). - - -# La simplicité de C? - -## 32 mots-clé et c'est tout - ----------------- -------------- ---------------- --------------- -`auto`{.C} `double`{.C} `int`{.C} `struct`{.C} -`break`{.C} `else`{.C} `long`{.C} `switch`{.C} -`case`{.C} `enum`{.C} `register`{.C} `typedef`{.C} -`char`{.C} `extern`{.C} `return`{.C} `union`{.C} -`const`{.C} `float`{.C} `short`{.C} `unsigned`{.C} -`continue`{.C} `for`{.C} `signed`{.C} `void`{.C} -`default`{.C} `goto`{.C} `sizeof`{.C} `volatile`{.C} -`do`{.C} `if`{.C} `static`{.C} `while`{.C} ----------------- -------------- ---------------- --------------- - -# Déclaration et typage - -En C lorsqu'on veut utiliser une variable (ou une constante), on doit déclarer son type - -```C -const double two = 2.0; // déclaration et init. -int x; // déclaration (instruction) -char c; // déclaration (instruction) -x = 1; // affectation (expression) -c = 'a'; // affectation (expression) -int y = x; // déclaration et initialisation en même temps -int a, b, c; // déclarations multiples -a = b = c = 1; // init. multiples -``` - -# Les variables - -## Variables et portée - -- Une variable est un identifiant, qui peut être liée à une valeur (une expression). -- Une variable a une **portée** qui définit où elle est *visible* (où elle peut être accédée). -- La portée est **globale** ou **locale**. -- Une variable est **globale** est accessible à tout endroit d'un programme et doit être déclarée en dehors de toute fonction. -- Une variable est **locale** lorsqu'elle est déclarée dans un **bloc**, `{...}`{.C}. -- Une variable est dans la portée **après** avoir été déclarée. - -## Exemple - -```C -float max; // variable globale accessible partout -int foo() { - // max est visible ici - float a = max; // valide - // par contre les varibles du main() ne sont pas visibles -} -int main() { - // max est visible ici - int x = 1; // x est locale à main - { - // x est visible ici, y pas encore - // on peut par exemple pas faire x = y; - int y = 2; - } // y est détruite à la sortie du bloc -} // x est dàtruite à la sortie de main - -``` - -# Représentation des variables en mémoire - -## La mémoire - -* La mémoire est: - - ... un ensemble de bits, - - ... accessible via des adresses, - - +------+----------+----------+------+----------+------+------+ - | bits | 00110101 | 10010000 | .... | 00110011 | .... | .... | - +======+==========+==========+======+==========+======+======+ - | addr | 2000 | 2001 | .... | 4000 | .... | .... | - +------+----------+----------+------+----------+------+------+ - - - ... gérée par le système d'exploitation. - - ... séparée en deux parties: **la pile** et **le tas**. - -## Une variable - -* 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. - - -## Représentation des variables en mémoire - -{width=100%} - -# Types de base - -## Numériques - -Type Signification (**gcc pour x86-64**) ----------------------------------- --------------------------------------------- -`char`{.C}, `unsigned char`{.C} Entier signé/non-signé 8-bit -`short`{.C}, `unsigned short`{.C} Entier signé/non-signé 16-bit -`int`{.C}, `unsigned int`{.C} Entier signé/non-signé 32-bit -`long`{.C}, `unsigned long`{.C} Entier signé/non-signé 64-bit -`float`{.C} Nombre à virgule flottante, simple précision -`double`{.C} Nombre à virgule flottante, double précision ----------------------------------- --------------------------------------------- - -**La signification de `short`{.C}, `int`{.C}, ... dépend du compilateur et de l'architecture.** - -## Numériques - -Voir `<stdint.h>` pour des représentations **portables** - -Type Signification ----------------------------------- --------------------------------------------- -`int8_t`{.C}, `uint8_t`{.C} Entier signé/non-signé 8-bit -`int16_t`{.C}, `uint16_t`{.C} Entier signé/non-signé 16-bit -`int32_t`{.C}, `uint32_t`{.C} Entier signé/non-signé 32-bit -`int64_t`{.C}, `uint64_t`{.C} Entier signé/non-signé 64-bit ----------------------------------- --------------------------------------------- - - - -## Booléens - -- Le ANSI C n'offre pas de booléens. -- L'entier `0`{.C} signifie *faux*, tout le reste *vrai*. -- Depuis C99, la librairie `stdbool` met à disposition un type `bool`{.C}. -- En réalité c'est un entier: - - $1 \Rightarrow$ `true`{.C} - - $0 \Rightarrow$ `false`{.C} -- On peut les manipuler comme des entier (les sommer, les multiplier, ...). - -## Void - -Le type `void` est un type particulier, qui représente l'absence de donnée, de type ou un type indéterminé. On ne peut pas déclarer une variable de type `void`. - -## Conversions - -- Les conversions se font de manière: - - Explicite: - ```C - int a = (int)2.8; - double b = (double)a; - int c = (int)(2.8+0.5); - ``` - - Implicite: - ```C - int a = 2.8; // warning, si activés, avec clang - double b = a + 0.5; - char c = b; // pas de warning... - int d = 'c'; - ``` - -# Expressions et opérateurs - -Une expression est tout bout de code qui est **évalué**. - -## Expressions simples - -- Pas d'opérateurs impliqués. -- Les littéraux, les variables, et les constantes. - -```C -const int L = -1; // 'L' est une constante, -1 un littéral -int x = 0; // '0' est un litéral -int y = x; // 'x' est une variable -int z = L; // 'L' est une constante -``` - -## Expressions complexes - -- Obtenues en combinant des *opérandes* avec des *opérateurs* - -```C -int x; // pas une expression (une instruction) -x = 4 + 5; // 4 + 5 est une expression - // dont le résultat est affecté à 'x' -``` - - -## Opérateurs relationnels - -Opérateurs testant la relation entre deux *expressions*: - - - `(a opérateur b)` retourne `1`{.C} si l'expression s'évalue à `true`{.C}, `0`{.C} si l'expression s'évalue à `false`{.C}. - -| Opérateur | Syntaxe | Résultat | -|-----------|--------------|----------------------| -| `<`{.C} | `a < b`{.C} | 1 si a < b; 0 sinon | -| `>`{.C} | `a > b`{.C} | 1 si a > b; 0 sinon | -| `<=`{.C} | `a <= b`{.C} | 1 si a <= b; 0 sinon | -| `>=`{.C} | `a >= b`{.C} | 1 si a >= b; 0 sinon | -| `==`{.C} | `a == b`{.C} | 1 si a == b; 0 sinon | -| `!=`{.C} | `a != b`{.C} | 1 si a != b; 0 sinon | - - -## Opérateurs logiques - -| Opérateur | Syntaxe | Signification | -|-----------|--------------|----------------------| -| `&&`{.C} | `a && b`{.C} | ET logique | -| `||`{.C} | `a || b`{.C} | OU logique | -| `!`{.C} | `!a`{.C} | NON logique | - - -## Opérateurs arithmétiques - -| Opérateur | Syntaxe | Signification | -|-----------|--------------|----------------------| -| `+`{.C} | `a + b`{.C} | Addition | -| `-`{.C} | `a - b`{.C} | Soustraction | -| `*`{.C} | `a * b`{.C} | Multiplication | -| `/`{.C} | `a / b`{.C} | Division | -| `%`{.C} | `a % b`{.C} | Modulo | - - -## Opérateurs d'assignation - -| Opérateur | Syntaxe | Signification | -|-----------|--------------|---------------------------------------------| -| `=`{.C} | `a = b`{.C} | Affecte la valeur `b` à la variable `a` | -| | | et retourne la valeur de `b` | -| `+=`{.C} | `a += b`{.C} | Additionne la valeur de `b` à `a` et | -| | | assigne le résultat à `a`. | -| `-=`{.C} | `a -= b`{.C} | Soustrait la valeur de `b` à `a` et | -| | | assigne le résultat à `a`. | -| `*=`{.C} | `a *= b`{.C} | Multiplie la valeur de `b` à `a` et | -| | | assigne le résultat à `a`. | -| `/=`{.C} | `a /= b`{.C} | Divise la valeur de `b` à `a` et | -| | | assigne le résultat à `a`. | -| `%=`{.C} | `a %= b`{.C} | Calcule le modulo la valeur de `b` à `a` et | -| | | assigne le résultat à `a`. | - - -## Opérateurs d'assignation (suite) - -| Opérateur | Syntaxe | Signification | -|-----------|--------------|---------------------------------------------| -| `++`{.C} | `++a`{.C} | Incrémente la valeur de `a` de 1 et | -| | | retourne le résultat (`a += 1`). | -| `--`{.C} | `--a`{.C} | Décrémente la valeur de `a` de 1 et | -| | | retourne le résultat (`a -= 1`). | -| `++`{.C} | `a++`{.C} | Retourne `a`{.C} et incrémente `a` de 1. | -| `--`{.C} | `a--`{.C} | Retourne `a`{.C} et décrémente `a` de 1. | - - -# Structures de contrôle: `if`{.C} .. `else if`{.C} .. `else`{.C} - -## Syntaxe - -```C -if (expression) { - instructions; -} else if (expression) { // optionnel - // il peut y en avoir plusieurs - instructions; -} else { // optionnel - instructions; -} -``` - -```C -if (x) { // si x s'évalue à `vrai` - printf("x s'évalue à vrai.\n"); -} else if (y == 8) { // si y vaut 8 - printf("y vaut 8.\n"); -} else { - printf("Ni l'un ni l'autre.\n"); -} -``` - - - -## Pièges - -```C -int x, y; -x = y = 3; -if (x = 2) // affectation au lieu de comparaison - printf("x = 2 est vrai.\n"); -else if (y < 8) - printf("y < 8.\n"); -else if (y == 3) // n'entrera jamais dans cette branche - printf("y vaut 3 mais cela ne sera jamais affiché.\n"); -else - printf("Ni l'un ni l'autre.\n"); - x = -1; // toujours évalué -``` - -# Structures de contrôle: `switch`{.C} .. `case`{.C} - -```C -switch (expression) { - case constant-expression: - instructions; - break; // optionnel - case constant-expression: - instructions; - break; // optionnel - // ... - default: - instructions; -} -``` - -**Que se passe-t-il si `break`{.C} est absent?** - -## Structures de contrôle: `switch`{.C} .. `case`{.C} - -```C -int x = 0; -switch (x) { - case 0: - case 1: - printf("0 ou 1\n"); - break; - case 2: - printf("2\n"); - break; - default: - printf("autre\n"); -} -``` - -**Dangereux, mais c'est un moyen d'avoir un "ou" logique dans un case.** - - -# Structures de contrôle: `while`{.C} - -## La boucle `while`{.C} - -```C -while (condition) { - instructions; -} -``` - -ou - -```C -do { - instructions; -} while (condition); -``` - -## La boucle `while`{.C}, un exemple - -```C -int sum = 0; // syntaxe C99 -while (sum < 10) { - sum += 1; -} -do { - sum += 10; -} while (sum < 100) -``` - - - -# Structures de contrôle: `for`{.C} - -## La boucle `for`{.C} - -```C -for (expression1; expression2; expression3) { - instructions; -} -``` - -## La boucle `for`{.C} - -```C -int sum = 0; // syntaxe C99 -for (int i = 0; i < 10; i++) { - sum += i; -} - -for (int i = 0; i != 1; i = rand() % 4) { // ésotérique - printf("C'est plus ésotérique.\n"); -} -``` - - - -# Structures de contrôle: `continue`{.C}, `break`{.C} - -- `continue`{.C} saute à la prochaine itération d'une boucle. - - ```C - int i = 0; - while (i < 10) { - if (i == 3) { - continue; - } - printf("%d\n", i); - i += 1; - } - ``` - -- `break`{.C} quitte le bloc itératif courant d'une boucle. - - ```C - for (int i = 0; i < 10; i++) { - if (i == 3) { - break; - } - printf("%d\n", i); - } - ``` - -# Exercice: la factorielle - -Écrire un programme qui calcule la factorielle d'un nombre -$$ -N! = 1\cdot 2\cdot ... \cdot (N-1)\cdot N. -$$ - -## Ecrire un pseudo-code - -. . . - -```C -int factorielle(int n) { - i = 1; - fact = 1; - pour i <= n { - fact *= i; - i += 1; - } -} -``` - - -## Ecrire un code en C - - -. . . - -```C -#include <stdio.h> -int main() { - int nb = 10; - int fact = 1; - int iter = 1; - while (iter <= nb) { - fact *= iter; - iter++; - } -} -``` - -. . . - -### Comment améliorer ce code ? A faire en exercice. - - -# Entrées/sorties: `printf()`{.C} - -## Généralités - -- La fonction `printf()`{.C} permet d'afficher du texte sur le terminal: - - ```C - int printf(const char *format, ...); - ``` -- Nombre d'arguments variables. -- `format`{.C} est le texte, ainsi que le format (type) des variables à afficher. -- Les arguments suivants sont les expressions à afficher. - - -## Exemple - -```C -#include <stdio.h> -#include <stdlib.h> - -int main() { - printf("Hello world.\n"); - int val = 1; - printf("Hello world %d time.\n", val); - printf("%f squared is equal to %f.\n", 2.5, 2.5*2.5); - return EXIT_SUCCESS; -} -``` - -. . . - -### Remarque : ici la fonction `main` retourne `int` au lieu de `void` pour pouvoir retourner un code d'erreur. - -# Entrées/sorties: `scanf()`{.C} - -## Généralités - -- La fonction `scanf()`{.C} permet de lire du texte formaté entré au clavier: - - ```C - int scanf(const char *format, ...); - ``` - -- `format`{.C} est le format des variables à lire (comme `printf()`{.C}). -- Les arguments suivants sont les variables où sont stockées les valeurs lues. - - -## Exemple - -```C -#include <stdio.h> -#include <stdlib.h> - -int main() { - printf("Enter 3 numbers: \n"); - int i, j, k; - scanf("%d %d %d", &i, &j, &k); - printf("You entered: %d %d %d\n", i, j, k); - - return EXIT_SUCCESS; -} -``` - - -# Les fonctions - -- Les parties indépendantes d'un programme. -- Permettent de modulariser et compartimenter le code. -- Syntaxe: - - ```C - type identificateur(paramètres) { - // variables optionnelles - instructions; - // type expression == type - return expression; - } - ``` - - -## Exemple - -```C -int max(int a, int b) { - if (a > b) { - return a; - } else { - return b; - } -} - -int main() { - int c = max(4, 5); -} -``` - -## Les fonctions - -- 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"); - } - ``` - -## 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; - } - } - ``` - - -## 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 - void set_to_two(int a) { // a: nouvelle variable - // valeur de a est une copie de x - // 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 - } - ``` - - -## 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 syntaxe `*x`{.C} sert à **déréférencer** le pointeur (à accéder à la mémoire pointée). - - -## 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 -} -``` \ No newline at end of file