<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss'><id>tag:blogger.com,1999:blog-33670414</id><updated>2009-10-17T07:32:23.831+02:00</updated><title type='text'>Bien programmer</title><subtitle type='html'>Blog consacré à la programmation en langage C. Introduction astuces, pièges à éviter, programmation avancée.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bien-programmer.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/33670414/posts/default'/><link rel='alternate' type='text/html' href='http://bien-programmer.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Emmanuel Delahaye</name><uri>http://www.blogger.com/profile/17332941569421327515</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>3</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-33670414.post-116540633648566813</id><published>2006-12-06T12:58:00.000+01:00</published><updated>2007-03-05T14:53:26.030+01:00</updated><title type='text'>Un exemple de gestion de projet</title><content type='html'>&lt;span style="font-style: italic;font-size:85%;" &gt;Ce style de caractères est utilisé pour les commentaires. &lt;br /&gt;&lt;br /&gt;Afin d'illustrer l'article sur la programmation, je vais montrer pas à pas les étapes de la réalisation d'un projet. Bien sûr il s'agit d'un projet simple, pour être court.&lt;br /&gt;&lt;br /&gt;Dans le but de rester lisible et éducatif, l'application est réalisée en C standard simple. Les spécifications sont donc basées sur des concepts un peu 'rigides' (tailles fixes etc.).&lt;br /&gt;&lt;br /&gt;En situation réelle, on chercherait probablement beaucoup plus de souplesse, à condition que le surcout induit soit raisonnable. Un léger surdimensionnement bien protégé et évolutif (par une nouvelle version) est souvent plus efficace qu'un système complètement souple. Le choix doit être analysé avec le client, étudié et motivé.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;h1&gt;1 - Spécifications&lt;/h1&gt;&lt;br /&gt;&lt;br /&gt;Il s'agit de réaliser un logiciel permettant d'afficher une série de Questionnaires à Choix Multiples (QCM), de saisir les réponses et d'afficher une note sur 20 à la fin de la séquence.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;1.1 - Limites&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Les QCM sont regroupés dans un fichier 'thème'. Le nombre de questions par thème varie de 1 à 10. Le nombre de choix possibles pour une question varie de 2 à 6. Il n'y a qu'un seul choix possible.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;1.2 - Comportement, erreurs&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;Toutes les saisies sont terminées par [enter].&lt;br /&gt;&lt;br /&gt;Lors du lancement de l'application, le nom du fichier thème est demandé. Sil il n'existe pas, un message d'erreur est émis. Si on entre rien, le programme se termine.&lt;br /&gt;&lt;br /&gt;Si le format du fichier n'est pas conforme, un message d'erreur est émis.&lt;br /&gt;&lt;br /&gt;La question est présentée, ainsi que la liste des réponses possibles numérotées de 1 à 10 (maximum). La saisie de 0 permet d'interrompre le QCM.&lt;br /&gt;&lt;br /&gt;Exemple de présentation :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Quelle est la date de la bataille de Marignan ?&lt;br /&gt;&lt;br /&gt;1 - 1415&lt;br /&gt;2 - 1515&lt;br /&gt;3 - 1155&lt;br /&gt;&lt;br /&gt;Choix ou 0 pour quitter :&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Si une saisie est erronée (valeur hors limites), un message d'erreur est émis.&lt;br /&gt;&lt;br /&gt;Lorsque la série est terminée, le programme affiche les résultats. La note est affichée avec 2 décimales significatives.&lt;br /&gt;&lt;br /&gt;Exemple :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Nombre de questions       : xx&lt;br /&gt;Nombre de bonnes réponses : yy&lt;br /&gt;Note                      : zz.tt/20&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ensuite, le programme demande un nouveau nom de fichier thème. Si on entre rien, on quitte le programme.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;1.3 - Interface&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;1.3.1 - IHM&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;L'Interface Homme Machine est de type conversationnel.&lt;br /&gt;&lt;br /&gt;Les entrées s'effectuent sur l'entrée standard avec saisie obligatoire de [enter] pour valider.&lt;br /&gt;&lt;br /&gt;La correction est possible. Aucune entrée ne doit provoquer de dysfonctionnement de l'application.&lt;br /&gt;&lt;br /&gt;Les sorties se font sur la sortie standard au fil de l'eau (séquenciellement). Il n'y a ni mise en forme, ni effacement.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;1.3.2 - Fichier de thème&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Le format des données dans le fichier de thème est le suivant :&lt;br /&gt;&lt;br /&gt;Fichier texte avec des lignes de 256 caractères utiles au maximum (hors-champ et fin de ligne).&lt;br /&gt;&lt;br /&gt;Les caractères supplémentaires sont ignorés. Ils ne provoquent pas de dysfonctionnement de l'application.&lt;br /&gt;&lt;br /&gt;Le fichier est composé d'une séquence de champs. Les lignes vides sont ignorées.&lt;br /&gt;&lt;br /&gt;Chaque champ est composé d'un mot clé obligatoirement suivit d'une ligne de texte. En cas d'absence de texte, une erreur est notifiée.&lt;br /&gt;&lt;br /&gt;Les champs sont :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;[theme]&lt;/span&gt;Nom du theme du questionnaire&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;[question]&lt;/span&gt;La question posée sans ? (mis automatiquement par l'application).&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;[ok]&lt;/span&gt;La réponse correcte&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:courier new;"&gt;[ko]&lt;/span&gt;Une réponse fausse&lt;/li&gt;&lt;/ul&gt;  Le séquencement des champs est le suivant :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Le premier champ est &lt;span style="font-family:courier new;"&gt;[theme]&lt;/span&gt;. Il est obligatoire. Si il est absent, une erreur est notifiée.&lt;/li&gt;&lt;li&gt;Il est obligatoirement suivit par le champ &lt;span style="font-family:courier new;"&gt;[question]&lt;/span&gt;. Si il est absent, une erreur est notifiée.&lt;/li&gt;&lt;li&gt;Il est obligatoirement suivit par le champ &lt;span style="font-family:courier new;"&gt;[ok]&lt;/span&gt; ou &lt;span style="font-family:courier new;"&gt;[ko]&lt;/span&gt;. Si il est absent, une erreur est notifiée.&lt;/li&gt;&lt;li&gt;Il est obligatoirement suivit par le champ &lt;span style="font-family:courier new;"&gt;[ok]&lt;/span&gt; ou &lt;span style="font-family:courier new;"&gt;[ko]&lt;/span&gt;. Si il est absent, une erreur est notifiée.&lt;/li&gt;&lt;li&gt; Il ne peut y avoir qu'un &lt;span style="font-family:courier new;"&gt;[ok]&lt;/span&gt;. Si il y en a plus d'un, une erreur est notifiée.&lt;/li&gt;&lt;li&gt;Il peut y avoir jusqu'à cinq &lt;span style="font-family:courier new;"&gt;[ko]&lt;/span&gt;. Si il y en a plus de cinq, une erreur est notifiée.&lt;/li&gt;&lt;li&gt;Il peut y avoir jusqu'à dix questions. Si il y en a plus de dix, une erreur est notifiée.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Exemple de fichier de theme :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[theme]Histoire&lt;br /&gt;[question]Quelle est la date de la bataille de Marignan&lt;br /&gt;[ko]1415&lt;br /&gt;[ok]1515&lt;br /&gt;[ko]1155&lt;br /&gt;[question]Quelle est la date de la prise de la Bastille&lt;br /&gt;[ko]4 Aout 1789&lt;br /&gt;[ko]14 Juillet 1790&lt;br /&gt;[ok]14 Juillet 1789&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Il est facile de réaliser un fichier thème avec un simple éditeur de texte.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;C'est la fin (provisoire) de la spécification. Comme dans la réalité, il se peut qu'il manque des éléments, ou qu'il y ait des incohérences fonctionnelles, mais il faut bien commencer un jour. Si des modifications sont apportées, elles sont listées dans un journal des modifications et reportées dans le texte directement.&lt;br /&gt;&lt;br /&gt;On peut maintenant passer à la phase de conception&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;h1&gt;2 - Conception&lt;/h1&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;Il s'agit d'un programme assez simple qui peut se résumer à un algorithme :&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;2.1 - Comportement global de l'application&lt;/h2&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;DEBUT&lt;br /&gt; BOUCLE&lt;br /&gt;  Demander le nom du fichier theme&lt;br /&gt;  SI le nom est vide&lt;br /&gt;   quitter&lt;br /&gt;  SINON&lt;br /&gt;   charger le fichier theme&lt;br /&gt;   BOUCLE&lt;br /&gt;    afficher le theme, la question et les reponses&lt;br /&gt;    demander une reponse&lt;br /&gt;    SI la reponse est 0&lt;br /&gt;     quitter&lt;br /&gt;    SINON&lt;br /&gt;     evaluer le resultat&lt;br /&gt;     incrementer le compteur de question et de bonnes reponses&lt;br /&gt;     lire la suite&lt;br /&gt;    FIN&lt;br /&gt;   FIN&lt;br /&gt;   calculer la note&lt;br /&gt;   afficher le resultat&lt;br /&gt;  FIN&lt;br /&gt; FIN&lt;br /&gt;FIN&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Une analyse rapide de cet algorithme fait apparaitre les blocs fonctionnels suivants :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;SD : Saisie de données (nom du theme, numero de reponse)&lt;/li&gt;&lt;li&gt;GF : Gestion du fichier (verification, lecture des QCM)&lt;/li&gt;&lt;li&gt;AQ : Affichage d'un QCM&lt;/li&gt;&lt;li&gt;GC : Gestion des compteurs&lt;/li&gt;&lt;li&gt;ER : Evaluation de la reponse&lt;/li&gt;&lt;li&gt;CN : Calcul de la note&lt;/li&gt;&lt;li&gt;PN : Présentation de la note&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;A chaque BF est attribué un groupe de deux lettres utilisé comme prefixe aux identificateurs d'objets.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Le traitement étant synchrone (question/réponse), il n'y a qu'un processus.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;2.2 - Gestion des données&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;&lt;br /&gt;On peut maintenant se demander si on charge les données en mémoire ou si on lit le QCM au fur et à mesure, à la volée. Les possibilités doivent être analysées, les performances comparées et un décision doit être prise.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Le fichier de données est lu séquenciellement. Les informations sont affichées au fur et à mesure de l'avancement du QCM. Les seules données mémorisées sont les états de la réponse (ok/ko). Cela permet au programme de savoir si la réponse est bonne.&lt;br /&gt;&lt;br /&gt;Un tableau de 10 booléens suffit donc.&lt;br /&gt;&lt;br /&gt;Un compteur de questions et un compteur de bonnes réponses sont aussi gérés au fur et à mesure de l'avancement du QCM. Ces compteurs sont de type entier.&lt;br /&gt;&lt;br /&gt;La calcul de la note utilise une regle de 3 :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;            nombre de réponses justes&lt;br /&gt;note = 20 x -------------------------&lt;br /&gt;               nombre de questions&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Le calcul s'effectuant sur des nombres réels, des précautions de codage et d'affichage doivent être prises.&lt;br /&gt;&lt;br /&gt;&lt;h1&gt;3 - Réalisation&lt;/h1&gt;&lt;br /&gt;&lt;br /&gt;Le langage utilisé est le C. Le développement est organisé de la façon suivante :&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Un fichier main.c qui contient l'algorithme de l'application&lt;/li&gt;&lt;li&gt;Un fichier par BF avec son header&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;Pour une application simple comme celle-ci, ça peut sembler démesuré, et ça l'est. mais il s'agit de montrer les bonnes pratiques permettant de réaliser un véritable projet industriel pouvant impliquer des dizaines de BF comprenant chacun des dizaines de fonctions...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;3.1 Codage de l'algorithme principal&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* main.c */&lt;br /&gt;#include &lt;stdlib.h&gt;&lt;br /&gt;#include "sd.h"&lt;br /&gt;#include "aq.h"&lt;br /&gt;#include "er.h"&lt;br /&gt;#include "gc.h"&lt;br /&gt;#include "cn.h"&lt;br /&gt;#include "an.h"&lt;br /&gt;#include "data.h"&lt;br /&gt;&lt;br /&gt;int main (void)&lt;br /&gt;{&lt;br /&gt;   do&lt;br /&gt;   {&lt;br /&gt;      char theme[32] ;&lt;br /&gt;&lt;br /&gt;      SD_get_string("Fichier theme", theme, sizeof theme);&lt;br /&gt;      if (*theme == 0)&lt;br /&gt;      {&lt;br /&gt;         exit (0);&lt;br /&gt;      }&lt;br /&gt;      else&lt;br /&gt;      {&lt;br /&gt;         struct data data;&lt;br /&gt;         GF_open (&amp;data, theme);&lt;br /&gt;         int end;&lt;br /&gt;         do&lt;br /&gt;         {&lt;br /&gt;            end = GF_read_qcm (&amp;data, theme);&lt;br /&gt;            if (!end)&lt;br /&gt;            {&lt;br /&gt;               AQ_display_qcm (&amp;data);&lt;br /&gt;               int response = SD_get_integer("choix");&lt;br /&gt;               if (response == 0)&lt;br /&gt;               {&lt;br /&gt;                  exit (0);&lt;br /&gt;               }&lt;br /&gt;               else&lt;br /&gt;               {&lt;br /&gt;                  ER_result(&amp;data, response);&lt;br /&gt;                  GC_update(&amp;data);&lt;br /&gt;               }&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;         while (!end);&lt;br /&gt;         GF_close (&amp;data);&lt;br /&gt;         CN_compute (&amp;data);&lt;br /&gt;         AN_display (&amp;data);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;   while (1);&lt;br /&gt;   return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;A ce stade d'avancement du projet, le code n'est pas testable, ni même compilable. C'est en quelque sorte un pseudo-code. De toutes façons, il représente la phase 4, c'est à dire l'intégration. Son rôle consiste à fixer (provisoirement) le développement et à montrer clairement les blocs fonctionnels.&lt;br /&gt;&lt;br /&gt;Il faut maintenant écrire et tester unitairement chaque BF.&lt;br /&gt;&lt;br /&gt;On commence par définir un embryon de &lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;struct data&lt;/span&gt;&lt;span style="font-style: italic;font-size:85%;" &gt; que l'on complètera au fur et à mesure des besoins :&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#ifndef H_ED_DATA_20060913232412&lt;br /&gt;#define H_ED_DATA_20060913232412&lt;br /&gt;/* data.h &lt;br /&gt;&lt;br /&gt;   Log&lt;br /&gt;   15-09-2006 Ajout de min et max&lt;br /&gt;   14-09-2006 creation&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;struct data&lt;br /&gt;{&lt;br /&gt;   char theme[256];&lt;br /&gt;&lt;br /&gt;   int min;&lt;br /&gt;   int max;&lt;br /&gt;   /* ... */&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;#endif /* guard */&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3.2 Le bloc fonctionnel "Saisie de Données (SD)"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;3.2 Le bloc fonctionnel "Saisie de Données (SD)"&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;Un bloc fonctionnel se traite comme un mini projet, avec specification, conception codage et test. Le test correspond au Test unitaire&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;3.2.1 Specification&lt;/h3&gt;&lt;br /&gt;Le but de ce bloc fonctionnel est de fournir à l'application les services de saisies de données sécurisées. Les besoins concernent :&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;La saisie de chaine : SD_get_string()&lt;/li&gt;&lt;li&gt;La saisie d'entiers : SD_get_integer()&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;3.2.1.1 SD_get_string()&lt;/h4&gt;&lt;br /&gt;Affichage d'un prompt et attente de la saisie d'une chaine sur l'entrée standard validée par la touche &amp;lt;enter&amp;gt;. La correction est possible par la touche &amp;lt;backspace&amp;gt;. La taille maximale est déterminée. Les caractères hors limites sont ignorés.&lt;br /&gt;&lt;br /&gt;Le premier paramètre est l'adresse de la chaine 'prompt' à afficher. La fonction ajoute les caractères " : ".&lt;br /&gt;&lt;br /&gt;Le 2ème paramètre est l'adresse du tableau de char dans lequel la chaine saisie sera stockée. La taille utile du tableau ne doit pas être inférieure à la valeur passée en 3ème paramètre.&lt;br /&gt;&lt;br /&gt;Le 3ème paramètre indique la taille maximale de la chaine à saisir (0 final inclu).&lt;br /&gt;&lt;br /&gt;Aucune valeur n'est retournée. Le prototype est :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;void SD_get_string (char const *prompt, char *s, size_t size);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;3.2.1.2 SD_get_integer()&lt;/h4&gt;&lt;br /&gt;Affichage d'un prompt et attente de la saisie d'un entier sur l'entrée standard validée par la touche &amp;lt;enter&amp;gt;. La correction est possible par la touche &amp;lt;backspace&amp;gt;. Les valeurs minimales et maximales sont déterminées. Les caractères hors limites sont ignorés et un message est affiché, ainsi qu'un invitation à re-saisir la valeur. Idem pour les valeurs hors limites. Attention, tant que la saisie est incorrecte, la fonction redemande la valeur.&lt;br /&gt;&lt;br /&gt;Le premier paramètre est l'adresse de la chaine 'prompt' à afficher. La fonction ajoute les caractères " : ".&lt;br /&gt;&lt;br /&gt;Le 2ème paramètre est la valeur minimale autorisée.&lt;br /&gt;&lt;br /&gt;Le 3ème paramètre est la valeur maximale autorisée.&lt;br /&gt;&lt;br /&gt;La fonction retourne la valeur valide saisie. Le prototype est :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;int SD_get_integer (char const *prompt, int min, int max);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;3.2.2 Conception&lt;/h3&gt;&lt;br /&gt;La saisie est basée sur la fonction standard fgets(), suivie d'une fonction qui supprime le '\n' si il est présent et purge les éventuels caractères non lus. Une fois la chaine nettoyée, celle-ci est prête pour la fonction SD_get_string(). Pour SD_get_integer(), une conversion en entier signé est appliquée avec strtol(). Les erreurs de format et de limites sont détectées et signalées sur la sortie standard. Tant que la saisie est incorrecte, celle-ci est redemandée.&lt;br /&gt;&lt;h3&gt;3.2.3 Codage&lt;/h3&gt;&lt;br /&gt;Voici l'interface et l'implémentation du BF SD.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#ifndef H_ED_SD_20060913232333&lt;br /&gt;#define H_ED_SD_20060913232333&lt;br /&gt;/* sd.h */&lt;br /&gt;&lt;br /&gt;#include &amp;lt;stddef.h&amp;gt;&lt;br /&gt;&lt;br /&gt;void SD_get_string(char const *prompt, char *s, size_t size);&lt;br /&gt;int SD_get_integer(char const *prompt, int min, int max);&lt;br /&gt;&lt;br /&gt;#endif /* guard */&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* sd.c&lt;br /&gt;&lt;br /&gt;   log&lt;br /&gt;&lt;br /&gt;   05-03-2007 mise au point. test des limites&lt;br /&gt;   07-10-2006 mise au point. efface errno.&lt;br /&gt;   15-09-2006 Passage au test unitaire. Mise au point.&lt;br /&gt;   15-09-2006 creation&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;#include "sd.h"&lt;br /&gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;limits.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;&lt;br /&gt;static void clean (char *s, FILE * fp)&lt;br /&gt;{&lt;br /&gt;   /* chercher le '\n' */&lt;br /&gt;   char *p = strchr (s, '\n');&lt;br /&gt;   if (p != NULL)&lt;br /&gt;   {&lt;br /&gt;      /* si on l'a trouve, on l'elimine. */&lt;br /&gt;      *p = 0;&lt;br /&gt;   }&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;      /* sinon, la saisie est incomplete.&lt;br /&gt;         Les caracteres restants sont lus (le flux est 'purge')&lt;br /&gt;       */&lt;br /&gt;      int c;&lt;br /&gt;      while ((c = fgetc (fp)) != '\n' &amp;&amp; c != EOF)&lt;br /&gt;      {&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void SD_get_string (char const *prompt, char *s, size_t size)&lt;br /&gt;{&lt;br /&gt;   printf ("%s : ", prompt);&lt;br /&gt;   fflush (stdout);&lt;br /&gt;&lt;br /&gt;   fgets (s, size, stdin);&lt;br /&gt;   clean (s, stdin);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int SD_get_integer (char const *prompt, int min, int max)&lt;br /&gt;{&lt;br /&gt;   int n = 0;&lt;br /&gt;   int err;&lt;br /&gt;&lt;br /&gt;   do&lt;br /&gt;   {&lt;br /&gt;      err = 1;&lt;br /&gt;      errno = 0;&lt;br /&gt;      printf ("%s : ", prompt);&lt;br /&gt;      fflush (stdout);&lt;br /&gt;      {&lt;br /&gt;         char s[16];&lt;br /&gt;         fgets (s, sizeof s, stdin);&lt;br /&gt;         clean (s, stdin);&lt;br /&gt;&lt;br /&gt;         if (*s != 0)&lt;br /&gt;         {&lt;br /&gt;            char *p_end;&lt;br /&gt;            long x = strtoul (s, &amp;p_end, 10);&lt;br /&gt;            if (*p_end == 0 &amp;&amp; INT_MIN &amp;lt;= n &amp;&amp; n &amp;lt;= INT_MAX&lt;br /&gt;                &amp;&amp; errno != ERANGE)&lt;br /&gt;            {&lt;br /&gt;               if (min &amp;lt;= x &amp;&amp; x &amp;lt;= max)&lt;br /&gt;               {&lt;br /&gt;                  n = (int) x;&lt;br /&gt;                  err = 0;&lt;br /&gt;               }&lt;br /&gt;               else&lt;br /&gt;               {&lt;br /&gt;                  printf ("saisie hors limites (%d %d)\n", min, max);&lt;br /&gt;               }&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;               printf ("saisie erronee\n");&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;         else&lt;br /&gt;         {&lt;br /&gt;            printf ("saisie erronee\n");&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;   while (err);&lt;br /&gt;   return n;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;Ce code semble correct en ce qui concerne l'écriture, il compile sans erreurs ni avertissements en mode sevère (gcc : -Wall -Wextra -O2), mais son comportement n'a pas été vérifié. Il faut maintenant écrire une petite application de test qui permet de vérifier le fonctionnement et tester le code en situation normale, limite et erronée. C'est le rôle du test unitaire&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;3.2.4 Test unitaire&lt;/h3&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;Afin de ne pas tomber dans le cercle vicieux du "mais comment est testé le programme de test ?", celui-ci doit être réalisé de façon la plus simple et la plus directe possible, sans 'astuces' dangereuses et, si nécessaire, en réutilisant du code validé.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Voici un test basique permettant de vérifier le fonctionnement de la fonction SD_get_string().&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* tu_sd.c&lt;br /&gt;&lt;br /&gt;   Test unitaire pour le BF QCM.SD&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;#include "sd.h"&lt;br /&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;&lt;br /&gt;int main (void)&lt;br /&gt;{&lt;br /&gt;   char s[16];&lt;br /&gt;&lt;br /&gt;   do&lt;br /&gt;   {&lt;br /&gt;      SD_get_string ("test", s, sizeof s);&lt;br /&gt;&lt;br /&gt;      printf ("'%s'\n", s);&lt;br /&gt;   }&lt;br /&gt;   while (*s != 0);&lt;br /&gt;&lt;br /&gt;   return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;Il permet de réaliser le "test du singe" et de s'assurer que le comportement reste stable, même dans les pires conditions.&lt;br /&gt;&lt;br /&gt;Cependant, on préfère qualifier la fonction avec un test plus, disons, orthodoxe, pour ne pas dire scientifique. Une des méthodes consiste à ecrire un fichier texte qui servira d'entrée au programme via une indirection (stdin), et un fichier de sortie de référence comprenant les données attendues sur stdout. Les sorties du programme sont alors enregistrées dans un fichier texte de sortie via une indirection  (stdout). Le fichier de sortie est alors comparé au fichier de référence. Si ils sont identiques, le test est satisfaisant.&lt;br /&gt;&lt;br /&gt;L'ensemble peut être écrit dans un batch, un script, ou un programme C (ou autre, Python, etc.).&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Exemple de fichier de test (entrées)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;C:\dev\qcm&gt;type sd_in.txt&lt;br /&gt;a&lt;br /&gt;abcd&lt;br /&gt;abcd efg&lt;br /&gt;012345678901234567890123456789012345678901234567890123456789&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Exemple de fichier de test (sorties attendues)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;C:\dev\qcm&gt;type sd_out.txt&lt;br /&gt;test : 'a'&lt;br /&gt;test : 'abcd'&lt;br /&gt;test : 'abcd efg'&lt;br /&gt;test : '012345678901234'&lt;br /&gt;test : ''&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Exemple de batch (Mode commande de Windows)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;C:\dev\qcm&gt;type tusd.bat&lt;br /&gt;tu_sd.exe &amp;lt; sd_in.txt &amp;gt; out.txt&lt;br /&gt;fc sd_out.txt out.txt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Exemple d'execution&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;C:\dev\qcm&gt;tusd&lt;br /&gt;&lt;br /&gt;C:\dev\qcm&gt;tu_sd.exe  0&amp;lt;sd_in.txt 1&amp;gt;out.txt&lt;br /&gt;&lt;br /&gt;C:\dev\qcm&gt;fc sd_out.txt out.txt&lt;br /&gt;Comparaison des fichiers sd_out.txt et OUT.TXT&lt;br /&gt;FC : aucune différence trouvée&lt;br /&gt;&lt;br /&gt;C:\dev\qcm&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Le test unitaire est étendu à la vérification de la fonction SD_get_integer()&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* tu_sd.c&lt;br /&gt;&lt;br /&gt;   Test unitaire pour le BF QCM.SD&lt;br /&gt;   &lt;br /&gt;   Log&lt;br /&gt;   &lt;br /&gt;   15-06-2006 ajoute TU pour SD_get_integer()&lt;br /&gt;   15-06-2006 creation&lt;br /&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;#include "sd.h"&lt;br /&gt;#include &amp;lt;stdio.h&gt;&lt;br /&gt;&lt;br /&gt;void tu_string(void)&lt;br /&gt;{&lt;br /&gt;   char s[16];&lt;br /&gt;   do&lt;br /&gt;   {&lt;br /&gt;      SD_get_string ("test s", s, sizeof s);&lt;br /&gt;&lt;br /&gt;      printf ("'%s'\n", s);&lt;br /&gt;   }&lt;br /&gt;   while (*s != 0);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void tu_integer(void)&lt;br /&gt;{&lt;br /&gt;   int n;&lt;br /&gt;   do&lt;br /&gt;   {&lt;br /&gt;      n = SD_get_integer ("test i", 0, 4);&lt;br /&gt;      printf ("'%d'\n", n);&lt;br /&gt;   }&lt;br /&gt;   while (n != 0);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int main (void)&lt;br /&gt;{&lt;br /&gt;   tu_string();&lt;br /&gt;   tu_integer();&lt;br /&gt;&lt;br /&gt;   return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Le fichier d'entrée est&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;a&lt;br /&gt;abcd&lt;br /&gt;abcd efg&lt;br /&gt;012345678901234567890123456&lt;br /&gt;&lt;br /&gt;1&lt;br /&gt;4&lt;br /&gt;1234&lt;br /&gt;9999999&lt;br /&gt;99999999&lt;br /&gt;-1&lt;br /&gt;-9999999&lt;br /&gt;azer&lt;br /&gt;12az&lt;br /&gt;&lt;br /&gt;0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Le fichier de sortie attendu est&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;test s : 'a'&lt;br /&gt;test s : 'abcd'&lt;br /&gt;test s : 'abcd efg'&lt;br /&gt;test s : '012345678901234'&lt;br /&gt;test s : ''&lt;br /&gt;test i : '1'&lt;br /&gt;test i : '4'&lt;br /&gt;test i : saisie hors limites (0 4)&lt;br /&gt;test i : saisie hors limites (0 4)&lt;br /&gt;test i : saisie hors limites (0 4)&lt;br /&gt;test i : saisie hors limites (0 4)&lt;br /&gt;test i : saisie hors limites (0 4)&lt;br /&gt;test i : saisie erronee&lt;br /&gt;test i : saisie erronee&lt;br /&gt;test i : saisie erronee&lt;br /&gt;test i : '0'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Le resultat est conforme.&lt;br /&gt;&lt;br /&gt;Le bloc fonctionnel SD est donc déclaré vérifié. Il peut être intégré à l'application.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;A suivre ...&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33670414-116540633648566813?l=bien-programmer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bien-programmer.blogspot.com/feeds/116540633648566813/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=33670414&amp;postID=116540633648566813' title='9 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/33670414/posts/default/116540633648566813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/33670414/posts/default/116540633648566813'/><link rel='alternate' type='text/html' href='http://bien-programmer.blogspot.com/2006/12/un-exemple-de-gestion-de-projet.html' title='Un exemple de gestion de projet'/><author><name>Emmanuel Delahaye</name><uri>http://www.blogger.com/profile/17332941569421327515</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03274073801737427006'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-33670414.post-115788916569541366</id><published>2006-09-10T13:41:00.000+02:00</published><updated>2006-11-30T23:08:04.153+01:00</updated><title type='text'>Initiation à la programmation</title><content type='html'>&lt;div style="text-align: left;"&gt;&lt;span style="font-size:130%;"&gt;Qu'est-ce que la programmation ?&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;blockquote&gt;"La programmation est l'art d'écrire un programme !"&lt;/blockquote&gt;&lt;br /&gt;Malheureusement, force est de constater, que ce soit chez les étudiants ou même chez les professionnels, que parfois, les programmes sont mal conçus, mal écrits, peu robustes, ou qu'ils ne correspondent pas aux désirs du client.&lt;br /&gt;&lt;br /&gt;Il est des domaines d'application où ce genre de comportement est inacceptable (avionique, médical, nucléaire). D'une façon générale, un programme instable ou peu performant est extrêmement mal perçu par le client, et l'image de l'entreprise ou la réputation de l'auteur s'en trouve largement ternie. Il faut donc prendre des mesures pour éviter ça.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"La &lt;span style="font-style: italic;"&gt;bonne &lt;/span&gt;programmation est l'art d'écrire &lt;span style="font-style: italic;"&gt;correctement &lt;/span&gt;un programme !"&lt;/blockquote&gt;&lt;br /&gt;Le secret de la réussite d'un projet informatique tient en trois points essentiels :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Une conception solide&lt;/li&gt;&lt;li&gt;Une réalisation impeccable&lt;/li&gt;&lt;li&gt;Une vérification profonde&lt;/li&gt;&lt;/ul&gt;Il s'agit donc de mettre en oeuvre, de la meilleure façon possible, la succession d'étapes qui permet de traduire une application en un langage compréhensible par une machine. Les étapes principales sont :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;La définition du projet (que veux-t-on faire ?)&lt;/li&gt;&lt;li&gt;La conception du projet (comment allons-nous le faire ?)&lt;/li&gt;&lt;li&gt;La réalisation (transcription du comportement en algorithmes, automates, fonctions)&lt;/li&gt;&lt;li&gt;Le codage dans un langage de programmation (C, Ada, Pascal, PhP etc.)&lt;/li&gt;&lt;li&gt;La traduction en langage machine (assembleur, compilateur, interpréteur)&lt;/li&gt;&lt;/ul&gt;L'ensemble de ses opérations s'inscrit dans le schéma plus général de la gestion d'un projet qui se décompose en 5 phases principales :&lt;br /&gt;&lt;ol&gt;&lt;li&gt;La définition&lt;/li&gt;&lt;li&gt;La conception&lt;/li&gt;&lt;li&gt;La réalisation&lt;/li&gt;&lt;li&gt;L'intégration&lt;/li&gt;&lt;li&gt;La validation&lt;/li&gt;&lt;/ol&gt;La programmation concerne principalement les phases 1 à 4 de la gestion d'un projet, avec une insistance particulière sur le point 3. Elle consiste donc à maitriser l'ensemble de ces phases.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;1 - La définition&lt;/span&gt;&lt;br /&gt;C'est l'étape initiale qui permet de définir le projet. Par définition, on entend :&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;La spécification des interfaces (IHM, autres machines, fichiers)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;La spécification des comportements (Normal, Extrême, Hors limite)&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size:100%;"&gt;La specification des performances minimales attendues&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:130%;"&gt;2 - La conception&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Cette étape découle obligatoirement de la précédente. L'ensemble des caractéristiques spécifiées est regroupé en grandes entités appelées &lt;span style="font-weight: bold;"&gt;blocs fonctionnels (BF)&lt;/span&gt;, puis, chaque BF est découpé en entités plus petites selon une hiérarchie de type arborescente.&lt;br /&gt;&lt;br /&gt;Les comportements sont décrits en détail par des algorithmes ou des machines à états. Les interfaces sont pris en compte. Des choix technologiques peuvent être effectués (qui fait quoi, matériel, logiciel). Il peut être choisi de recourir à des technologies plus ou moins existantes ou innovantes. Ces choix sont importants et déterminent en grande partie la réussite du projet.&lt;br /&gt;&lt;br /&gt;La phase de conception est difficile et nécessite une bonne expérience et une certaine intuition. A ma connaissance, il n'existe pas de recette miracle ni de méthode toute faite pour réaliser une bonne conception&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-size:130%;"&gt;3 - La réalisation&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;C'est ici qu'intervient tout l'art du programmeur. Les phases précédentes sont essentielles, mais n'ont fait que préparer le terrain. Il s'agit maintenant de passer au choses sérieuses, au concret, au réel.&lt;br /&gt;&lt;br /&gt;La conception fixe le cadre du développement. Elle défini les sous ensembles, les blocs fonctionnels et les comportements détaillés.&lt;br /&gt;&lt;br /&gt;Une bonne méthode de réalisation d'un composant logiciel quelconque consiste à écrire les tests unitaires qui vérifient la conformité du composant en terme d'interface, de comportement et de performances, puis d'écrire le composant lui-même, et de vérifier le comportement au fur et à mesure.&lt;br /&gt;&lt;br /&gt;Une bonne réalisation est aussi basée sur une connaissance approfondie du langage que l'on utilise. En effet, il est absolument primordial de s'assurer qu'en aucun cas, il ne puisse se produire de comportement indéfini (&lt;span style="font-style: italic;"&gt;Undefined Behaviour&lt;/span&gt; ou UB). Le UB est la plaie du programmeur. Il en suffit d'un pour semer le doute sur la fiabilité du programme en entier.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(204, 0, 0); font-weight: bold;"&gt;Il faut donc être absolument sûr que chaque ligne de code est écrite dans le respect absolu de la définition du langage. C'est particulièrement vrai avec des langages 'souples' et au typage faible  comme le C.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Il est important de souligner que les conséquences d'un UB étant imprévisibles par  définition, il est illusoire de compter sur les tests pour le révéler. Encore une fois, seule une excellente maitrise du langage, éventuellement confirmée par une lecture croisée (travail en binôme) est à même de garantir la qualité d'un code en terme d'absence de comportements indéfinis.&lt;br /&gt;&lt;br /&gt;Ensuite, le test sert à vérifier la conformité du code aux spécifications (nominales, extrêmes, hors limites).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;4 - Intégration&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;C'est l'assemblage des composants et vue de réaliser le produit final. Les composants étant testés et réputés fiables, seuls les comportements globaux sont testés.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;5 - Validation&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;C'est tout simplement la vérification de la conformité du produit à ses spécifications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33670414-115788916569541366?l=bien-programmer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bien-programmer.blogspot.com/feeds/115788916569541366/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=33670414&amp;postID=115788916569541366' title='12 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/33670414/posts/default/115788916569541366'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/33670414/posts/default/115788916569541366'/><link rel='alternate' type='text/html' href='http://bien-programmer.blogspot.com/2006/09/initiation-la-programmation.html' title='Initiation à la programmation'/><author><name>Emmanuel Delahaye</name><uri>http://www.blogger.com/profile/17332941569421327515</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03274073801737427006'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-33670414.post-115706627588554419</id><published>2006-09-01T01:15:00.001+02:00</published><updated>2009-10-17T07:32:23.847+02:00</updated><title type='text'>Introduction</title><content type='html'>Bonjour,&lt;br /&gt;&lt;br /&gt;En prolongement de  mon site &lt;a href="http://mapage.noos.fr/emdel/index.htm"&gt;http://www.bien-programmer.fr/&lt;/a&gt; consacré à la programmation, je crée ce blog destiné à améliorer l'interactivité avec le site.&lt;br /&gt;&lt;br /&gt;Voici quelles seront les têtes de chapitres&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Initiation à la programmation&lt;/li&gt;&lt;li&gt;Notions d'algorithmique&lt;/li&gt;&lt;li&gt;Initiation au langage C&lt;/li&gt;&lt;li&gt;Organisation du développement&lt;br /&gt; &lt;/li&gt;&lt;li&gt;Algorithmique en C&lt;/li&gt;&lt;li&gt;Pièges à éviter en C&lt;/li&gt;&lt;li&gt;C avancé&lt;/li&gt;&lt;li&gt;Programmation système&lt;/li&gt;&lt;/ul&gt;A bientôt&lt;br /&gt;&lt;br /&gt;Emmanuel Delahaye&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/33670414-115706627588554419?l=bien-programmer.blogspot.com'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bien-programmer.blogspot.com/feeds/115706627588554419/comments/default' title='Publier les commentaires'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=33670414&amp;postID=115706627588554419' title='4 commentaires'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/33670414/posts/default/115706627588554419'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/33670414/posts/default/115706627588554419'/><link rel='alternate' type='text/html' href='http://bien-programmer.blogspot.com/2006/09/introduction.html' title='Introduction'/><author><name>Emmanuel Delahaye</name><uri>http://www.blogger.com/profile/17332941569421327515</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='03274073801737427006'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></entry></feed>