Voir les cours et résoudre les problèmes en :
Le C est un langage de programmation impératif conçu pour la programmation système. Inventé au début des années 1970 avec UNIX, C est devenu un des langages les plus utilisés. De nombreux langages plus modernes se sont inspirés de sa syntaxe. Il privilégie la performance sur la simplicité de la syntaxe. [En savoir plus]
Le C++ est un langage de programmation impératif. Inventé au début des années 1980, il apporte de nouveaux concepts au langage C (les objets, la généricité), le modernise et lui ajoute de nombreuses bibliothèques. C++ est devenu l'un des langages les plus utilisés. Sa performance et sa richesse en font le langage de prédilection pour les concours. [En savoir plus]
Pascal est un langage de programmation impératif inventé dans les années 1970 dans un but d'enseignement. Quoiqu'encore utilisé à cette fin, l'absence de bibliothèque standard en limite son utilisation malgré une grande efficacité. Sa syntaxe a été reprise par d'autres langages plus modernes avec plus ou moins de succès. [En savoir plus]


Remarque : Les cours pour ce langage ne sont disponibles que jusqu'au chapitre 4, « Lecture de l'entrée ». Les corrections sont toutefois toujours fournies.
OCaml est un langage de programmation fonctionnel inventé au milieu des années 1990. Il permet aussi une programmation impérative ou objet. Il permet d'écrire des programmes courts et faciles à vérifier et est ainsi utilisé pour certains systèmes embarqués très sensibles comme ceux des avions. Il est utilisé dans l'enseignement en classes préparatoires aux grandes écoles. [En savoir plus]


Remarque : Les cours pour ce langage ne sont disponibles que jusqu'au chapitre 4, « Lecture de l'entrée ». Les corrections sont toutefois toujours fournies.
Java est un langage de programmation impératif et orienté objet. Inventé au début des années 1990, il reprend en grande partie la syntaxe du langage C++ tout en la simplifiant, au prix d'une performance un peu moins bonne. S'exécutant dans une machine virtuelle, il assure une grande portabilité et ses très nombreuses bibliothèques en font un langage très utilisé. On lui reproche toutefois la « verbosité » de son code. [En savoir plus]


Remarque : Pour un débutant souhaitant apprendre Java, nous conseillons fortement de commencer par JavaScool, plus facile à apprendre, bien que fortement similaire.
Java's Cool (alias JavaScool) est conçu spécifiquement pour l'apprentissage des bases de la programmation. Il reprend en grande partie la syntaxe de Java sur laquelle il s'appuie, mais la simplifie pour un apprentissage plus aisé. La plateforme JavaScool est accompagnée d'un ensemble d'activités diverses de découverte de la programmation. [En savoir plus]
Python est un langage de programmation impératif inventé à la fin des années 1980. Il permet une programmation orientée objet et admet une syntaxe concise et claire qui en font un langage très bien adapté aux débutants. Étant un langage interprété, il n'est cependant pas aussi performant que d'autres langages. [En savoir plus]

Note : Codes sources disponibles uniquement en C.

Décomposer pour simplifier

Les fonctions sont un outil incontournable pour produire des programmes qui peuvent être repris et étendus. Elles permettent d'une part d'éviter les répétitions de blocs d'instructions similaires, et d'autre part de décomposer le programme en éléments simples.

Pour résoudre un problème très complexe, un moyen efficace consiste à le décomposer en sous-problèmes avant de résoudre chacun des sous-problèmes indépendamment. Chaque sous-problème est alors plus simple à appréhender que le problème complet. On peut ensuite décomposer à leur tour les sous-problèmes obtenus, jusqu'à obtenir des problèmes simples à résoudre. Décomposer un problème en sous-problèmes n'est pas toujours une tâche facile, mais en le faisant, on réduit considérablement la difficulté de sa résolution.

Les fonctions permettent d'appliquer cette technique à la réalisation de programmes. Prenons par exemple le programme suivant :

#include <stdio.h>

