diff --git a/tutoriels/c/1SN_LangageC_C1.ipynb b/tutoriels/c/1SN_LangageC_C1.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..9df8ba8ce4f8ca7586adca4244b09f8b9abf8de5 --- /dev/null +++ b/tutoriels/c/1SN_LangageC_C1.ipynb @@ -0,0 +1,3077 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Langage C - Notebook C1.\n", + "\n", + "\n", + "#### Katia Jaffrès-Runser, Xavier Crégut\n", + "\n", + "Toulouse INP - ENSEEIHT,\n", + "\n", + "1ère année, Dept. Sciences du Numérique, 2021-2022." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1. Déroulement du cours\n", + "---\n", + "Ce cours se déroule sur 3 séances de TP au Semestre 5. L'ensemble des exercices de ce sujet C1 couvre les 3 séances. Certains exercices, et notamment les exercices BILAN, sont à réaliser directement sur votre machine (sans passer par le notebook) et sont à versionner avec votre dépot SVN.\n", + "\n", + "Vous aurez, en fin de cours, un QCM d'une demi-heure." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Objectifs \n", + "Ce cours, sous la forme de notebooks Jupyter et d'un ensemble d'exercices à réaliser en TP, a pour objectif de vous présenter les spécificités de la programmation en langage C. Il se base sur vos acquis du cours de Programmation Impérative en algorithmique et vous détaille les éléments du langage C nécessaires à la production d'un programme en C. \n", + "\n", + "Un support de cours PDF vous est également fournit sur Moodle : [Cours C](http://moodle-n7.inp-toulouse.fr/pluginfile.php/49240/mod_resource/content/5/LangageC_poly.pdf)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plan du sujet C1.\n", + "---\n", + "\n", + "Les éléments suivants de la programmation en Langage C sont présentés dans ce notebook au cours des 3 premières séances de TP : \n", + "- La structure d'un programme et sa compilation\n", + "- Les constantes, types et variables\n", + "- Les entrées / sorties\n", + "- Les structures de contrôle\n", + " - Conditionnelles\n", + " - Boucles\n", + "- Les types énumération, enregistrement et tableaux\n", + "- Les chaînes de caractère\n", + "- Le type pointeur\n", + "- Les sous-programmes en C \n", + " - Leur signature\n", + " - Passage par valeur\n", + " - Passage par adresse\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Jupyter notebook\n", + "\n", + "Le support de cours que vous lisez est un notebook Jupyter. Pour visualiser le notebook, lancer l'editeur web avec la commande \n", + "> `jupyter-notebook`\n", + "\n", + "et rechercher le fichier dans l'arborescence. Le fichier est édité dans votre navigateur Web par défaut. L'enregistrement est automatique (`CTRL S` pour le forcer). \n", + "\n", + "Pour fermer votre fichier, il faut fermer le navigateur et terminer le processus serveur qui s'exécute dans le terminal (`CTRL C`, puis `y`).\n", + "\n", + "> __Important__ : \n", + "> - Pour faire fonctionner le kernel C de jupyter notebook, il faut, avant votre __première utilisation__ de ce Notebook, lancer la commande suivante dans un `Terminal` : \n", + "> - `install_c_kernel --user`\n", + "\n", + "\n", + "Il se compose de cellules présentant soit :\n", + "- Des éléments de cours, au format [Markdown](https://fr.wikipedia.org/wiki/Markdown). Ce langage est interprété pour un affichage aisé quand on clique sur la flèche `Exécuter` et que la cellule est active.\n", + "- Du code en Langage C (ou Python, ou autre..). Pour compiler et exécuter le code écrit dans la cellule active, on clique sur la flèche `Exécuter`. Si la compilation se déroule sans erreur ni avertissement, le programme est exécuté et les sorties sont affichées en bas de la cellule. Si ce n'est pas le cas, les avertissements et warnings sont affichés en bas de la cellule. \n", + "\n", + "En double-cliquant sur une cellule, on peut éditer son contenu. \n", + "Vous pouvez ainsi : \n", + "- Editer une cellule markdown pour y intégrer vos propres notes. \n", + "- Modifier les programmes pour répondre aux questions et exercices proposés.\n", + "\n", + "Il est possible d'exporter votre travail en PDF, HTML, etc.\n", + "\n", + "Le programme __`premier.c`__ dans la cellule suivante s'exécute sans erreur. Vous pouvez \n", + "- le tester en l'exécutant. \n", + "- y introduire une erreur (suppression d'un point-virgule par exemple) pour observer la sortie du compilateur. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "\n", + "int main(){\n", + " printf(\"******************************\\n\");\n", + " printf(\"******** Langage C ***********\\n\");\n", + " printf(\"******************************\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Un premier programme en Langage C\n", + "\n", + "Le fichier __`pgcd.c`__ suivant comporte un programme en Langagce C. Exécutez-le. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "#include <assert.h>\n", + "\n", + "/* Afficher le pgcd de deux entiers strictement positifs. */\n", + "int main() {\n", + " // Déclaration et initialisation de deux entiers\n", + " int a = 105, b = 35; \n", + " \n", + " // Déterminer le pgcd de a et b\n", + " int na = a, nb = b; // gain de place ! À éviter !\n", + " while (na != nb) {\t// na et nb différents\n", + " // Soustraire au plus grand le plus petit\n", + " if (na > nb) {\n", + " na = na - nb;\n", + " } else {\n", + " nb = nb - na;\n", + " }\n", + " }\n", + " int pgcd = na; // le pgcd de a et b\n", + " \n", + " // Afficher le pgcd\n", + " printf(\"Le pgcd de %d et %d est %d\\n\", a, b , pgcd);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ce programme se compose de : \n", + "- Trois commandes **pré-processeur** `#include`. \n", + " - Toutes les commandes pré-processeur commencent par le caractère #.\n", + " - Ces deux commandes importent des librairies (i.e. des modules). \n", + "- La fonction `int main()`, qui correspond au programme principal. Ses instructions sont définies entre accolades. \n", + "> __Règle__ : L'identificateur du programme principal est forcément `main()`.\n", + "- Un ensemble d'instructions entre les accolades. \n", + "> __Règle__ : Chaque instruction se termine avec un __point-virgule__.\n", + "- Un appel au sous-programme d'affichage à l'écran `printf` du module `stdio`. A l'exécution, on observe que les valeurs des variables `a`, `b` et `pgcd` sont écrites en lieu et place des `%d`, dans l'ordre de leurs appels.\n", + "- Le retour d'une constante `EXIT_SUCCESS` définie dans le module `stdlib`. Cette constante vaut 0 et indique que l'exécution s'est terminée avec succès. Il existe aussi `EXIT_FAILURE` qui indique la mauvaise terminaison du programme.\n", + "> __Règle__ : L'instruction `return` arrête et indique le résultat de la fonction. \n", + "- Une boucle TantQue avec la structure de contrôle `while`. Les instruction de corps de la boucle sont définies entre accolades.\n", + "- Une conditionelle `if (condition) {..} else {..}`\n", + "- Des déclarations de variables, des opérations d'initialisation et d'affectations. \n", + "> __Règle__ : L'opérateur d'affectation est `=`.\n", + "- Des tests.\n", + "> __Règle__ : L'opérateur de test d'égalité est `==`, et d'inégalité est `!=`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compilation et pré-processeur\n", + "---\n", + "\n", + "La compilation en C se décompose en deux étapes successives : \n", + "1. L'exécution du pré-processeur, \n", + "2. L'exécution du compilateur C. \n", + "\n", + "Les deux étapes sont réalisées par un seul appel à la suite de compilation avec la commande :\n", + "> `gcc -Wall premier.c -o premier`\n", + "\n", + "Les options permettent : \n", + "- `Wall` : d'afficher l'ensemble des avertissements produits par la compilation\n", + "- `-o` : de choisir le nom de l'exécutable généré. \n", + "\n", + "> _Note 1_ : dans le cas particulier où on utiliser la bibliothèque `math.h`, il faut rajouter `-lm` à la commande de compilation. \n", + "\n", + "> _Note 2_ : les options de compilation avec le kernel C actuel étant limitées, on ne peut pas ajouter`-lm` à la commande de compilation dans ce notebook. Si vous devez utiliser `math.h`, il faut utiliser une ligne de commande pour compiler et non une cellule de code dans Jupyter notebook.\n", + "\n", + "Le pré-processeur fournit un unique fichier au compilateur, qui le transforme en un fichier binaire exécutable. \n", + "Ce pré-processeur : \n", + "- Supprime les commentaires de ligne `//` ou de bloc `/* */`.\n", + "- Interprête les commandes pré-processeur qui commencent par `#` (`#define`, `#include`, etc.)\n", + "\n", + ">__Règle :__ Il n'y a pas de point-virgule à la fin d'une instruction pré-processeur." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "#### Exercice 1 -- Compilation.\n", + "---\n", + "---\n", + "\n", + "__[1.1]__ Compiler votre premier programme dans un terminal. Pour se faire, créer un répertoire `Langage_C` et y ajouter un fichier nommé `pgcd.c`. Recopier le programme de l'exemple précédent. Le compiler avec le compilateur `gcc`et l'exécuter avec la commande `./pgcd`\n", + "\n", + "__[1.2]__ Introduire une erreur dans les instructions et observer le retour du compilateur :\n", + "\n", + "- Suppression d'un point-virgule en fin de ligne,\n", + "- Ajout d'un point-virgule supplémentaire en fin de ligne, \n", + "- Supprimer la déclaration de la variable `a`. \n", + "- Supprimer l'accolade de fin de bloc de la boucle `while`.\n", + "\n", + "__[1.3]__ Observer l'unique fichier généré par le pré-processeur avec l'appel à la commande \n", + "`cpp -P premier_programme.c`. Quel est l'effet de la commande `#include <stdio.h>` ?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "#### Exercice 2 -- Comprendre la macro `assert()`.\n", + "---\n", + "---\n", + "Voyons comment fonctionne la macro `assert` du langage C. Nous nous appuyons sur le programme `assert-comprendre.c`. \n", + "\n", + "__[2.1]__ Compiler et exécuter dans Jupyter Notebook ce programme (fichier __`macro_assert.c`__). Qu'observez-vous ? " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "\n", + "void assert_ok() {\n", + " int n = 10;\n", + " assert(n > 0);\n", + " printf(\"(assert_ok) n = %d\\n\", n);\n", + "}\n", + "\n", + "\n", + "void assert_erreur() {\n", + " int n = 10;\n", + " assert(n <= 0);\n", + " printf(\"(assert_erreur) n = %d\\n\", n);\n", + "}\n", + "\n", + "\n", + "int main(void) {\n", + " assert_ok();\n", + " assert_erreur();\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "L’appel à `assert_ok` se déroule normalement car le paramètre effectif de `assert` s’évalue à vrai. \n", + "\n", + "Au contraire, l’appel à `assert_erreur` provoque l’arrêt du programme car le paramètre effectif de assert s’évalue à faux. \n", + "Un message d’erreur indique la ligne dans le fichier source contenant l’appel à `assert`.\n", + "\n", + "\n", + "__[2.2]__ Compiler et exécuter le programme dans un terminal, dans le répertoire SVN fourni, avec les commandes : \n", + "```C\n", + "make assert-comprendre\n", + "./assert-comprendre\n", + "```\n", + "> _Note_ : La commande `make` sera présentée à la fin du cours. Elle permet d'automatiser la compilation. Elle est paramétrée par le fichier `Makefile`. Vous pouvez le consulter mais sa compréhension n'est pas l'objet de cette question.\n", + "\n", + "Qu'observez-vous ? \n", + "\n", + "__[2.3]__ L’évaluation des assert peut être désactivée en définissant la macro `NDEBUG` (no debug). Par exemple, en début du fichier `assert-comprendre.c` (mais avant l’inclusion de `assert.h`), on peut\n", + "ajouter la commande préprocesseur suivante qui définit NDEBUG :\n", + "```C\n", + "#define NDEBUG\n", + "```\n", + "Modifier le fichier `assert-comprendre.c`, compiler et exécuter à nouveau pour constater que les assert ne sont plus vérifiés. Vous pouvez aussi le tester sur le notebook Jupyter.\n", + "\n", + "> _Note_ : En général, on positionne `NDEBUG` à la compilation, sans l'écrire dans le fichier C, en utilisant l’option `-D` du compilateur (`-DNDEBUG`) : \n", + "```\n", + "gcc -Wall -pedantic -DNDEBUG assert-comprendre.c -o assert-comprendre\n", + "```\n", + "On peut aussi ajouter `-DNDEBUG` à la définition de `CFLAGS` dans le fichier `Makefile`.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Types\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plusieurs types fondamentaux sont définis en C :\n", + "- Des types discrets : `int` (entier), `bool`(bouléen), `char` (caractère)\n", + "- Des types réels : `float` et `double`, à simple et double précision.\n", + "> Note :\n", + "> Il faut inclure le module `stdbool` pour utiliser le type booléen, et ses valeur `true` et `false`.\n", + "\n", + "#### Exemples\n", + "```C\n", + "int entier_1 = 20;\n", + "bool est_vide = false;\n", + "char initiale = 'B'; //Caractère constant 'B' entre guillemets simples/ \n", + "```\n", + "\n", + "### Modificateurs de type\n", + "Il existe aussi des modificateurs de type : `long`, `short`, `unsigned`. Ils sont utilisés pour modifier certains types fondamentaux `int`, `double`, `float`. \n", + "\n", + "La taille en mémoire d'une variable entière de type `short int` est inférieure à la taille mémoire d'une variable de type `int`, qui elle même est inférieure à une variable de taille `long int`.\n", + "\n", + "Le modificateur `unsigned` définit un type à valeurs positives ou nulles. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "#### Exercice 3 -- Valeurs maximales et conversion implicites\n", + "---\n", + "---\n", + " \n", + "La valeur maximale des types dépend du système d'exploitation. Elles sont enregistrées dans les bibliothèques `limits.h` pour les entiers et `float.h` pour les flottants. \n", + "\n", + "__[3.1]__ Exécuter l'exemple du fichier __`conversions.c`__suivant pour les observer. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "// Liste les valeurs maximales des entiers pour votre système\n", + "#include <limits.h> \n", + "// Liste les valeurs maximales des flottants pour votre système\n", + "#include <float.h> \n", + "\n", + "int main(){\n", + " printf(\"Valeur maximale d'un entier %d \\n\", INT_MAX); \n", + " long int entier_long = -20000000; // Déclaration d'un entier long\n", + " printf(\"Valeur maximale d'un entier long %ld > %ld \\n\\n\", LONG_MAX, entier_long); \n", + " \n", + " unsigned long int entier_non_signe = entier_long; // Il y a conversion implicite \n", + " printf(\"Valeur maximale d'un entier non signé %u \\n\", UINT_MAX); \n", + " printf(\"Valeur maximale d'un entier non signé long %lu > %lu \\n\\n\", ULONG_MAX, entier_non_signe); \n", + " \n", + " float flottant_simple = 20.13;\n", + " double flottant_double; \n", + " long double long_double = 200001102.2;\n", + " printf(\"Valeur maximale d'un réel simple : \\n%f \\n < valeur max double : \\n%lf \\n < valeur max long double : \\n%Lf \", FLT_MAX, DBL_MAX, LDBL_MAX);\n", + " \n", + " return EXIT_SUCCESS;\n", + "}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[3.2]__ Il est possible d'initialiser un entier non signé avec un entier signé. Observer la valeur obtenue pour l'entier non signé. D'où provient-elle ? " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> _Note_ : pour afficher ces valeurs maximales avec `printf`, on doit modifier les lettres qui suivent le signe `%` dans `printf` pour adapter le format au type des variables :\n", + "> - `%d`, `%ld` : permet d'afficher un entier, un entier long. \n", + "> - `%u`, `%lu` : permet d'afficher un entier non signé, un entier non signé long.\n", + "> - `%f`, `%lf`, `%Lf` : permet d'afficher un flottant, un double et un long double. \n", + "> - `%c` : permet d'afficher un caractère. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[3.3]__ Modifier le code ci-dessus pour que :\n", + "1. l'entier non signé soit affiché comme un entier signé par `printf`. \n", + "2. l'entier non signé soit affiché comme un flottant simple.\n", + "\n", + "Qu'observez-vous dans les deux cas ? Que peut-on en conclure sur les avertissements du compilateur ? " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Variables\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Déclaration de variables\n", + "Les variables peuvent être déclarées n'importe quand. Typiquement, **on déclare une variable au moment où on l'utilise** de la façon suivante :\n", + "\n", + "`type identifiant_variable;`\n", + "\n", + "Exemples : \n", + "```C\n", + "int valeur, produit; // déclaration de deux variables entières, \n", + "double numerateur; // déclaration d'une variable réelle.\n", + "char initiale; // déclaration d'une variable caractère\n", + "```\n", + "### Affectation de variables\n", + "L'initialisation et l'affectation des variables est réalisé **avec l'opérateur =** comme dans le fichier __`affectations.c`__ :\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "int main(){\n", + " int valeur = 10, produit = 23; // déclaration et initialisation de deux entiers, \n", + " double numerateur = 10.3;\n", + " char initiale = 'A'; \n", + "\n", + " produit = produit * valeur; // affectation \n", + " valeur = valeur + 1;\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Opérateurs arithmétiques\n", + "Les opérateurs binaires __+, - , * et /__ s'appliquent à des variables de type entier (signés ou non), booléens, flottant ou double.\n", + "\n", + "Les opérateurs binaires __/ et %__ utilisés sur __des entiers__, fournissent respectivement le quotient de le reste de la division entière des deux termes.\n", + "\n", + "> __Règle__ : \n", + "> - Division __entière__ : la division `a / b` __si `b` est entier__ fournit le quotient de la division entière. \n", + "> - Division __réélle__ : la division `a / b` __si `b` est réel__ fournit un résultat réel.\n", + "\n", + "Les opérateur unaires __- et +__ s'appliquent aux entiers signés et aux types réels. \n", + "\n", + "> Note : Des opérateur mathématiques avancés sont disponibles dans la bibliothèque `<math.h>` (puissance, log, etc.)\n", + "\n", + "__Exemples__ (à exécuter, cf. fichier __`operateurs.c`__)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <assert.h>\n", + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "\n", + "int main(){\n", + " int quantite = 10; \n", + " int prix = 15;\n", + "\n", + " int total = quantite * prix; \n", + " assert(total == 150);\n", + " \n", + " float nb_personnes = 60;\n", + " float prix_par_personne = total / nb_personnes;\n", + " assert(prix_par_personne == 2.5);\n", + " \n", + " int nb_personnes_int = 60;\n", + " prix_par_personne = total / nb_personnes_int;\n", + " assert(prix_par_personne != 2.5); // Quelle est la valeur de prix_par_personne ici ?\n", + " \n", + " printf(\"Le prix par personne est de %f euros\", prix_par_personne);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Affectations avec opération\n", + "Les instructions de la forme `x = x # y` avec `#` un opérateur arithmétique binaire, se synthétisent en C par : \n", + "\n", + "` x #= y `\n", + "\n", + "Il est aussi possible de simplifier l'incrémentation et la décrémentation avec les opérateurs __++ et --__. On a :\n", + "- `i++;` équivalent à `i = i+1;` \n", + "- `i--;` équivalent à `i = i-1;`.\n", + "\n", + "__Exemples__ (à exécuter, cf. fichier __`plusegal.c`__): " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <assert.h>\n", + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "\n", + "int main(){\n", + " int valeur = 10, produit = 23;\n", + " produit += valeur; // On ajoute valeur à produit\n", + " assert(produit == 33);\n", + " \n", + " produit *= 2; // multiplication par 2 puis affectation\n", + " assert(produit == 66);\n", + " \n", + " produit /= 3; // division par 3 puis affectation\n", + " assert(produit == 22);\n", + " \n", + " valeur++; // incrémentation de valeur\n", + " produit--; // décrémentation de produit\n", + " assert(valeur == 11 && produit == 21);\n", + " \n", + " printf(\"%s\", \"Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Opérateurs de comparaison\n", + "Les opérateurs __==, !=, <, >, >= et <=__ permettent de comparer deux variables. La proposition `vrai` s'évalue à `1` et proposition `faux` à `0`. \n", + "\n", + "### Opérateurs logiques\n", + "C définit les opérateurs logiques suivants : \n", + "- Le EtAlors algorithmique : __&&__, \n", + "- Le OuSinon algorithmique : __||__,\n", + "- La négation : __!__ " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Types caractère et entier en langage C\n", + "Un caractère est représenté en mémoire comme un entier non-signé (`unsigned int`) qui correspond au code ASCII de ce caractère. Les types caractère et entier (non-signé) sont donc compatibles. \n", + "\n", + "L'exemple suivant (à exécuter) présente les différentes opérations permettant de convertir un entier en caractère, et réciproquement. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <assert.h>\n", + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "int main(){\n", + " // Conversion du char '1' en l'entier 1\n", + " char c_char = '1';\n", + " int c_int = c_char - '0'; //on retire le code ascii du caractère '0'\n", + " assert(c_int == 1);\n", + " \n", + " // Conversion de l'entier 1 en un char qui vaut '1' \n", + " int new_int = 1;\n", + " char c_char2 = new_int + '0'; //on ajoute le code ascii du caractère '0'\n", + " assert(c_char2 == '1'); // c_char2 est bien égal au caractère \n", + " \n", + " printf(\"%s\", \"Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "#### Exercice 4 -- Comprendre les opérateurs arithmétiques et les relations entre caractere et entier.\n", + "---\n", + "---\n", + "__[4.1]__ Dans cet exercice (fichier __`exercice4.c`__), suivre la consigne présentée dans les commentaires." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#define XXX -1\n", + "\n", + "// Consigne : dans la suite *** uniquement ***, remplacer XXX par le bon \n", + "// résultat (une constante littérale).\n", + "\n", + "#include <assert.h>\n", + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "\n", + "int main(void) {\n", + " // Comprendre les opérateurs arithmétiques\n", + " assert(XXX == 5 - 2 * 5);\n", + " assert(XXX == 25 % 10);\n", + " assert(XXX == 25 / 10);\n", + " assert(XXX == 25 / 10.0);\n", + "\n", + " // Comprendre les relations caractères et entiers\n", + " assert(XXX == '5' - '0');\n", + " assert(XXX == '0' + 7);\n", + " assert(XXX == 'A' + 3);\n", + "\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + "\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Portée et masquage des variables\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Portée et masquage des variables.\n", + "__Un bloc__ est une série d'instructions délimitée par __une paire d'accolades__.\n", + "\n", + "- __Portée__ : Les variables déclarées dans un bloc sont libérées quand l'accolade fermante est exécutée. On dit que leur portée se limite au bloc où elles sont déclarées. \n", + "\n", + "```C\n", + "{ //debut du bloc B1\n", + " int age = 20;\n", + " { // debut du bloc B2\n", + " int nouvel_age = 25 ; // variable locale à B2\n", + " } // fin du bloc B2\n", + " // La variable nouvel_age n'existe plus. \n", + " age = age + 1;\n", + "} //fin du bloc B1\n", + "```\n", + "\n", + "- __Masquage__ : Les variables déclarées dans un bloc peuvent avoir le même identifiant qu'une variable déclarée avant l'ouverture du bloc. Dans ce cas, la variable déclarées dans le bloc masque la variable homonyme déclarées avant : c'est elle qui est utilisée par les instructions du bloc.\n", + "\n", + "Masquage et portée sont illustrés dans l'exemple (__`portee_masquage.c`__ à exécuter) suivant : " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <assert.h>\n", + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "\n", + "int main() { //debut du bloc B1\n", + " int alea = 20, diviseur = 2;\n", + " \n", + " { //debut du bloc B2\n", + " \n", + " int alea = 3; // masquage de la variable entière alea par la variable alea entière\n", + " float diviseur = 2.0; // idem pour le diviseur réel qui masque le diviseur de type entier.\n", + " float res_reel = alea / diviseur;\n", + " assert(res_reel = 1.5);\n", + " \n", + " } // du bloc B2\n", + " int res_int = alea / diviseur;\n", + " assert(res_int = 10);\n", + " \n", + " printf(\"%s\", \"Les tests passent\\n\");\n", + " return EXIT_SUCCESS;\n", + "} //fin du bloc B1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "#### Exercice 5 -- Portée et masquage des variables\n", + "---\n", + "---\n", + "__[5.1]__ Dans cet exercice (fichier __`exercice5.c`__), suivre la consigne présentée dans les commentaires." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Objectifs : Illustrer portée et masquage.\n", + "\n", + "#define XXX -1\n", + "\n", + "// Consigne : *** dans la suite uniquement ***, remplacer XXX par le bon résultat (une\n", + "// constante littérale). Ne compiler et exécuter que quand tous les XXX ont été traités.\n", + "\n", + "#include <assert.h>\n", + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "\n", + "int main(void) {\n", + " int x = 10;\n", + " assert(XXX == x);\n", + "\n", + " {\n", + " int y = 7;\n", + " assert(XXX == x);\n", + " assert(XXX == y);\n", + "\n", + " {\n", + " char x = '?';\n", + " assert(XXX == x);\n", + " assert(XXX == y);\n", + " y = XXX;\n", + " }\n", + "\n", + " assert(XXX == x);\n", + " assert(XXX == y);\n", + " }\n", + "\n", + " assert(XXX == x);\n", + "\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constantes\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Les constantes se déclarent généralement juste après l'inclusion des bibliothèques. Leur valeur ne peut être modifiée.\n", + "Il est possible de définir des constantes de deux façons : \n", + "1. En utilisant le mot-clé `const` pour obtenir une constante typée\n", + "\n", + "```C\n", + " const int MAJORITE_EU = 18; // déclaration d'une constante typée MAJORITE_EU \n", + " const int MAJORITE_US = 21; // déclaration d'une constante typée MAJORITE_US \n", + " const char CM = 'c'; // déclaration d'une constante caractère. \n", + "```\n", + "\n", + ">__Règle__ : Un caractère se distingue par l'utilisation de guillements simples (apostrophe) : 'A', 'c', 'D', '\\n', '\\t'.\n", + "\n", + "2. En définissant une constante pré-processeur : \n", + "\n", + "```C\n", + " #define MAJORITE_EU 18 // déclaration d'une constante pré-processeur 18, \n", + "```\n", + "Le pré-processeur remplace les occurrences de `MAJORITE_EU` par la valeur 18. \n", + "\n", + ">__Règle__ : Pas de point-virgule à la fin d'une instruction pré-processeur.\n", + "\n", + "__Constantes littérales__\n", + "\n", + "Ce sont les valeurs numériques écrites directement dans les instructions : \n", + "```C\n", + " int age = 20; // 20 est une constante littérale\n", + " char initiale_nom = 'M' // Le caractère 'M' est une constante littérale.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Expressions et compatibilité entre types\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Définition d'une expression\n", + "Une expression est une instruction qui est caractérisée par une valeur de retour. Voici quelques exemples : \n", + "- Une variable initialisée \n", + "```C\n", + " int val = 20;\n", + " val; // La variable `val` vaut 20 dans cette instruction.\n", + "```\n", + "- Une comparaison : `(b > 20)`. Cette expression vaudra `true` ou `false`.\n", + "- L'utilisation d'opérateur arithmétiques : \n", + "```C\n", + " int x = 3;\n", + " x + 3; // Cette expression vaut 6\n", + " (x * 2) / 3; // Cette expression vaut 2\n", + "```\n", + "\n", + "> _Note_ : Une affectation est aussi une expression : `val = 40` est une expression qui vaut 40. L'utilisateur de l'affectation comme expression est à éviter en C. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Priorité des opérateurs\n", + "En C, la priorité des opérateurs évalués dans une même expression est la suivante :\n", + "\n", + "Priorité | Opérateurs | \n", + ":-------:|:-:|\n", + "1 | +, -, __!__ (unaires)|\n", + "2 | *, /(entier), /(flottant), %, __&&__ |\n", + "3 | +, -, __\\|\\|__ |\n", + "4 | __<, >, <=, >=, ==, !=__ |\n", + "\n", + "La priorité 1 est la plus forte. Les opérateurs booléens sont présentés en gras." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compatibilité entre types\n", + "En C, **une expression peut être composée d'expressions de types différents si ces types sont compatibles**. Voici quelques exemples (fichier __`compatibilite.c`__:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <assert.h>\n", + "#include <stdlib.h>\n", + "int main(){\n", + " int quantite = 5;\n", + " float prix = 12.3;\n", + "\n", + " float total = prix * quantite; //l'entier quantité est compatible avec les flottants \n", + " float recette = 12; // l'entier 12 est compatible avec le flottant recette.\n", + " assert(total == 12.3*5.0 && recette == 12.0);\n", + "\n", + " quantite = total / recette;\n", + " assert(quantite != total / recette);\n", + " // le réel obtenue par la division de total et recette \n", + " // n'est pas compatible avec l'entier quantite.\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Si un type A est compatible avec un type B, on peut interchanger une expression de type B par une expression de type A sans changer la valeur de l'expression.\n", + "\n", + "Typiquement, un type A est compatible avec un type B si le passage de l'un à l'autre n'engendre pas de perte de donnée : \n", + "- Un entier est compatible avec un réel (12 devient 12.0)\n", + "- Un réel n'est pas compatible avec un entier (le passage de 1.3 à 1 introduit une perte d'information).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Conversion explicite\n", + "Il est possible de convertir explicitement une expression pour qu'elle soit évaluée avec un autre type. \n", + "Pour cela, on utilise la notation : \n", + "> `(type) expression`\n", + "\n", + "__Exemple__ Voici l'exemple illustrant la division entière et réelle présenté précédement (cf fichier __`conversion_explicite.c`__). Il a été modifié pour déclarer le nombre de personnes avec un entier, et dériver tout de même un prix par personnes avec une division réelle grâce à une conversion explicite." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <assert.h>\n", + "#include <stdio.h>\n", + "\n", + "int main(){\n", + " int quantite = 10; \n", + " int prix = 15;\n", + "\n", + " int total = quantite * prix; \n", + " assert(total == 150);\n", + " \n", + " int nb_personnes_int = 60;\n", + " float prix_par_personne = total /(float) nb_personnes_int;\n", + " assert(prix_par_personne == 2.5); // Maintenant on effectue bien une division réelle \n", + " \n", + " printf(\"Le prix par personne est de %1.2f euros\", prix_par_personne);\n", + " return 0;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> _Note_ : On observe que le descripteur de format `%f` a été étendu à `%1.2f` pour limiter le nombre de décimales à 2." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 6\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[6.1]__ Ecrire un programme (fichier __`exercice6.c`__) qui calcule le périmètre et l'aire d'un cercle, étant donné un rayon qui vaut `15`. Le rayon est une variable entière. Les éventuelles constantes seront déclarées comme des constantes pré-processeur. \n", + "\n", + "__[6.2]__ Ecrire les deux résultats réels à l'écran." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "\n", + "int main(){\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 7\n", + "---\n", + "---\n", + "__[7.1]__ Compléter et corriger le corps des fonctions du fichier __`exercice7.c`__ ci-dessous (voir TODO)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <stdbool.h>\n", + "#include <assert.h>\n", + "\n", + "/**\n", + " * \\brief obtenir le chiffre des unités d'un entier naturel.\n", + " * \\param[in] nombre le nombre dont on veut obtenir le chiffre des unités\n", + " * \\return le chiffre des unités de nombre\n", + " * \\pre nombre positif : nombre >= 0\n", + " */\n", + "int chiffre_unites(int nombre)\n", + "{\n", + " assert(nombre >= 0);\n", + "\n", + " // TODO: Donner le bon code !\n", + " return -1;\n", + "}\n", + "\n", + "/**\n", + " * \\brief obtenir le chiffre des dizaines d'un entier naturel.\n", + " * \\param[in] nombre le nombre dont on veut obtenir le chiffre des dizaines\n", + " * \\return le chiffre des dizaines de nombre\n", + " * \\pre nombre positif : nombre >= 0\n", + " */\n", + "int chiffre_dizaines(int nombre)\n", + "{\n", + " assert(nombre >= 0);\n", + "\n", + " // TODO: Donner le bon code !\n", + " return -1;\n", + "}\n", + "\n", + "/**\n", + " * \\brief Indiquer si une année est bissextile.\n", + " * \\param[in] annee l'année à considérer\n", + " * \\return vrai si l'année est bissextile\n", + " * \\pre année positive : annee > 0\n", + " */\n", + "bool est_bissextile(int annee) {\n", + " // TODO: Donner le bon code !\n", + " // Attention : on n'utilisera pas de conditionnelle,\n", + " // seulement les opérateurs logiques.\n", + " return -1;\n", + "}\n", + "\n", + "\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "// //\n", + "// NE PAS MODIFIER CE QUI SUIT... //\n", + "// //\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "\n", + "\n", + "void test_chiffre_unites(void) {\n", + " assert(5 == chiffre_unites(1515));\n", + " assert(2 == chiffre_unites(142));\n", + " assert(0 == chiffre_unites(0));\n", + " printf(\"%s\", \"chiffre_unites... ok\\n\");\n", + "}\n", + "\n", + "void test_chiffre_dizaines(void) {\n", + " assert(1 == chiffre_dizaines(1515));\n", + " assert(4 == chiffre_dizaines(142));\n", + " assert(9 == chiffre_dizaines(91));\n", + " assert(8 == chiffre_dizaines(80));\n", + " assert(0 == chiffre_dizaines(7));\n", + " assert(0 == chiffre_dizaines(0));\n", + " printf(\"%s\", \"chiffre_dizaines... ok\\n\");\n", + "}\n", + "\n", + "\n", + "void test_annee_bissextile(void) {\n", + " // cas simples\n", + " assert(! est_bissextile(2019));\n", + " assert(est_bissextile(2020));\n", + " assert(est_bissextile(2016));\n", + "\n", + " // multiples de 100\n", + " assert(! est_bissextile(1900));\n", + " assert(! est_bissextile(2100));\n", + "\n", + " // multiples de 400\n", + " assert(est_bissextile(1600));\n", + " assert(est_bissextile(2000));\n", + " assert(est_bissextile(2400));\n", + "\n", + " printf(\"%s\", \"annee_bissextile... ok\\n\");\n", + "}\n", + "\n", + "\n", + "int main(void) {\n", + " test_chiffre_unites();\n", + " test_chiffre_dizaines();\n", + " test_annee_bissextile();\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Entrées et sorties en Langage C\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Définition\n", + "- Les entrées sont des instructions qui permettent de lire des données provenant de l'environnement d'exécution (clavier, souris, capteur, fichier, réseau, etc.). \n", + "- Les sorties sont des instructions qui permettent de transférer des données à l'environnement d'exécution (moniteur, actuateur, fichier, réseau, etc.).\n", + "\n", + "Cette partie présente l'utilisation des **entrées clavier ou des sorties moniteur**. Les autres types de périphériques seront abordés dans d'autres enseignements.\n", + "\n", + "> Attention : **la gestion des entrées/sorties en C n'est pas triviale !** " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Flux de données\n", + "La notion de flux de données est fondamentale. Les flux permettent d'interagir avec les périphériques pour échanger des données. \n", + "Un flux est une file d'attente de type FIFO (_fist in first out_) : \n", + "- la données la plus ancienne peut être lue. Elle est alors consommée (supprimée de la file), \n", + "- la donnée la plus récente est insérée en fin de file. On dit qu'elle est écrite dans la file. \n", + "\n", + "En C, on peut manipuler des flux qui enregistrent des données de deux types : \n", + "- **texte** : on y enregistre une suite de caractères, séparés par des retour-chariots,\n", + "- **binaire** : on y enregistre une suite d'octets.\n", + "\n", + "Il existe des files sont définies par défaut : \n", + "- `FILE* STDOUT` : flux de sortie vers le moniteur\n", + "- `FILE* STDIN`: flux d'entree depuis le clavier\n", + "Elles sont toutes de type `FILE*`\n", + "\n", + "> Note : \n", + "> - Ces files sont définies dans le module `stdlib`\n", + "> - Les sous-programmes mentionnés par la suite sont définis dans le module `stdio`\n", + "\n", + "> Remarque : \n", + "> - En 1SN nous ne traiterons que les entrées sorties en mode **texte**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ecrire les sorties " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Sorties formattés\n", + "L'objectif du sous-programme `printf()` est d'écrire des données typées à l'écran. L'objectif est d'afficher en une seule instruction le contenu de variables de type entier, flottant, chaine de caractères, voire une combinaison hétérogène de variables.\n", + "\n", + "Le sous-programme `printf` du module `stdio` est définit comme suit : \n", + "\n", + "> `int printf(\"format\", param1, param2, etc.);`\n", + "\n", + "La chaîne \"format\" est une chaîne de caractères, parsemée de __spécificateurs de format__.\n", + "\n", + "**Un spécificateur de format commence par le caractère %**. \n", + "Il y autant de spécificateurs de format que de paramètres. A l'exécution, \n", + "- le 1er spécificateur est remplacé par la valeur du 1er paramètre, \n", + "- le 2er spécificateur est remplacé par la valeur du 2er paramètre, \n", + "- etc.\n", + "\n", + "Le spécificateur indique comment afficher la variable qui lui correspond : \n", + "- %d ou %i : indique à printf que l'on souhaite afficher le paramètre comme un entier signé\n", + "- %u : indique à printf que l'on souhaite afficher le paramètre comme un entier __non__signé\n", + "- %f, %lf, %Lf : indique à printf que l'on souhaite afficher le paramètre comme un float, double ou long double. Il est possible de limiter le nombre de décimales : %1.3f limite le nombre de décimales à 3.\n", + "- %c : indique à printf que l'on souhaite afficher le paramètre comme un caractère\n", + "- %s : indique à printf que l'on souhaite afficher le paramètre comme une chaîne de caractères\n", + "- %p : indique à printf que l'on souhaite afficher le paramètre comme une adresse\n", + "- etc.\n", + "\n", + "> __ATTENTION__ : le compilateur ne vérifie pas forcément la cohérence entre le spécificateur et le type du paramètre correspondant ! Des warnings sont généralement observés.\n", + "\n", + "__Exemples__ (fichier __`exemple_ES.c`__): " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "\n", + "int main(){\n", + " float cote = 2.0; //longueur du côté\n", + " char unite = 'm';\n", + " printf(\"Le périmètre du carré de côté %1.0f%c est : \", cote, unite); \n", + " //affichage du flottant avec 0 chiffres après la virgule\n", + " \n", + " //calcul et affichage du perimètre\n", + " float perimetre = 4 * cote; \n", + " printf(\"%1.2f%c\\n\", perimetre, unite); \n", + " //affichage du flottant avec 2 chiffres après la virgule\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> _Note_ : Il existe d'autres sous-programmes d'écriture qui ne seront pas présentés ici : `putchar()`, `fputc()`, `sprintf()`, `fprintf()`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### Lire les entrées " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Entrées formattés\n", + "L'objectif du sous-programme `scanf()` est de lire des données typées depuis le clavier. \n", + "Le sous-programme `scanf` du module `stdio` est définit comme suit : \n", + "\n", + "> `int scanf(\"format\", ¶m1, ¶m2, etc.);`\n", + "\n", + "La chaîne \"format\" ne comporte **principalement des spécificateurs de formats**. Chaque format fait référence à un des paramètres, dans l'ordre d'apparition. \n", + "Le spécificateur indique comment lire la variable qui lui correspond : \n", + "- %d ou %i : indique à `scanf` qu'il doit lire un entier\n", + "- %f : indique à scanf qu'il doit lire un float\n", + "- etc.\n", + "\n", + "La donnée lue est écrite à __l'adresse__ de `param1`, `param2`. etc.\n", + "\n", + "> Si dans le \"format\" on insère un espace entre deux spécificateurs, tous les caractères 'blancs' (espace, tabulation) sont consommés mais non interprétés.\n", + "\n", + "L'entier retourné par `scanf` représente le nombre de paramètres lus avec succès.\n", + "\n", + "__Exemples__ : \n", + "```C\n", + "// Lire un entier\n", + "int monentier;\n", + "scanf(\"%i\", &monentier);\n", + "// Lire un flottant avec 2 décimales maximum.\n", + "float monfloat;\n", + "scanf(\"%1.2f\", &monfloat); \n", + "// Lire deux caracteres d'affilée non blancs\n", + "char c1, c2;\n", + "scanf(\"%c %c\", &c1, &c2);\n", + "```\n", + "\n", + "> _Note_ : Il existe d'autres sous-programmes de lecture des entrées qui ne seront pas présentés ici : `getchar()`, `fgetc()`, `sscanf()`, `fscanf()`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Les structures de contrôle\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Elles permettent de contrôle l'ordre d'exécution des instructions. En C, il existe \n", + "- La séquence\n", + "- Les structures conditionnelles : \n", + " - `if ... then ... else`\n", + " - `switch ... case ... `\n", + "- Les boucles : \n", + " - Répéter : `do ... while`\n", + " - TantQue : `while ...`\n", + " - Pour : `for ... `" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Les conditionelles\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__1. La conditionnelle simple :__\n", + "\n", + "```C\n", + "if (cond) {\n", + " sequence1\n", + "} else {\n", + " sequence2\n", + "}\n", + "```\n", + "Si la condition `cond` est vraie, `séquenc1` est exécutée, sinon, `sequence2` est exécutée.\n", + "\n", + "La clause `SinonSi` n'existe pas, on imbrique les conditionnelles pour introduire une étape de sélection supplémentaire : \n", + "\n", + "```C\n", + "if (cond1) {\n", + " sequence1\n", + "} else if (cond2) {\n", + " sequence2\n", + "} else {\n", + " sequence3\n", + "}\n", + "```\n", + "\n", + "`sequence3` est exécuté si `cond1` et `cond2` sont fausses." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 8 - Ecrire des conditionnelles.\n", + "---\n", + "---\n", + "__[8.1]__ Compléter et corriger le corps des fonctions ci-dessous (voir TODO)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "\n", + "/**\n", + " * \\brief Retourner '<', '>' ou '=' pour indiquer si n est strictement négatif,\n", + " * strictement positif ou nul.\n", + " * \\param[in] nombre le nombre dont on veut évaluer le signe\n", + " * \\return un caractère donnant le signe d'un nombre\n", + " */\n", + "char signe(int nombre)\n", + "{\n", + " // TODO: Donner le bon code !\n", + " return '?';\n", + "}\n", + "\n", + "\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "// //\n", + "// NE PAS MODIFIER CE QUI SUIT... //\n", + "// //\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "\n", + "void test_signe() {\n", + " assert('<' == signe(-821));\n", + " assert('<' == signe(-1));\n", + " assert('=' == signe(0));\n", + " assert('>' == signe(125));\n", + " assert('>' == signe(1));\n", + " printf(\"%s\", \"signe... ok\\n\");\n", + "}\n", + "\n", + "\n", + "int main(void) {\n", + " test_signe();\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__2. La conditionelle multiple :__\n", + "Elle s'exprime avec la structure de contrôle `switch .. case`. Elle suit la syntaxe suivante : \n", + "\n", + "```C\n", + "switch (expr) {\n", + " case choix1 : \n", + " sequence1;\n", + " break;\n", + " case choix2 : \n", + " sequence2;\n", + " break;\n", + " case default : \n", + " sequence_def;\n", + "}\n", + "```\n", + "A l'exécution: \n", + "\n", + "1. `(expr)` est évalué. `(expr)` est une expression de type __discret__ (entier, booléen ou caractère)\n", + "2. L'exécution se poursuit au niveau du `case` qui correspond a la valeur de `(expr)` ou au niveau du `default` si aucune correspondance n'est trouvée. \n", + "\n", + "Autrement dit, si . \n", + "- Si `expr == choix1`, toutes les instructions sont exécutées **à partir de sequence1**. \n", + "- Si `expr == choix2`, toutes les instructions sont exécutées **à partir de sequence2**.\n", + "- Si `expr != choix1 && expr != choix2`, sequence_def est exécuté. \n", + "\n", + "Si l'instruction __break;__ est rencontrée, les instructions suivantes du bloc `switch` ne sont jamais exécutées. \n", + "\n", + "> Note : Il est important d'utiliser l'instruction __break;__ pour n'exécuter qu'une séquence par choix possible pour retrouver le comportement algorithmique d'un `Selon .. Dans`.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 9 - Comprendre le `switch ... case`\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[9.1]__ Dans la fonction `test_f` (cf fichier __`exercice9.c`__) du programme suivant, remplacer XXX par la valeur qui sera retournée par l'appel correspondant à la fonction `f`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "\n", + "#define XXX -1\n", + "\n", + "// Une fonction f qui retourne un entier en fonction du paramètre n fourni.\n", + "int f(int n) {\n", + " int r = 0;\n", + "\n", + " // modifier r\n", + " switch (n) {\n", + " case 1:\n", + " r += 1;\n", + " break;\n", + " case 2:\n", + " case 3:\n", + " r += 8;\n", + " break;\n", + " case 4:\n", + " case 5:\n", + " case 7:\n", + " r += 10;\n", + " case 10:\n", + " case 11:\n", + " r += 5;\n", + " break;\n", + " case 12:\n", + " r += 50;\n", + " break;\n", + " case 13:\n", + " r += 100;\n", + " default:\n", + " r -= 1;\n", + " }\n", + "\n", + " return r;\n", + "}\n", + "\n", + "void test_f(void)\n", + "{\n", + " assert(XXX == f(3));\n", + " assert(XXX == f(-5));\n", + " assert(XXX == f(0));\n", + " assert(XXX == f(12));\n", + " assert(XXX == f(13));\n", + " assert(XXX == f(2));\n", + " assert(XXX == f(10));\n", + " assert(XXX == f(5));\n", + "}\n", + "\n", + "int main(void) {\n", + " test_f();\n", + " printf(\"%s\", \"Bravo ! Pas d'erreur détectée.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 10 - Ecrire un `switch .. case`\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[10.1]__ Compléter et corriger le corps de la fonction `nb_jours_mois` (cf fichier __`exercice10.c`__) ci-dessous (voir TODO)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "\n", + "/**\n", + " * \\brief Obtenir le nombres de jour d'un mois d'une année non bissextile.\n", + " * \\param[in] mois le mois considéré (de 1, janvier, à 12, décembre)\n", + " * \\return le nombre de jours du mois considéré\n", + " */\n", + "int nb_jours_mois(int mois)\n", + "{\n", + " // Contraintes :\n", + " // 1. On utilisera un Selon et aucune autre structure de contrôle.\n", + " // 2. On fera un seul « return » à la fin de la fonction.\n", + " // 3. On n'utilisera pas de tableau !\n", + " // TODO: Donner le bon code !\n", + " return 0;\n", + "}\n", + "\n", + "\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "// //\n", + "// NE PAS MODIFIER CE QUI SUIT... //\n", + "// //\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "\n", + "void test_nb_jours_mois() {\n", + " assert(31 == nb_jours_mois(1));\n", + " assert(28 == nb_jours_mois(2));\n", + " assert(31 == nb_jours_mois(3));\n", + " assert(30 == nb_jours_mois(4));\n", + " assert(31 == nb_jours_mois(5));\n", + " assert(30 == nb_jours_mois(6));\n", + " assert(31 == nb_jours_mois(7));\n", + " assert(31 == nb_jours_mois(8));\n", + " assert(30 == nb_jours_mois(9));\n", + " assert(31 == nb_jours_mois(10));\n", + " assert(30 == nb_jours_mois(11));\n", + " assert(31 == nb_jours_mois(12));\n", + " printf(\"%s\", \"nb_jours_mois... ok\\n\");\n", + "}\n", + "\n", + "\n", + "int main(void) {\n", + " test_nb_jours_mois();\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Les boucles / répétitions\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### La répétition `do` ... `while`\n", + "\n", + "On répète au moins une fois une séquence. La condition d'arrêt est testée une fois la séquence exécutée. \n", + "```C\n", + " do {\n", + " sequence;\n", + " }\n", + " while (cond);\n", + "```\n", + "__Exemple__ (cf fichier __`alea_borne.c`__):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <time.h>\n", + "#include <assert.h>\n", + "/*\n", + " * \\brief Obtenir une valeur aléatoire entre min et max, inclus.\n", + " * \\param[in] min borne minimale, \n", + " * \\param[in] max borne maximale, \n", + " * \\return valeur aleatoire entre min et max\n", + " * \\pre min >= 0, max <= RAND_MAX\n", + " */ \n", + "int alea_borne(int min, int max){\n", + " assert(min >= 0);\n", + " assert(max <= RAND_MAX);\n", + " \n", + " // Initialisation du générateur de nombres aléatoires avec la date courante\n", + " srand(time(NULL));\n", + " int alea;\n", + " do {\n", + " alea = rand(); // valeur aléatoire entre 0 et RAND_MAX\n", + " }\n", + " while (alea < min || alea > max);\n", + " return alea;\n", + "}\n", + "\n", + "int main(void) {\n", + " int val = alea_borne(4, 10);\n", + " assert(val >= 4 && val <= 10);\n", + " printf(\"val = %d\\n\", val);\n", + " \n", + " val = alea_borne(2, 25);\n", + " assert(val >= 2 && val <= 25);\n", + " printf(\"val = %d\\n\", val);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### La répétition `while`\n", + "On répète une séquence qui peut ne jamais être exécutée. \n", + "On sort de la boucle quand la condition est fausse : \n", + "\n", + "```C\n", + " while (cond) {\n", + " sequence;\n", + " }\n", + "```\n", + "\n", + "__Exemple__ (cf fichier __`while.c`__):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "\n", + "#define LIMITE 300\n", + "\n", + "int main(void) {\n", + " int prec = 1, un = 2;\n", + " int rang = 2;\n", + " int nouveau;\n", + "\n", + " while (un < LIMITE) {\n", + " // Determiner le nouveau terme de la suite de Fibonacci\n", + " nouveau = un + prec;\n", + " // Enregistrer les termes un et prec\n", + " prec = un;\n", + " un = nouveau;\n", + " // Calculer le rang\n", + " rang ++;\n", + " } \n", + " printf(\"La valeur de la suite de fibonacci >= %d est %d. Elle est de rang %d\\n\", LIMITE, un, rang);\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### La répétition `for`\n", + "\n", + "Si on connait le nombre d'itérations, on utilise une boucle Pour : \n", + "```C\n", + " for (instruction_init_compteur; condition_boucle; instruction_incr_compteur) {\n", + " sequence;\n", + " }\n", + "```\n", + "\n", + "On a ici : \n", + "- `instruction_init_increment` : une instruction qui initialise (voire déclare) le compteur, \n", + "- `condition_boucle` : une condition qui, __si fausse__, arrête la répétition. \n", + "- `instruction_incr_compteur` : une instruction qui précise comment le compteur varie à chaque répétition. \n", + "\n", + "__Exemple__ (cf fichier __`for.c`__): " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#define LIMITE 30\n", + "\n", + "int main(void) {\n", + " //calcul de la moyenne des LIMITE premiers entiers\n", + " int somme = 0;\n", + " \n", + " // Déclaration du compteur i et initialisation à 1\n", + " // Répétition si i <= LIMITE\n", + " // Incrémentation i = i + 1 à chaque répétition.\n", + " for (int i = 1; i <= LIMITE; i++) {\n", + " somme += i;\n", + " }\n", + " float moyenne = somme / (float) LIMITE; \n", + " printf(\"La moyenne des %d premiers entiers est %1.2f\\n\", LIMITE, moyenne);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 11 - Ecrire un `TantQue`\n", + "---\n", + "---\n", + "\n", + "__[11.1]__ Compléter et corriger le corps de la function `sommes_cubes_inférieurs_a` (cf. fichier __`exercice11.c`__) (voir TODO)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <stdbool.h>\n", + "#include <assert.h>\n", + "\n", + "/**\n", + " * \\brief Calculer la somme des cubes des entiers naturels dont le cube est inférieur\n", + " * ou égal à limite.\n", + " * \\param[in] limite la limite à ne pas dépasser pour les cubes\n", + " * \\return la sommes des cubes\n", + " * \\pre limite positive : limite > 0\n", + " */\n", + "int sommes_cubes_inferieurs_a(int limite)\n", + "{\n", + " assert(limite >= 0);\n", + "\n", + " // Consigne :\n", + " // 1. On n'utilisera pas l'exponentielle.\n", + " // 2. On utilisera seulement un TantQue\n", + " // TODO: Donner le bon code !\n", + " return -1;\n", + "}\n", + "\n", + "\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "// //\n", + "// NE PAS MODIFIER CE QUI SUIT... //\n", + "// //\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "\n", + "\n", + "void test_sommes_cubes_inferieurs_a(void) {\n", + " assert(1 == sommes_cubes_inferieurs_a(5));\n", + " assert(1 == sommes_cubes_inferieurs_a(1));\n", + " assert(1 == sommes_cubes_inferieurs_a(7));\n", + " assert(9 == sommes_cubes_inferieurs_a(8));\n", + " assert(9 == sommes_cubes_inferieurs_a(26));\n", + " assert(36 == sommes_cubes_inferieurs_a(27));\n", + " assert(36 == sommes_cubes_inferieurs_a(63));\n", + " assert(100 == sommes_cubes_inferieurs_a(64));\n", + " assert(100 == sommes_cubes_inferieurs_a(124));\n", + " assert(225 == sommes_cubes_inferieurs_a(125));\n", + " printf(\"%s\", \"sommes_cubes_inferieurs_a... ok\\n\");\n", + "}\n", + "\n", + "\n", + "int main(void) {\n", + " test_sommes_cubes_inferieurs_a();\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 12 - Ecrire un `Répéter ... Jusqu'à`\n", + "---\n", + "---\n", + "\n", + "__[12.1]__ Compléter et corriger le corps de la function `frequence` (cf. fichier __`exercice12.c`__)(voir TODO)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <stdbool.h>\n", + "#include <assert.h>\n", + "\n", + "/**\n", + " * \\brief Obtenir la fréquence d'un chiffre dans un nombre.\n", + " * Exemples : la fréquence de 5 dans 1515 est 2. La fréquence de 3 dans 123 est 1.\n", + " * La fréquence de 0 dans 412 est 0.\n", + " * \\param[in] chiffre dont ont veut calculer la fréquence\n", + " * \\param[in] nombre pour lequel on veut calculer la fréquence de chiffre\n", + " * \\return la fréquence de chiffre dans nombre\n", + " * \\pre chiffre est un vrai chiffre : 0 <= chiffre <= 9\n", + " */\n", + "int frequence(int nombre, int chiffre)\n", + "{\n", + " assert(chiffre >= 0);\n", + " assert(chiffre <= 9);\n", + "\n", + " // TODO: Donner le bon code !\n", + " return -1;\n", + "}\n", + "\n", + "\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "// //\n", + "// NE PAS MODIFIER CE QUI SUIT... //\n", + "// //\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "\n", + "\n", + "void test_frequence(void) {\n", + " assert(2 == frequence(1515, 5));\n", + " assert(1 == frequence(123, 3));\n", + " assert(0 == frequence(421, 0));\n", + " assert(3 == frequence(444, 4));\n", + " assert(1 == frequence(0, 0));\n", + " printf(\"%s\", \"frequence... ok\\n\");\n", + "}\n", + "\n", + "\n", + "int main(void) {\n", + " test_frequence();\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 13 - Ecrire un `Pour`\n", + "---\n", + "---\n", + "\n", + "__[13.1]__ Compléter et corriger le corps de la function `frequence` (cf. fichier __`exercice13.c`__)(voir TODO)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Consigne : compléter et corriger le corps des fonctions ci-dessous (voir TODO).\n", + "\n", + "#include <stdlib.h>\n", + "#include <stdio.h>\n", + "#include <stdbool.h>\n", + "#include <assert.h>\n", + "\n", + "/**\n", + " * \\brief Calculer la somme des cubes des entiers naturels de 1 à max.\n", + " * \\param[in] max un entier naturel\n", + " * \\return la sommes des cubes de 1 à max\n", + " * \\pre max positif : max >= 0\n", + " */\n", + "int sommes_cubes(int max)\n", + "{\n", + " assert(max >= 0);\n", + "\n", + " // Consigne :\n", + " // 1. On n'utilisera pas l'exponentielle.\n", + " // 2. On utilisera seulement un Pour\n", + " // TODO: Donner le bon code !\n", + " return -1;\n", + "}\n", + "\n", + "\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "// //\n", + "// NE PAS MODIFIER CE QUI SUIT... //\n", + "// //\n", + "////////////////////////////////////////////////////////////////////////////////\n", + "\n", + "\n", + "void test_sommes_cubes(void) {\n", + " assert(1 == sommes_cubes(1));\n", + " assert(9 == sommes_cubes(2));\n", + " assert(36 == sommes_cubes(3));\n", + " assert(100 == sommes_cubes(4));\n", + " assert(225 == sommes_cubes(5));\n", + " assert(0 == sommes_cubes(0));\n", + " printf(\"%s\", \"sommes_cubes... ok\\n\");\n", + "}\n", + "\n", + "\n", + "int main(void) {\n", + " test_sommes_cubes();\n", + " printf(\"%s\", \"Bravo ! Tous les tests passent.\\n\");\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "## Exercice BILAN 1 : Conversion pouce/centimètres\n", + "---\n", + "---\n", + "Cet exercice de Bilan 1 n'est pas à rendre. Le squelette du programme est disponible dans votre répertoire SVN, et est à éditer avec un éditeur de fichier standard, et à compiler / exécuter en ligne de commande.\n", + "\n", + "__[B.1]__ Traduire l'algorithme du listing en Langage C. Il permet de convertir en pouces et en centimètres une longueur saisie en pouces, centimètres ou mètres. \n", + "\n", + "__[B.2]__ Modifier le programme pour que l’utilisateur puisse mettre des espaces (des blancs) entre la valeur et l’unité de la longueur.\n", + "\n", + "__[B.3]__ Ajouter la possibilité de recommencer.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "/** Squelette du programme **/\n", + "/*********************************************************************\n", + " * Auteur : \n", + " * Version : \n", + " * Objectif : Conversion pouces/centimètres\n", + " ********************************************************************/\n", + "\n", + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "\n", + "int main()\n", + "{\n", + "\n", + " /* Saisir la longueur */\n", + "\n", + " /* Calculer la longueur en pouces et en centimètres */\n", + "\n", + " /* Afficher la longueur en pouces et en centimètres */\n", + "\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Les types utilisateurs \n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Les types utilisateurs permettent au programmeur de définir des types plus évolués. Les 3 types en C sont :\n", + "- Les types énumérés. \n", + "- Les enregistrements.\n", + "- Les tableaux.\n", + "\n", + "Ces types se définissent au début d'un programme, avant la signature du programme principal `int main()`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Les types énumérés\n", + "Un type énuméré permet de définir un ensemble discret de valeurs possibles. \n", + "L'exemple suivant déclare un type énuméré `enum Jour` :\n", + "> `enum Jour { LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE};`\n", + "\n", + "Les constantes `LUNDI`, `MARDI`, sont des constantes entières qui vallent respectivemet 0, 1, 2, etc. \n", + "On peut donc les comparer.\n", + "\n", + "Une variable de type `enum Jour` ne peut prendre que ces valeurs.\n", + "> `enum Jour mon_jour = LUNDI; //declaration d'une variable initialsée à LUNDI`\n", + "\n", + "__Il est conseillé__ de créer un alias au type `enum Jour` à l'aide de l'instruction `typedef`\n", + "> `typedef enum Jour Jour`\n", + "Ici on a créé l'alias (le synonyme) `Jour`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Les types enregistrement \n", + "Un type enregistrement permet de déclarer une variable qui regroupe plusieurs données hétérogènes (i.e. de type différent). \n", + "En C, on le définit de la sorte : \n", + "```C\n", + "struct Date {\n", + " int jour;\n", + " Mois mois;\n", + " int annee;\n", + "};\n", + "```\n", + "> Note : ne pas oublier le ; après la dernière parenthèse.\n", + "\n", + "Le type `struct Date` est un 3-uplet qui regroupe un jour, un mois et une année. \n", + "```C\n", + " struct Date d1, d2; // déclaration de deux dates\n", + " // initialisation champ par champ\n", + " d1.jour = 30;\n", + " d1.mois = AVRIL; //ici mois est un type énuméré\n", + " d1.annee = 1997;\n", + " // initialisation directe des 3 champs\n", + " d2 = {31, DECEMBRE, 2012};\n", + "```\n", + "Il est aussi possible de créer un alias au type `struct Date` à l'aide de l'instruction `typedef`\n", + "> `typedef struct Date Date`\n", + "Ici on a créé l'alias (le synonyme) `Date`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 14 : definir et utiliser un type enregistrement\n", + "---\n", + "---\n", + "__[14.1]__ Définir un type Point qui regroupe deux coordonnées entières, X et Y. \n", + "\n", + "__[14.2]__ Ecrire un programme principal qui génère deux points ptA et ptB au coordonnées (0,0) et (10,10) respectives. Il calcule la distance entre ptA et ptB en norme Euclidienne.\n", + "\n", + "__ATTENTION__ si vous utilisez la bibliothèque `math.h`, vous devez compiler votre code en ligne de commande" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "\n", + "// Definition du type Point \n", + "\n", + "\n", + "int main(){\n", + " // Déclarer deux variables ptA et ptB de types Point\n", + " \n", + " // Initialiser ptA à (0,0)\n", + " \n", + " // Initialiser ptB à (10,10)\n", + " \n", + " // Calculer la distance entre ptA et ptB.\n", + " float distance = 0;\n", + " \n", + " assert( (int)(distance*distance) == 200);\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tableaux\n", + "Les tableaux permettent d'enregistrer __un nombre fini de données de même type__. \n", + "\n", + "#### Déclarer une variable tableau\n", + "On pourra par exemple definir une __variable tableau__ capable d'enregistrer NB entiers.\n", + "> Attention : En C, __les indices varient entre 0 et NB-1__. \n", + "\n", + "```C\n", + " #define NB 4\n", + " // déclaration d'une variable tableau de NB entiers\n", + " int tab[NB]; \n", + " // Initialisation de la 2e case : \n", + " tab[1] = 20;\n", + "\n", + " // Si on initialise à la déclaration, on n'a pas besoin de donner la taille\n", + " int tab_2[] = {1, 4, -1, 4};\n", + "```\n", + "\n", + "#### Déclarer un __type tableau__\n", + "La déclaration d'un type tableau est réalisé avec `typedef` :\n", + "```C\n", + " // declaration du type t_tab \n", + " typedef int t_tab[NB];\n", + " // declaration de variables tableau de type t_tab\n", + " t_tab tab1, tab2;\n", + " // l'accès aux données de tab1 et tab2 se fait de ma même façon : \n", + " tab1[0] = 20;\n", + "```\n", + "> __Attention !__ `tab1 = tab2` __est interdit__. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 15\n", + "---\n", + "---\n", + "(cf fichier __`exercice15.c`__)\n", + "\n", + "__[15.1]__ Definir un type `t_tableau` de réels de capacité 20. \n", + "\n", + "__[15.2]__ Completer et corriger la fonction `initialiser` qui permet d'initialiser chaque élément d'un tableau de type `t_tableau` à `0.0`.\n", + "\n", + "__[15.3]__ Completer et corriger la fonction `est_vide` qui vérifie que tous les éléments sont bien initialisés à `0.0`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "#include <stdbool.h>\n", + "\n", + "#define CAPACITE 20\n", + "// Definition du type tableau\n", + "// TODO \n", + "\n", + "/**\n", + " * \\brief Initialiser les éléments d'un tableau de réels avec 0.0\n", + " * \\param[out] tab tableau à initialiser\n", + " * \\param[in] taille nombre d'éléments du tableau\n", + " * \\pre taille <= CAPACITE\n", + " */ \n", + "void initialiser(t_tableau tab, int taille){\n", + " assert(taille <= CAPACITE);\n", + " // TODO\n", + "}\n", + "\n", + "/**\n", + " * \\brief le tableau est-il vide ?\n", + " * \\param[in out] tab tableau à tester\n", + " * \\param[in] taille nombre d'éléments du tableau\n", + " * \\pre taille <= CAPACITE\n", + " */ \n", + "bool est_vide(t_tableau tab, int taille){\n", + " assert(taille <= CAPACITE);\n", + " bool vide = false;\n", + " // TODO\n", + " return vide;\n", + "}\n", + "\n", + "int main(void){\n", + " t_tableau T;\n", + " //Initialiser les éléments d'une variable tableau à 0.0\n", + " initialiser(T, 10);\n", + " //Vérifier avec assert que tous les éléments vallent bien 0.0\n", + " assert(est_vide(T, 10));\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Chaines de caractères en C\n", + "\n", + "Les chaines de caractères sont des __tableaux de caractères__ : \n", + "\n", + "```C\n", + " char[10] ma_chaine; // un tableau de caractères de taille 10.\n", + " char[] mon_nom = \"Jaffres-Runser\"; // initialise le tableau avec une chaine constante\n", + "```\n", + "La bibliothèque `string.h` permet de manipuler les chaines de caractères :\n", + "- `strlen(s)` retourne le nombre de caractères de la chaine\n", + "- `strcpy(s1, s2)` recopie le contenu de `s1` dans `s2`. Attention, il faut que `s2` ait une capacité suffisante ! \n", + "- `strcat` concatène deux chaines, etc.\n", + "\n", + "__Exécuter__ l'exemple suivant (cf fichier __`chaine.c`__) : " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <string.h>\n", + "#include <stdio.h>\n", + "#include <stdlib.h>\n", + "\n", + "int main(void){\n", + " char mon_nom[] = \"Jaffres-Runser\"; \n", + " printf(\"Longueur de '%s' : %lu caractères\\n\", mon_nom, strlen(mon_nom));\n", + " printf(\"Taille du tableau : %lu éléments \\n\", sizeof(mon_nom));\n", + " printf(\"dernier élément : '%i' \\n\", mon_nom[sizeof(mon_nom)-1]);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Le dernier caractère d'une chaine est le caractère `\\0` de code ascii 0.\n", + "> __Règle__ : une chaine de caractère se termine toujours par le caractère `\\0`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Type pointeur et adresse mémoire\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adresse mémoire \n", + "En C, il est possible de connaitre l'adresse mémoire à laquelle est stockée une variable `var1` avec l'opérateur unaire `&` (éperluette) :\n", + "\n", + "> &var1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Déclaration d'un pointeur\n", + "Il est possible d'enregistrer cette adresse dans une variable de type **pointeur**. Une variable de type pointeur est aussi communément appelée pointeur. Elle enregistre la référence (i.e. l'adresse) d'une variable ou d'une donnée. \n", + "\n", + "Pour déclarer un pointeur en C, il faut connaitre le type de la donnée qui sera enregistrée à cette adresse.\n", + "Par exemple, on peut déclarer un pointeur sur une variable de type `entier` ou un pointeur sur une variable de type `double`. Pour déclarer un pointeur, on met * devant le nom de la variable : \n", + "\n", + "> `type_pointé* pointeur; `\n", + "\n", + "Quelques exemples : \n", + "```C\n", + " int* ptr_int; // déclaration du pointeur ptr_int sur un entier\n", + " double* ptr_dbl; // déclaration du pointeur ptr_dbl sur un double \n", + " char* ptr_char; // un pointeur sur un caractère\n", + " int** ptr_ptr_int; // un pointeur sur un pointeur, qui pointe sur un entier\n", + "```\n", + "> Note : il peut y avoir un espace avant l'opérateur *. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Initialisation d'un pointeur \n", + "\n", + "Comme pour n'importe quelle déclaration de variable en C, le pointeur n'est pas initialisé à une valeur par défaut à sa déclaration. En d'autres termes, l'adresse enregistrée n'a aucun sens, elle est aléatoire. \n", + "\n", + "On doit initialiser un pointeur soit avec :\n", + "- Le pointeur `NULL` (élément neutre des adresses possibles) :\n", + "```C\n", + " double* ptr_d = NULL;\n", + "```\n", + "\n", + "\n", + "- Soit avec l'adresse mémoire d'une variable du bon type:\n", + "```C\n", + " int var1 = 10;\n", + " int* ptr_int = &var1; // initialisation avec l'adresse de la variable var1\n", + "```\n", + "\n", + "\n", + "- Ou avec la valeur d'un pointeur de même type\n", + "```C\n", + " int* ptr_int_2 = ptr_int; \n", + "```\n", + "\n", + "> __Si l'adresse est n'est pas connue au moment de la déclaration, il faut toujours initialiser le pointeur à `NULL`__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Accès à la donnée pointée \n", + "Pour accéder à la variable pointée, on utilise aussi l'opérateur * placé avant l'identificateur.\n", + "> `*ptr_int = 25 `\n", + "\n", + "Quelques exemples :\n", + "```C\n", + " int var1 = 10;\n", + " int* ptr_int = &var1;\n", + " *ptr_int = 20 // On modifie ici la variable var1, qui vaudra 20 par la suite.\n", + " // Déclaration et initialisation d'un nouvel entier var2 avec la donnée référencée par le pointeur ptr_int\n", + " int var2 = *ptr_int ; \n", + " assert(var1 == 20);\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Affectation de pointeurs\n", + "Affecter un pointeur `p1` à un pointeur `p2`, comme pour toute affectation, recopie l'adresse `p1` dans `p2`. Les deux pointeurs référencent alors la même zone mémoire.\n", + "\n", + "Quelques exemples :\n", + "```C\n", + " int var1 = 10;\n", + " int* p1 = &var1;\n", + " int* p2 = p1;\n", + " // A cet instant, on peut modifier le contenu de var1 en passant par p1 ou par p2.\n", + " *p1 = 100; //var1 vaut 100\n", + " *p2 = 1000; //var1 vaut 1000 maintenant !\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 16 : Manipulation de pointeurs\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[16.1]__ Compiler le programme suivant (cf fichier __`pointeur1.c`__). \n", + "\n", + "- Qu'observez-vous pour le premier affichage ? Le résultat dépend du compilateur, du système, etc.\n", + "- Qu'observez-vous pour le second affichage ? " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "\n", + "int main(){\n", + " int d1 = 1;\n", + " int d2 = 4; \n", + " int* p_1 ;\n", + " int* p_2 ;\n", + " printf(\"*p_1 = %d, *p_2 = %d\\n\", *p_1, *p_2);\n", + " printf(\"p_1 = %p, p_2 = %p\", p_1, p_2);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[16.2]__ Modifier ce programme pour que `p_1` et `p_2`pointent respectivement sur `d1` et `d2` (cf fichier __`pointeur2.c`__). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "\n", + "int main(){\n", + " int d1 = 1;\n", + " int d2 = 4; \n", + " int* p_1 ;\n", + " int* p_2 ;\n", + " printf(\"*p_1 = %d, *p_2 = %d\", *p_1, *p_2);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[16.3]__ Compléter le programme pour échanger les entiers pointés par `p1` et `p2`. Après initialisation des pointeurs, on n'accèdera aux entiers qu'à travers des pointeurs (cf fichier __`pointeur3.c`__). " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "\n", + "int main(){\n", + " int d1 = 1;\n", + " int d2 = 4; \n", + " int* p_1 ;\n", + " int* p_2 ;\n", + " printf(\"Avant échange : *p_1 = %d, *p_2 = %d\", *p_1, *p_2);\n", + " \n", + " // TODO : echanger les pointeurs\n", + " \n", + " printf(\"Après échange : *p_1 = %d, *p_2 = %d\", *p_1, *p_2);\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[16.4]__ Qu'en est-il des données enregistrées dans `d1` et `d2` ? Ont-elles changé ? " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "\n", + "### Enregistrement et pointeurs\n", + "\n", + "On suppose le type enregistrement `point` suivant : \n", + "```C\n", + " struct point {\n", + " int x;\n", + " int y;\n", + " };\n", + " typedef struct point point;\n", + "```\n", + "\n", + "Un pointeur sur un enregistrement permet d'accéder au contenu de l'enregistrement de deux manières : \n", + "\n", + "1. Avec les opérateurs __* et .__ \n", + "```C\n", + " point pt1;\n", + " struct point * ptr_point = &pt1; \n", + " (*ptr_point).x = 12; \n", + " (*ptr_point).y = 0; \n", + "```\n", + "\n", + "\n", + "2. Avec l'opérateur __->__\n", + "```C\n", + " point pt1;\n", + " struct point * ptr_point = &pt1; \n", + " ptr_point->x = 12; \n", + " ptr_point->y = 0; \n", + "```\n", + ">__Règle__ : __Il faut utiliser la notation ->__ \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tableau et pointeurs\n", + "En C, le nom de la variable tableau est __l'identifiant d'un pointeur sur la première case__ du tableau. On peut donc accéder au contenu de la première case par ce pointeur.\n", + "Par exemple : \n", + "```C\n", + " int tab[] = {1, 4, 8, 16};\n", + " // tab est un pointeur sur la case 0\n", + " *tab = 20; // équivalent à tab[0] = 20\n", + "```\n", + "\n", + "Il est possible d'accéder à la case suivante __en incrémentant de 1 le pointeur__ (arithmétique des pointeurs) : \n", + "```C\n", + " *(tab+1) = 40; // équivalent à tab[1] = 40\n", + " // déclaration d'un pointeur sur la 4e case du tableau\n", + " int* ptr = tab+3;\n", + " assert(*ptr == 16);\n", + "```\n", + "\n", + "L'opérateur __-__ permet de se déplacer vers la gauche dans le tableau :\n", + "```C\n", + " ptr = ptr-2;\n", + " //ptr pointe sur la 2e case du tableau\n", + " assert(*ptr == 40);\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 17\n", + "---\n", + "---\n", + "\n", + "__[17.1]__ Ré-écrire la fonction `initialiser` de l'exercice 15 avec la notation pointeur du tableau et l'arithmétique associée. (cf fichier __`exercice17.c`__)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "#include <stdbool.h>\n", + "\n", + "#define CAPACITE 20\n", + "// Definition du type tableau\n", + "// TODO \n", + "\n", + "/**\n", + " * \\brief Initialiser les éléments d'un tableau de réels avec 0.0\n", + " * \\param[out] tab tableau à initialiser\n", + " * \\param[in] taille nombre d'éléments du tableau\n", + " * \\pre taille <= CAPACITE\n", + " */ \n", + "void initialiser(t_tableau tab, int taille){\n", + " assert(taille <= CAPACITE);\n", + " // TODO\n", + "}\n", + "\n", + "int main(void){\n", + " t_tableau T;\n", + " //Initialiser les éléments d'une variable tableau à 0.0\n", + " initialiser(T, 10);\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Les sous-programmes \n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Le langage C ne permet pas de différencier les fonctions des procédures algorithmiques. Le seul sous-programme utilisable est la fonction : \n", + "\n", + "> `type_retour identificateur_fonction ( type_param1 id_param1, type_param2 id_param2, ...)`\n", + "\n", + "Chaque paramètre formel est typé et __passé par valeur__ (mode IN algorithmique). \n", + "Ainsi, l'appel à une fonction sur des paramètres réels variables ne modifie pas la donnée. Par contre, les instructions de la fonction connaissent la donnée (valeur) et peuvent la manipuler pour fournir l'unique résulat retourné via le type retour. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Illustration du passage par valeur\n", + "Exécuter le programme suivant (cf fichier __`valeur.c`__):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdio.h>\n", + "\n", + "// Definition d'une fonction f1\n", + "int f1(int valeur) { \n", + " printf(\" valeur au début de f1 : %i \\n\", valeur);\n", + " valeur = 0;\n", + " printf(\" valeur à la fin de f1 : %i \\n\", valeur);\n", + " return valeur;\n", + "}\n", + "\n", + "int main(){\n", + " int donnee = 20;\n", + " printf(\"donnee dans main() avant f1 : %i \\n\", donnee);\n", + " int donnee_retournee = f1(donnee); // la fonction utilise la valeur de donnee\n", + " printf(\"donnee dans main() après f1 : %i \\n\", donnee);\n", + " printf(\"donnee_retournee dans main() : %i \\n\", donnee_retournee); \n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Passage par adresse\n", + "Pour pouvoir modifier le contenu d'une variable passée en paramètre d'une fonction (mode OUT ou IN OUT algorithmique), on fournit à la fontion __l'adresse de la variable__. Connaissant l'adresse, la fonction pourra alors modifier sa valeur.\n", + "\n", + "On passe l'adresse d'une variable à une fonction à l'aide d'un pointeur. Pour se faire, il faut déclarer la fonction avec des paramètres formels qui sont des pointeurs.\n", + "\n", + "Petite illustration du __passage de paramètres par adresse__ (cf fichier __`adresse.c`__):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdio.h>\n", + "\n", + "// Definition d'une fonction f1 avec un paramètre pointeur qui \n", + "// peut enregistrer d'adresse d'un entier\n", + "int f1(int* valeur) { \n", + " *valeur += *valeur; // Accès à la variable au travers du pointeur. \n", + " printf(\"valeur dans f1 après incrémentation : %i \\n\", *valeur);\n", + " return *valeur;\n", + "}\n", + "\n", + "int main(){\n", + " int donnee = 20;\n", + " // Pour utiliser la fonction, on donne l'adresse de la variable \n", + " int nouvelle_donnee = f1(&donnee); \n", + " printf(\"donnee dans main() après incrémentation : %i \\n\", donnee);\n", + " printf(\"nouvelle_donne dans main() : %i \\n\", nouvelle_donnee); \n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pour ce passage par adresse, il faut : \n", + "- utiliser des pointeurs pour définir les paramètres formels\n", + "- dans les instructions de la fonction, accéder à la donnée pointée avec l'opérateur *\n", + "- lors de l'appel du sous-programme, fournir une adresse valide d'une variable à modifier avec l'opérateur &." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Passage d'un paramètre de type tableau en C\n", + "\n", + "Un tableau étant un pointeur, le passage par valeur d'un paramètre tableau offre naturellement un passage en mode `in out`. Ainsi : \n", + "\n", + "- Il n'est pas nécessaire de passer un tableau par adresse si on souhaite in mode `in out`. \n", + "- Si on veut définir un mode `in`, il faut empêche la modification en utilisant `const` : \n", + "\n", + "```C\n", + "/*\n", + " * \\brief Affiche un tableau de taille éléments\n", + " * \\param[in] tab tableau à afficher\n", + " * \\param[in] taille nombre d'éléments du tableau\n", + " * \\pre taille <= CAPACITE\n", + " */ \n", + "void afficher_tab (const int[] tab, int taille)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 18 : passage par adresse\n", + "---\n", + "---\n", + "Compléter et corriger le programme suivant en répondant aux questions suivantes (cf fichier __`exercice18.c`__): \n", + "\n", + "__[18.1]__ Définir le type `t_note`, caractérisé par sa valeur et son coefficient. Par exemple, la note de 14 a été obtenue pour le BE d’algorithmique et programmation qui compte coefficient 1/4.\n", + "\n", + "__[18.2]__ Définir le type `t_tab_notes` qui permet d'enregistrer 5 notes. \n", + "\n", + "__[18.3]__ Compléter et corriger la fonction qui initialise une note à partir de sa valeur et de son coefficient.\n", + "\n", + "__[18.4]__ Compléter et corriger la fonction qui calcule la moyenne des notes d'un tableau de notes. \n", + ">__Attention__ il faut respecter le mode `in` du paramètre tableau." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "#include <assert.h>\n", + "#include <stdbool.h>\n", + "\n", + "// Definition du type t_note\n", + "// TODO \n", + "\n", + "// Definition d'un tableau de notes t_tab_notes de 5 éléments.\n", + "// TODO\n", + "\n", + "/**\n", + " * \\brief Initialiser une note\n", + " * \\param[out] note note à initialiser\n", + " * \\param[in] valeur nombre de points\n", + " * \\param[in] coef coefficient\n", + " * \\pre valeur <= 20 && valeur >= 0\n", + " * \\pre coef <= 1 && coef >= 0\n", + " */ \n", + "void initialiser_note(t_note note, float valeur, float coef){\n", + " assert(valeur <= 20 && valeur >= 0);\n", + " assert(coef <= 20 && coef >= 0);\n", + " // TODO\n", + "}\n", + "\n", + "\n", + "/**\n", + " * \\brief Calculer la moyenne des notes du tableau \n", + " * \\param[in] tab_notes tableau de nodes\n", + " * \\param[in] nb_notes nombre de notes\n", + " */ \n", + "float moyenne(t_tab_notes tab_notes, int nb_notes){\n", + " // TODO\n", + " return 0;\n", + "}\n", + "\n", + "\n", + "int main(void){\n", + " t_tab_notes notes;\n", + " \n", + " //Initialiser les éléments d'une variable tableau à 0.0\n", + " initialiser_note(notes[0], 10, 0.2);\n", + " initialiser_note(notes[1], 1.5, 0.3);\n", + " initialiser_note(notes[2], 12, 0.5);\n", + " \n", + " //Calculer la moyenne des 3 notes\n", + " float moy = moyenne(notes, 3);\n", + " assert( (int)(moy*100) == (int)((10*0.2 + 1*0.3 + 12*0.5)*100));\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Arguments de la ligne de commande\n", + "---\n", + "\n", + "Il est possible de fournir des arguments pour paramétrer l'exécution d'un programme. On pourra par exemple personnaliser le message affiché à l'utilisateur dans le `premier_programme` en exécutant : \n", + "\n", + "`./premier_programme Michel`\n", + "\n", + "pour qu'il présente l'affichage suivant " + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "*************************************\n", + "********* Bienvenue Michel **********\n", + "*************************************" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Pour se faire, il faut déclarer la signature du programme principal avec les paramètres `argc` et `argv[]` : " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h>\n", + "\n", + "int main(int argc, char* argv[]){\n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> `int argc` : nombre d'arguments. \n", + "\n", + "> `char* argv[]` : tableau de chaines de caractères\n", + "\n", + "La chaîne à l'indice 0 existe toujours et contient le nom de l'exécutable. \n", + "Les autres éventuelles chaines listent les arguments dans l'ordre où ils sont présentés. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "### Exercice 19 : Lister les arguments de la ligne de commande\n", + "---\n", + "---\n", + "__[19.1]__ Ecrire un programme qui permet d'afficher les arguments de la ligne de commande (cf. fichier __`exercice19.c`__)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#include <stdlib.h> \n", + "#include <stdio.h>\n", + "\n", + "int main(int argc, char* argv[]){\n", + " printf(\"Les arguments sont : \\n\");\n", + " // Afficher ici les argc arguments\n", + " \n", + " return EXIT_SUCCESS;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "---\n", + "## Exercices BILAN 2 \n", + "---\n", + "---\n", + "\n", + "\n", + "> __ATTENTION__ \n", + "> - Les deux exercices bilan suivants __sont à rendre à votre intervenant__ de TP sous SVN." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 1 : Portée et masquage des variables.\n", + "Le programme fourni suivant compile sans erreur, même avec l’option -Wall. On répondra\n", + "aux questions suivantes dans __un fichier texte `Bilan2_Exercice1.txt` prévu à cet effet sous SVN__, sans compiler ni exécuter le programme.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[B1.1]__ Quelle est la portée de chaque variable déclarée ? Pour chaque variable, on donnera le numéro de ligne où commence et se termine sa portée.\n", + "\n", + "__[B1.2]__ Y a-t-il un exemple de masquage de variable dans ce programme ?\n", + "\n", + "__[B1.3]__ Peut-on savoir ce que devrait afficher l’exécution de ce programme ?\n", + "\n", + "__[B1.4]__ Même s’il compile sans erreur, ce programme est faux. Pourquoi ?\n", + "\n", + "__[B1.5]__ La valeur de `p` change-t-elle après l’initialisation de la ligne 14 ?\n", + "\n", + "__[B1.6]__ Que se passerait-il si on modifiait `*p` après la ligne 19 ?\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercice 2 : Définition d'une monnaie.\n", + "\n", + "Dans cet exercice nous nous intéressons à la notion de monnaie. Une monnaie est caractérisée par sa valeur et sa devise. Nous considérerons que la valeur est réelle et que la devise est représentée par un caractère. La valeur d’une monnaie doit toujours être positive. Par exemple, la monnaie «cinq euros» sera représentée par la valeur 5 et le caractère « e », « dix dollars » par la valeur 10 et le caractère « $ ».\n", + "\n", + "__Vos réponses sont attendues dans un fichier `monnaie.c` sous SVN__ prévu à cet effet." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "__[B2.1]__ Définir le type `monnaie`.\n", + "\n", + "__[B2.2]__ Écrire un sous-programme qui initialise une monnaie à partir d’une valeur et d’une devise. La valeur doit être strictement positive. On utilisera la programmation par contrat pour le spécifier.\n", + "\n", + "__[B2.3]__ Écrire un sous-programme qui permet d’ajouter à une monnaie la valeur d’une autre monnaie. Les deux monnaies doivent avoir même devise pour que l’opération soit possible. Par\n", + "exemple, si on ajoute une monnaie `m1` qui vaut 5 euros à une monnaie `m2` qui vaut 7 euros alors `m2` vaut 12 euros après l’opération et `m1` est inchangée. Si les deux monnaies n’ont pas la même devise, l’opération n’aura pas lieu. \n", + "\n", + "On utilisera la programmation défensive et un code d’erreur, ici un booléen (valeur retournée) indiquera si l’opération a été réalisée ou non.\n", + "\n", + "__[B2.4]__ Écrire des sous-programmes de test des sous-programmes définis sur le type `monnaie`.\n", + "\n", + "__[B2.5]__ Écrire un programme principal qui :\n", + "1. déclare un tableau de 5 monnaies appelé porte_monnaie (5 doit être une constante préprocesseur),\n", + "2. initialise chaque élément du tableau en demandant la valeur et la devise d’une monnaie à l’utilisateur,\n", + "3. affiche la somme de toutes les monnaies qui sont dans une devise demandée à l’utilisateur.\n", + "\n", + "Il est bien entendu possible de créer des sous-programmes issus d'un raffinement du programme principal." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "C", + "language": "c", + "name": "c" + }, + "language_info": { + "file_extension": ".c", + "mimetype": "text/plain", + "name": "c" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}