Le langage C

Deuxième partie

par Guilhem de Wailly

Résumé

Dans cette série d'articles, nous présentons le langage C. Il ne s'agit pas de réécrire les ouvrages de référence donnés en annexe, mais de donner, au travers du C, une méthode de programmation basée sur la notion d'interface.

Au fur et à mesure de notre progression, nous décrirons les éléments indispensables du langage et conduirons le lecteur à consulter les ouvrages de références. Le but poursuivit est clairement de parvenir à construire des programmes de manière segmentée.

Introduction

Dans l'article précédent, nous avons découvert le langage C et le compilateur. Nous avons créé notre premier programme, le fameux hello World!.

Nous avons également vu comment écrire la fonction principale d'un programme C, comment déclarer une variable, l'initialiser et l'utiliser. Nous avons aussi parlé de l'affichage à l'écran.

Nous allons examiner plus en détails les fonctions d'entrée / sortie de base, l'écriture des commentaires, des itérations et des expressions de test.

Entrée / sorties console

Dans cette section, nous allons un peu développer les entrées/sorties sur console. Malheureusement, les entrées / sorties en C font appel à des fonctions qui ne sont pas triviales ; ce sont des fonctions acceptant un nombre d'argument variable. Or elles sont indispensables dès que l'on souhaite écrire des programmes d'exemple.

Nous allons nous attacher à décrire les deux principales fonctions d'entrée / sortie en donnant une explication sommaire de leur fonctionnement. Plus tard, lorsque nous aurons plus d'expérience en C, nous reviendrons sur ces fonctions plus en détails, quitte à les réécrire partiellement.

Affichage

La principale fonction qui permet d'afficher des messages à l'écran est printf (il existe d'autres moyens plus primitifs de manipuler l'écran, mais printf est le plus immédiat).

C'est une fonction dont le nombre d'argument est variable. Le premier argument est obligatoirement une chaîne de caractère appelée chaîne de formatage.

Cette chaîne est affichée telle quelle, mis à part un certain nombre de séquences de caractère qui modifient l'affichage :

Nous aurons par exemple :

        printf ("bonjour\ntous le monde!\n");
      

affichera :

        bonjour
        tout le monde!
      

        printf ("caractère %c, entier %d\n",
                'e', 123);
        caractère e, entier 123
      

        printf ("réel %f, chaîne %s\n", 123.0, "hello");
        réel 123.0, chaîne hello
      

Le langage C est très permissif : il effectue peu de contrôles.

Par exemple :

        printf ("rien\n", 1, 2, 3, 4);
        rien
      

Dans cetexemple, des arguments supplémentaires sont donnés à la fonction, mais ils sont ignorés car ils ne correspondent à aucun %.

        printf ("aléatoire %d\n");
        aléatoire 9566758
      

Là, le format spécifie un argument lu comme un entier, mais aucune valeur n'est donnée ; la valeur affichée sera aléatoire et différente à chaque appel.

        printf ("aléatoire %s\n");
        Segmentation error. Core Dumped
      

Ici, la valeur attendue est une chaîne de caractère, qui est une donnée complexe. La valeur aléatoire a peu de chance de correspondre à une chaîne de caractère en mémoire. Il a donc une erreur mémoire.

        printf ("code ascii de '%c' = %d\n", 'a', 'a');
        Code ascii de 'e' = 64
      

Enfin, nous affichons un caractère comme un entier ; la valeur affichée correspond au nombre entier qui sert à coder le caractère dans la mémoire. Cette valeur dépend de la machine utilisée et du système d'exploitation. Avec Unix, il y a de grande chance que cela corresponde au code ASCII.

Comme on le remarque, C effectue peu de contrôle. Il responsabilise le programmeur.

Lecture

La principale fonction de lecture de C est scanf.

C'est aussi une fonction ayant un nombre d'argument variable dont le premier est obligatoirement une chaîne de format. Les caractères de cette chaîne sont les caractères devant être lus. Dans cette chaîne, les séquences % doivent correspondre à des variables qui reçoivent les valeurs lues.

Ces variables doivent être préfixées par le caractère & (on verra plus tard que se sont en fait des adresses).

On aura par exemple :

        int a;

        printf ("Entrez un entier:\n");
        scanf  ("%d", & a);
        printf ("L'entier lu est %d\n", a);
      

        Entrez en entier
        123 [ENTER]
        L'entier lu est 123
      