int main()
{
   int nbColonnes, nbLignes;

   scanf("%d", &nbColonnes);
   for (int iCol = 0; iCol < nbColonnes; iCol = iCol + 1)
   {
      printf("X");
   }
   printf("\n");

   scanf("%d", &nbLignes);
   for (int iLigne = 0; iLigne < nbLignes; iLigne = iLigne + 1)
   {
      for (iCol = 0; iCol < iLigne + 1; iCol = iCol + 1)
      {
         printf("*");
      }
      printf("\n");
   }
   printf("\n");

   scanf("%d%d", &nbLignes, &nbColonnes);
   for (int iLigne = 0; iLigne < nbLignes; iLigne = iLigne + 1)
   {
      for (int iCol = 0; iCol < nbColonnes; iCol = iCol + 1)
      {
         printf("#");
      }
      printf("\n");
   }
}

Cet exemple montre qu'un source plutôt bien présenté, avec des noms de variables choisis correctement et qui réalise quelque chose de pourtant très simple, peut malgré tout devenir assez indigeste. On peut bien sûr le comprendre en le lisant entièrement ligne par ligne, mais il faut un peu de temps pour comprendre ce qu'il fait exactement. Cela reste raisonnable, car nous n'allons pas vous imposer la lecture du source d'un véritable projet, mais cela ne fait que s'aggraver avec la taille du programme si l'on ne prend pas le soin d'utiliser les fonctions.

Regardez maintenant le source suivant, qui fait exactement la même chose que l'exemple précédent, mais est cette fois organisé en fonctions :

#include <stdio.h>

void afficheLigne(int nbColonnes, char motif)
{
   for (int iCol = 0; iCol < nbColonnes; iCol = iCol + 1)
   {
      printf("%c", motif);
   }
   printf("\n");
}

void afficheTriangle(int nbLignes, char motif)
{
   for (int iLigne = 0; iLigne < nbLignes; iLigne = iLigne + 1)
   {
      int nbColonnes = iLigne + 1;
      afficheLigne(nbColonnes, motif);
   }
}

void afficheRectangle(int nbLignes, int nbColonnes, char motif)
{
   for (int iLigne = 0; iLigne < nbLignes; iLigne = iLigne + 1)
      afficheLigne(nbColonnes, motif);
}

int main()
{
   int nbColonnes, nbLignes;

   scanf("%d", &nbColonnes);
   afficheLigne(nbColonnes, 'X');

   scanf("%d", &nbLignes);
   afficheTriangle(nbLignes, '*');

   scanf("%d%d", &nbLignes, &nbColonnes);
   afficheRectangle(nbLignes, nbColonnes, '#');
}

Cette nouvelle version du même programme est bien plus facile à comprendre et bien plus agréable à lire, ceci pour plusieurs raisons.

  • Une fonction a été créée pour chacune des trois choses que fait le programme : afficher une ligne, un triangle et un rectangle. Chaque partie peut donc être lue séparément, ce qui est plus facile que de la lire au milieu d'autres instructions.
  • Le nom donné à chaque fonction décrit clairement ce qu'elle fait. Le nom des paramètres permet également de comprendre tout de suite à quoi ils correspondent. On peut ainsi lire les instructions de chaque fonction en sachant déjà ce que ces instructions sont supposées faire, ce qui aide beaucoup.
  • La fonction principale main ne contient quasiment plus que des appels à ces fonctions et est un résumé qui décrit très clairement, en quelques lignes, ce que fait l'ensemble du programme. Les noms des fonctions bien choisis font que ces lignes se comprennent tout de suite.
  • Les instructions d'affichage d'une ligne de symboles, utilisées dans les trois parties du programme, ne sont plus recopiées à chaque fois mais écrites une fois pour toutes dans la fonction afficheLigne, qui est ainsi appelée par les deux autres fonctions.

Pour obtenir un source facile à lire et à comprendre, nous vous conseillons donc d'appliquer les mêmes principes à vos programmes :

  1. consacrer une fonction à chaque action distincte ;
  2. choisir des noms de fonctions qui décrivent ce qu'elles font ;
  3. regrouper les parties de code similaires en fonctions, pour éviter les répétitions ;
  4. ne pas écrire de fonctions trop longues, éviter de dépasser 25 lignes.

