Les outils de développement sous FreeBSD : Guide de l'utilisateur
Précédent |  | Suivant

# 3. Compiler avec `cc`

Cette section ne s'occupe que du compilateur GNU pour C et C++, qui est fourni de base avec le système FreeBSD. Il peut être appelé soit avec la commande `cc`, soit avec `gcc`. Les détails de la réalisation d'un programme avec un interpréteur varient considérablement d'un interpréteur à l'autre, est sont généralement bien décrits par l'aide en ligne de l'interpréteur.

Une fois que vous avez écrit votre chef-d'oeuvre, l'étape suivante consiste à le convertir en quelque chose qui (espérons-le!) s'exécutera sous FreeBSD. Cela demande habituellement plusieurs opérations successives, dont chacune est confiée à un programme différent.

1. Pré-processer votre code source pour en éliminer les commentaires et faire diverses autres choses, comme la substitution des macros-instructions en C.

2. Vérifier la syntaxe de votre code pour s'assurer que vous avez respecté les règles du langage. Si ce n'est pas le cas, il rouspétera.

3. Convertir le code source en langage assembleur - c'est très proche du code machine, mais encore compréhensible par des êtres humains. C'est du moins ce que l'on prétend[1].

4. Convertir le langage assembleur en code machine - oui, nous parlons ici de bits et d'octets, de zéros et de uns.

5. Vérifier que vous avez utilisé de façon cohérente les fonctions et les variables globales. Si, par exemple, vous avez appelé une fonction qui n'existe pas, il s'en plaindra.

6. Si vous essayez de générer un programme à partir de plusieurs fichiers de source, faire ce qu'il faut pour les regrouper.

7. S'arranger pour produire quelque chose que le chargeur de programmes du système pourra mettre en mémoire et exécuter.

Le mot compiler est souvent utilisé pour ne désigner que les étapes 1 à 4 - les autres sont appelées édition de liens. L'étape 1 est parfois appelée pré-processer et les étapes 3-4 assembler.

Heureusement, pratiquement tous ces détails vous sont transparents, car la commande `cc` est une interface qui gère pour vous l'appel de ces différents programmes avec les bons arguments; taper simplement:

    % cc foobar.c

effectue la compilation de `foobar.c` en passant par toutes les étapes décrites ci-dessus. Si vous avez à compiler plus d'un fichier, faites simplement quelque chose comme:

    % cc foo.c bar.c

Notez que la vérification syntaxique ne consiste qu'en cela: vérifier la syntaxe. Il n'y aura pas de contrôle sur les erreurs logiques que vous auriez commises, comme faire exécuter au programme une boucle infinie ou utiliser un tri à bulles au lieu d'un tri par arbre binaire[2].

`cc` dispose d'une quantité d'options, qui sont toutes décrites dans les pages de manuel. En voici quelques-unes des plus importantes, et la façon de les utiliser.

`-o `nom_de_fichier``

  Le nom du fichier résultat. Si vous n'utilisez pas cette option, `cc` générera un exécutable appelé `a.out` [3].

      % cc foobar.c               l'exécutable est a.out
      % cc -o foobar foobar.c     l'exécutable est foobar

`-c`

  Uniquement compiler le fichier, ne pas faire l'édition de liens. Utile pour des programmes d'essai, quand vous voulez simplement vérifier la syntaxe, ou si vous vous servez d'un `Makefile`.

      % cc -c foobar.c

  Cela générera un fichier objet (et non un exécutable) appelé `foobar.o`. Il pourra être lié avec d'autres fichiers objet pour constituer un exécutable.

`-g`

  Crée une version débogable de l'exécutable Le compilateur inclut alors dans l'exécutable des informations de correspondance entre les numéros de ligne du fichier source et les fonctions appelées. Un débogueur peut alors utiliser ces informations pour vous afficher le code source tandis que vous exécutez pas à pas le programme, ce qui est très utile; l'inconvénient est que toutes ces informations augmentent considérablement la taille du programme. Normalement, vous compilez avec `-g` quand vous développez le programme, et compilez ensuite une ``version de livraison'' quand vous êtes satisfait parce qu'il fonctionne correctement.

      % cc -g foobar.c

  Cela produira une version débogable du programme[4].