En plaçant des caractères dans le format, cela donne :

        int a;

        printf ("Entrez  un entier");
        printf (" préfixé par x");
        scanf  ("x%d", & a);
        printf ("L'entier lu est %d\n", a);
      

        Entrez en entier préfixé par x
        x123[ENTER]
        L'entier lu est 123
      

Si on n'introduit pas le caractère x en premier, l'entier ne sera jamais lu ; en fait, la fonction scanf reste bloquée jusqu'à ce qu'elle rencontre le caractère x, suivi d'un entier.

Programme

Nous allons utiliser nos connaissances sur les entrées / sorties pour écrire un programme qui calcule la somme de deux nombres.

Plaçons le code suivant dans le fichier somme.c :

        #include 
          
        void main (void) {
          int a, b, somme;
        
          /* lecture du premier entier */
          printf ("Premier entier:\n");
          scanf  ("%d", & a);

          /* lecture du second entier */
          printf ("Second entier:\n");
          scanf  ("%d", & b);

          /* calcul de leur somme */
          somme = a + b;

          /* affichage */
          printf ("%d + %d = %d\n", a, b, somme);
        }
      

Compilons le programme, et exécutons-le :

        $ gcc somme.c ­o somme
        $ ./somme
      

        Premier nombre:
        123[ENTER]
        Second nombre:
        456[ENTER]
        123 + 456 = 579
      

Utilisez maintenant ces programmes avec d'autres entiers. Fonctionne-t-il toujours correctement ?

Itération

Le programme précédent est intéressant, mais un peu pénible à exécuter sur une séquence de nombres. Il serait pratique d'écrire une boucle dans le main afin de permettre la répétition des calculs.

while

Pour cela, il existe le mot-clef while qui s'utilise de la manière suivante :

        while (condition) instruction
      

condition est une expression. Tant que cette expression retourne un résultat non nul, l'instruction est exécutée. L'instruction est soit une expression simple, soit un bloc d'expressions entre accolades.

Ainsi,notre fonction principale devient :

        void main (void) {
          int a, b, somme = 1;

          while (somme != 0) {
            /* lecture du premier entier */
            printf ("Premier entier:\n");
            scanf  ("%d", & a);

            /* lecture du second entier */
            printf ("Second entier:\n");
            scanf  ("%d", & b);

            /* calcul de leur somme */
            somme = a + b;

            /* affichage */
            printf ("%d + %d = %d\n",a, b, somme);
          }
        }
      

Pour que la boucle s'arrête, il faut que la somme des deux entiers soit nulle. Nous évitons que ce cas ne se produise dès le démarrage en initialisant somme à 1. Remarquons au passage que les commentaires en C s'écrivent entre /* et */. Ils peuvent tenir sur plusieurs lignes.

Les expressions de tests sont :

Le langage C ne possède pas le type booléen : N'importe quel type de valeur peut jouer ce rôle. La valeur nulle est considérée comme fausse et les autres valeurs sont considérées comme vraies.

do...while

Une autre forme d'itération peut être écrite avec la forme do...while. Elle a la forme :

        do instruction while (condition);
      

Dans ce cas, l'instruction est d'abord exécutée, puis la condition est évaluée. Cette forme correspondrait mieux à nos souhaits pour la fonction principale, évitant ainsi d'initialiser la variable somme :

        void main (void) {
          int a, b, somme;
        
          do {
            /* corps identique */
          } while (somme != 0);
        }
      

for

L'instruction for est la forme la plus générale pour écrire des itérations. Sa syntaxe est la suivante :

        for (init; test; post) expression
      

L'expression init est exécutée au début de l'itération. Puis le test est évalué et si son résultat est nul, l'itération se termine. Sinon, l'expression est évaluée. Avant de recommencer l'itération, l'expression post est évaluée. L'itération recommence alors par l'évaluation du test.

Il est possible de placer plusieurs instructions séparées par des virgules dans init et post. L'expression peut être un bloc entouré d'accolades. Ces expressions peuvent aussi être vides.

Pour afficher la table de multiplication par 7, nous écririons :

        int i, n = 7;
        
        for (i = 0; i <= 10; i = i + 1) {
          printf ("%d x %d = %d\n", i, n, i * n);
        }
      

Pour écrire une boucle infinie, nous entrerions :

        for (;;) expression;
      

Ainsi, l'instruction for laisse complètement le contrôle au programmeur sur la manière d'exécuter l'itération.

L'auteur

Guilhem de Wailly (gdw at free dot fr)

Références