Appliquer systématiquement ces conseils peut parfois sembler ennuyeux, surtout lorsque l'on est en plein dans l'écriture de code, que l'on se sent inspiré et qu'on a envie d'avancer rapidement. Le temps que l'on économise en ne prenant pas la peine d'appliquer ces divers conseils finit cependant toujours par être largement reperdu plus tard, en recherche de bogues ou modifications. L'effort consistant à bien organiser son code est en outre bien plus gratifiant que celui passé à chercher des bogues dans un source incompréhensible.

Retour sur les commentaires

On notera que dans la plupart des langages, contrairement aux blocs d'instructions, les commentaires ne peuvent pas s'imbriquer les uns dans les autres. Par exemple, ceci est invalide et ne compilera pas :

/*
   devenu inutile :
   printf("\n"); /* On passe à la ligne */
*/

On peut en revanche utiliser la compilation conditionnelle (les directives de préprocesseur) pour ignorer facilement une partie du programme :

#if 0
   printf("Instruction en attente\n");
#endif

qui sont alors imbricables. Le code dans le #if 0 sera complètement ignoré à la compilation. Dans les langages ne proposant pas la compilation conditionnelle, on peut utiliser des if normaux pour sauter une partie du programme, bien qu'il faille alors que le code à l'intérieur soit valide.

Ajouter des commentaires à votre source est un bon moyen pour le rendre plus facilement lisible par d'autres, ou par vous-même plus tard. Avoir mis des commentaires ne doit cependant pas être une excuse pour avoir par ailleurs un code difficile à lire, des noms de variables et de fonctions peu clairs, etc. Avant d'ajouter des commentaires, réfléchissez à la manière dont vous pouvez modifier votre source pour qu'il soit compréhensible de lui-même. Après seulement, envisagez l'ajout de commentaires pour expliquer ce qui n'est pas évident à la lecture du code (et ne peut être rendu évident simplement).

Évitez autant que possible de mettre des commentaires au milieu du code. En essayant d'expliquer ce que fait le source, ils ont l'inconvénient de le rendre plus long et moins facile à lire. Préférez des commentaires à l'extérieur des fonctions. Si votre fonction fait beaucoup de choses et que vous avez envie de mettre des commentaires entre les différentes parties pour expliquer ce qu'elles font, c'est probablement que votre fonction mérite d'être décomposée en sous-fonctions. Le code suivant montre comme les commentaires manquent d'intérêt et peuvent ralentir la lecture :

#include <stdio.h>

// La fonction qui suit permet d'afficher une ligne de caractères.
// Elle prend en paramètre le nombre de caractères à afficher et
// le caractère à afficher.

void afficheLigne(int nbColonnes, char motif)
{
   int iCol = 0;
   // Tant qu'on n'a pas atteint le nombre de colonnes indiqué
   while (iCol < nbColonnes)
   {
      printf("%c", motif); // On affiche le caractère
      iCol = iCol + 1; // On passe à la colonne suivante
   }
   printf("\n"); // On passe à la ligne
}
// La fonction est terminée

Les commentaires peuvent être très utiles en cours de développement, en particulier pour les fonctions qui ne sont pas terminées. Par exemple, vous pouvez insérer des commentaires aux endroits où il reste des choses à faire, à améliorer, ou à modifier. Plutôt que de remettre simplement quelque chose à plus tard, au risque de l'oublier, mettez une note en commentaire à l'endroit concerné. Une notation usuelle pour indiquer une chose restant à faire consiste à commencer le commentaire par le texte « TODO: » (qui vient de l'anglais ‘to do’ : « à faire ») :

int main()
{
   int nbColonnes;
   scanf("%d", &nbColonnes);
   // TODO: vérifier que l'on n'entre pas une valeur négative ou trop grande
   afficheLigne(nbColonnes, 'X');
}

Une autre utilisation consiste à écrire d'abord en français le pseudo-code d'une fonction, en le laissant en commentaire, pour que le programme compile toujours, en attendant de l'avoir écrite en C.

Pensez à vous inscrire pour valider les cours et résoudre les exercices.