`-O`

  Génère une version optimisée de l'exécutable. Le compilateur effectue alors diverses opérations bien pensées pour essayer de construire un programme qui aille plus vite que normalement. Vous pouvez faire suivre `-O` d'un nombre pour demander un degré plus important d'optimisation, mais cela met souvent en évidence des bogues dans l'optimiseur du compilateur. Par exemple, on sait que la version de `cc` de FreeBSD 2.1.0 produit du code incorrect avec l'option `-O2` dans certaines circonstances.

  On n'active en général l'optimisation qu'à la compilation de la version de livraison.

      % cc -O -o foobar foobar.c

  Cela construira une version optimisée de `foobar`.

Les trois indicateurs suivants demanderont à `cc` de vérifier que votre code est conforme à la norme internationale, souvent appelée norme ANSI, bien que ce soit à proprement parler une norme ISO.

`-Wall`

  Active tous les messages d'avertissement que les auteurs du compilateur `cc` ont jugés intéressants. Malgré son nom (``all'' - tous), cela n'active pas tous les messages d'avertissement dont le compilateur est capable.

`-ansi`

  Désactive la plupart, mais pas toutes, les possibilités non-ANSI C fournies par `cc`. Malgré son nom, cela ne garantit pas absolument que votre code soit conforme à la norme.

`-pedantic`

  Désactive toutes les possibilités non-ANSI C de `cc`.

Sans ces indicateurs, `cc` vous permet d'utiliser ses extensions non-standard de la norme. Quelques-unes sont très utiles, mais ne se retrouveront pas sur d'autres compilateurs - de fait, l'un des objectifs principaux de la norme est de permettre l'écriture de code qui puissent être réutilisé avec n'importe quel compilateur sur n'importe quel système. C'est cela que l'on appelle du code portable.

En général, vous devriez vous efforcer de rendre votre code aussi portable que possible, sans quoi vous risquez de devoir récrire entièrement votre programme par la suite pour qu'il fonctionne ailleurs - et qui peut dire ce que vous utiliserez dans quelques années?

    % cc -Wall -ansi -pedantic -o foobar foobar.c

Cela générera un exécutable `foobar` après avoir vérifié que `foobar.c` respecte la norme.

`-l`bibliothèque``

  Définit une bibliothèque de fonctions à utiliser pour l'édition de liens.

  L'exemple le plus courant est la compilation d'un programme qui utilise certaines des fonctions mathématiques de C. A l'inverse de la plupart des autres plates-formes, ces fonctions sont dans une bibliothèque différente de la bibliothèque C standard et vous devez préciser au compilateur qu'il doit l'utiliser.

  La règle est que si la bibliothèque s'appelle `lib`quelque_chose`.a`, vous donnez à `cc` l'argument `-l`quelque_chose``. Par exemple, la bibliothèque mathématique s'appelle `libm.a`, vous donnez donc à `cc` l'argument `-lm`. Un détail à connaître à propos de la bibliothèque mathématique est que ce doit généralement être la dernière sur la ligne de commande.

      % cc -o foobar foobar.c -lm

  Cela ajoutera à l'édition de liens de `foobar` des fonctions de la bibliothèque mathématique.

  Si vous compilez du code C++, vous devrez ajouter `-lg++`, ou `-lstdc++` si vous utilisez la version 2.2 de FreeBSD ou une version ultérieure, à la ligne de commande pour éditer les liens avec les fonctions de la bibliothèque C++. Au lieu de cela, vous pouvez utiliser la commande `c++` au lieu de `cc`, qui fera la même chose à votre place. Sous FreeBSD, `c++` peut aussi être appelé avec `g++`.

      % cc -o foobar foobar.cc -lg++     Avec FreeBSD 2.1.6 et antérieurs
      % cc -o foobar foobar.cc -lstdc++  Avec FreeBSD 2.2 et ultérieurs
      % c++ -o foobar foobar.cc

  Chacun de ces exemples construira un exécutable `foobar` à partir du fichier source C++ `foobar.cc`. Remarquez que, sur les systèmes Unix, les fichiers sources C++ ont traditionnellement l'extension `.C`, `.cxx` ou `.cc`, plutôt que l'extension `.cpp` de sytle MS-DOS" (qui est déjà utilisée pour autre chose). `gcc` se fiait autrefois à l'extension pour savoir quel type de compilateur utiliser avec le fichier source, mais cette restriction ne s'applique plus, vous pouvez donc appeler vos fichiers C++ `.cpp` en toute impunité!

## 3.1. Questions et problèmes `cc`

Q. J'essaie d'écrire un programme qui utilise la fonction `sin()` et j'obtiens une erreur qui ressemble à ce qui suit. Qu'est-ce que cela veut dire?

    /var/tmp/cc0143941.o: Undefined symbol `_sin' referenced from text segment

R. Quand vous utilisez des fonctions mathématiques telles que `sin()`, vous devez dire à `cc` d'inclure la bibliothèque mathématique à l'édition de liens, comme ceci:

    % cc -o foobar foobar.c -lm

Q. D'accord, j'ai écrit ce petit programme pour m'entraîner à utiliser `-lm`. Il ne fait que calculer 2.1 à la puissance 6:

    #include <stdio.h>

    int main() {
          float f;

          f = pow(2.1, 6);
          printf("2.1 ^ 6 = %f\n", f);
          return 0;
    }

et l'ai compilé comme ceci:

    % cc temp.c -lm

comme vous avez dit qu'il fallait le faire, mais voilà ce que j'obtiens à l'exécution:

    % ./a.out
    2.1 ^ 6 = 1023.000000

Ce n'est pas la bonne réponse! Que se passe-t-il?

R. Quand le compilateur voit que vous appelez une fonction, il regarde s'il en a déjà vu un prototype. Si ce n'est pas le cas, il suppose que la fonction retourne un int - entier, ce qui n'est évidemment pas ce que vous souhaitez dans ce cas.

Q. Comment alors régler ce problème?

R. Les prototypes des fonctions mathématiques sont dans `math.h`. Si vous incluez ce fichier, le compilateur trouvera le prototype et cessera de vous fournir un résultat bizarre!

    #include <math.h>
    #include <stdio.h>

    int main() {
    ...

Après l'avoir recompilé de la même façon qu'auparavant, exécutez-le:

    % ./a.out
    2.1 ^ 6 = 85.766121

Si vous utilisez la moindre fonction mathématique, incluez toujours `math.h` et n'oubliez pas d'utiliser la bibliothèque mathématique à l'édition de liens.

Q. J'ai compilé un fichier appelé `foobar.c` et je ne trouve pas d'exécutable appelé `foobar`. Où est-il passé?

R. N'oubliez pas, `cc` appellera l'exécutable `a.out` à moins que vous ne lui disiez de faire autrement. Utilisez l'option `-o `nom_de_fichier``:

    % cc -o foobar foobar.c

Q. OK, si j'ai un exécutable appelé `foobar`, je le vois avec `ls`, mais quand je tape `foobar` sur la ligne de commande, il me dit que le fichier n'existe pas. Pourquoi ne le trouve-t-il pas?

R. A l'inverse de MS-DOS, Unix ne regarde pas dans le répertoire courant quand il cherche le programme que vous voulez exécuter, à moins que vous ne le lui disiez. Soit tapez `./foobar`, ce qui veut dire ``exécuter le fichier appelé `foobar` du répertoire courant'', ou modifiez votre variable d'environnement `PATH` pour qu'elle ressemble à:

    bin:/usr/bin:/usr/local/bin:.

Le dernier point signifie ``chercher dans le répertoire courant si le fichier n'est pas dans les autres répertoires''.

Q. J'ai appelé mon exécutable `test`, mais il ne se passe rien quand je le lance. Pourquoi?

R. Il y a un programme appelé `test` dans `/usr/bin` sur la plupart des systèmes Unix et c'est celui-là que trouve l'interpréteur de commandes avant de regarder dans le répertoire courant. Soit tapez:

    % ./test

ou choisissez un meilleur nom pour votre programme!

Q. J'ai compilé et tout a commencé à fonctionner correctement, puis il y a eu une erreur et il m'a dit quelque chose à propos de `core dumped`. Qu'est-ce que cela veut dire?

A. L'expression core dump date des tous premiers jours d'Unix, quand les machines utilisaient la mémoire centrale - ``core memory'' pour stocker les informations. Essentiellement, si le programme ``plantait'' dans certaines conditions, le système enregistrait sur disque le contenu de la mémoire centrale dans un fichier appelé `core`, que le programmeur pouvait ensuite disséquer pour trouver où les choses avaient mal tournées.

Q. Fascinant, mais que suis-je censé faire maintenant?

A. Servez-vous de `gdb` pour analyser l'image mémoire (Reportez-vous à la section Déboguer).

R. Quand mon programme a généré une image mémoire, il a dit quelque chose à propose de `segmentation fault` - ``erreur de segmentation''. Qu'est-ce que c'est?

Q. Cela signifie essentiellement que votre programme a essayé d'effectuer une quelconque opération illégale sur la mémoire; Unix est conçu pour protéger le système d'exploitation des programmes mal éduqués.

Les raisons les plus courantes en sont:

- Essayer d'écrire en mémoire adressée par un pointeur `NULL`, e.g.:

      char *foo = NULL;
      strcpy(foo, "bang!");

- Utiliser un pointeur qui n'a pas été initialisé, e.g.:

      char *foo;
      strcpy(foo, "bang!");

  Le pointeur aura une valeur aléatoire qui, avec de la chance, adressera une zone mémoire non accessible à votre programme, de sorte que le noyau tuera ce dernier avant qu'il ne provoque de dégât. Si vous manquez de chance, il pointera quelque part à l'intérieur de votre programme et endommagera l'une de vos structures de données, provoquant un dysfonctionnement mystérieux de votre programme.

- Tenter d'accéder au-delà du dernier élément d'un tableau, e.g.:

      int bar[20];
      bar[27] = 6;

- Essayer d'enregistrer quelque chose dans une zone de mémoire accessible en lecture seule, e.g.:

      char *foo = "Mon texte";
      strcpy(foo, "bang!");

  Les compilateurs Unix stockent souvent les chaînes de caractères constantes comme `"Mon texte"` en mémoire accessible en lecture seule.

- Utiliser incorrectement les fonctions `malloc()` et `free()`, e.g.:

      char bar[80];
      free(bar);

  ou:

      char *foo = malloc(27);
      free(foo);
      free(foo);

Commettre l'une de ces fautes ne provoquera pas toujours une erreur, mais ce sont malgré tout des choses à ne pas faire. Certains systèmes et compilateurs sont plus tolérants que d'autres, ce qui fait que des programmes qui s'exécutent correctement sur un système peuvent ne plus fonctionner sur un autre.

Q. Parfois, le programme provoque la génération d'une image mémoire avec le message `bus error`. Mon manuel Unix dit qu'il s'agit d'un erreur matériel, mais l'ordinateur fonctionne apparemment correctement. Est-ce vrai?

R. Fort heureusement, non (à moins bien sûr que vous n'ayez aussi un problème matériel). C'est habituellement une autre façon de dire que vous avez accédé incorrectement à la mémoire.

Q. Il me semble que cette histoire de core dump peut être très utile, si je peux la provoquer quand je veux. Est-ce possible, ou dois-je attendre qu'il se produise une erreur?

R. Oui, allez simplement sur une autre console ou fenêtre xterm et tapez:

    % ps

pour connaître l'IDentifiant de processus de votre programme, puis:

    % kill -ABRT pid

où ``pid`` est l'ID de processus que vous avez recherché.

C'est par exemple utile si votre programme est parti dans une boucle infinie. Au cas où votre programme piégerait les interruptions `SIGABRT`, il y a plusieurs autres signaux qui auront le même effet.

### Notes

[1] | Pour être rigoureusement exact, `cc` convertit le code source en un P-code qui lui est propre, et ne dépend pas de la machine, et non en assembleur à ce stade.
[2] | Au cas où vous ne le sauriez pas, un tri par arbre binaire est une manière efficace d'ordonner des données, ce qui n'est pas le cas du tri à bulles.
[3] | Les raisons de cela se sont perdues dans les brumes de l'histoire.
[4] | Remarquez que nous n'avons pas utilisé l'indicateur `-o` pour préciser le nom de l'exécutable, celui-ci s'appellera donc `a.out`. Générer une version débogable appelée `foobar` est laissé à titre d'exercice aux soins du lecteur!

Précédent | Sommaire | Suivant
Introduction à la programmation |  | Make

Ce document, ainsi que d'autres peut être téléchargé sur ftp.FreeBSD.org/pub/FreeBSD/doc/.

Pour toutes questions à propos de FreeBSD, lisez la documentation avant de contacter <questions@FreeBSD.org>.
Pour les questions sur cette documentation, contactez <doc@FreeBSD.org>.