Livre de chevet du développeur FreeBSD
Précédent | Chapitre 5. Outils de programmation | Suivant

# 5.4. Compiler avec `cc`

Cette section traite uniquement du compilateur GNU pour C et C++, celui-ci faisant partie du système FreeBSD de base. Il peut être invoqué soit par `cc` ou `gcc`. Les détails de production d'un programme avec un interpréteur varient considérablement d'un interpréteur à l'autre, et sont habituellement bien couverts par la documentation et l'aide en ligne de l'interpréteur.

Une fois que vous avez écrit votre chef d'oeuvre, la prochaine étape est de le convertir en quelque chose qui s'exécutera (espérons !) sur FreeBSD. Cela implique normalement plusieurs étapes, réalisées chacune par un programme différent.

1. Pré-traiter votre code source pour retirer les commentaires et faire d'autres trucs comme développer (expanser) les macros en C.

2. Vérifier la syntaxe de votre code source pour voir si vous avez obéi aux règles du langage. Si vous ne l'avez pas fait, il se plaindra !

3. Convertir le code source en langage assembleur-- cela est vraiment proche du code machine, mais reste compréhensible par des humains. Prétendument. [1]

4. Convertir le langage assembleur en code machine --ouais, on parle de bits et d'octets, de uns et de zéros.

5. Vérifier que vous avez utilisé des choses comme des fonctions et des variables globales de façon consistente. Par exemple, si vous avez appelé une fonction inexistente, le compilateur se plaindra.

6. Si vous essayez de produire un exécutable depuis plusieurs fichiers de code source, résoudre comment les faire fonctionner ensemble.

7. Résoudre comment produire quelque chose que le chargeur au vol du système sera capable de charger en mémoire et exécuter.

8. Finalement, écrire l'exécutable dans le système de fichiers.

Le mot compilation est souvent utilisé pour les étapes 1 à 4 seules--les autres correspondent au terme liaison. Quelquefois, l'étape 1 est appelée pre-traitement et les étapes 3-4 assemblage.

Heureusement, la plupart de ces détails vous sont cachés, étant donné que `cc` est un frontal qui s'occupe d'appeler tous les programmes avec les arguments corrects pour vous; tapez simplement

    % cc foobar.c

compilera `foobar.c` avec toutes les étapes au-dessus. Si vous avez plus d'un fichier à compiler, faites simplement quelque chose comme

    % cc foo.c bar.c

Notez que la vérification de syntaxe n'est que cela--vérifier la syntaxe. Cela ne vérifiera pas les erreurs de logique que vous pouvez avoir faites, comme mettre le programme en boucle infinie ou utiliser un tri à bulles quand vous devriez utiliser un tri binaire. [2]

Il y a beaucoup d'options pour `cc`, qui qui se trouvent toutes dans les pages de manuel en ligne. Voici quelques unes des plus importantes, avec des exemples illustrant leur utilisation.

`-o `nom_du_fichier``

  Le nom de sortie du fichier. Si vous n'utilisez pas cette option, `cc` produira 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`

  Compile juste le fichier, ne le lie pas. Utile pour les programmes jouets dont vous voulez juste vérifier la syntaxe, ou si vous utilisez un `Makefile`.

      % cc -c foobar.c

  Cela va produire un fichier objet (pas un exécutable) appelé `foobar.o`. Celui-ci peut être lié ensuite avec d'autres fichiers objets pour produire un exécutable.

`-g`

  Crée une version de déverminage de l'exécutable. Cela oblige le compilateur à placer des informations dans l'exécutable comme telle ligne du fichier source correspond à tel appel de fonction. Un dévermineur peut utiliser cette information pour vous montrer le code source au fur et à mesure que vous avancez pas à pas dans le programme, ce qui est très utile; le désavantage est que toutes ces informations supplémentaires rendent le programme plus gros. Normalement, vous compilez avec l'option `-g` quand vous êtes en train de développer un programme et compilez ensuite une “version de production” sans `-g` quand vous êtes satisfait du fonctionnement.

      % cc -g foobar.c

  Cela va produire une version de déverminage du programme foobar. [4]

`-O`

  Crée une version optimisée de l'exécutable. Le compilateur effectue différents trucs malins pour essayer de produire un exécutable qui s'exécute plus rapidement que normal. Vous pouvez ajouter un nombre après l'option `-O` pour spécifier un niveau d'optimisation plus important, mais cela vous expose souvent aux bogues dans l'optimiseur du compilateur. Par exemple, la version de `cc` fournit avec la version 2.1.0 FreeBSD est connue pour produire du mauvais code avec l'option `-O2` dans certaines circonstances.

  L'optimisation est habituellement activée uniquement lors de la compilation d'une version de production.

      % cc -O -o foobar foobar.c

  Cela va produire une version optimisée de `foobar`.

Les trois prochaines options vont forcer `cc` à vérifier que votre code est conforme au standard international en cours, se référant souvent à la norme ANSI, qui pour dire précisement est un standard ISO.

`-Wall`

  Active tous les avertissements que les auteurs de `cc` pensent valoir le coup. Malgré le nom, il n'active pas tous les avertissements dont `cc` est capable.

`-ansi`

  Désactive la plupart, mais pas toutes, des caractéristiques du C fournies par `cc` qui sont non-ANSI . Malgré le nom, cela ne garantit pas strictement que votre code sera conforme au standard.

`-pedantic`

  Désactive toutes les caractéristiques de `cc` qui ne sont pas ANSI .

Sans ces options, `cc` vous permettrait d'utiliser quelques extensions au standard non-standards. Quelques unes de celles-ci sont très utiles, mais ne fonctionneront pas avec d'autres compilateurs --en fait, un des principaux buts du standard est de permettre aux gens d'écrire du code qui fonctionnera avec n'importe quel compilateur sur n'importe quel système. Cela est connu sous le nom de code portable.

Generalement, vous devriez essayer de faire votre code aussi portable que possible, sinon vous pourriez avoir a ré-écrire totalement votre programme plus tard pour le faire fonctionner autre part--et qui sait ce que vous utiliserez dans quelques années?

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

Cela produira un exécutable `foobar` après avoir vérifié que `foobar.c` est conforme au standard.

`-l`librairie``

  Spécifie une librairie de fonctions à utiliser lors de l'édition des liens.

  L'exemple le plus commun de cela est lors de la compilation d'un programme qui utilise quelques fonctions mathématiques en C. A l'inverse de la plupart des plateformes, celles-ci se trouvent dans une librairie standard du C et vous devez dire au compilateur de l'ajouter.

  La règle est que si une librairie est appelée `lib`quelque_chose`.a`, vous donnez à `cc` l'argument `-l`quelque_chose``. Par exemple, la librairie des fonctions mathématiques est `libm.a`, aussi vous donnez à `cc` le paramètre `-lm`. Un “piège” habituel avec la librairie math est qu'elle doit être la dernière sur la ligne de commande.

      % cc -o foobar foobar.c -lm

  Cela va lier les fonctions de la librairie math à l'interieur de `foobar`.

  Si vous compilez du C++; vous devrez ajouter `-lg++`, ou `-lstdc++` si vous utilisez FreeBSD 2.2 ou ultérieur, à la ligne de commande de `cc` pour lier avec les fonctions de la librairie C++. Alternativement, vous pouvez utiliser `c++` plutôt que `cc`, qui fait tout cela pour vous. `c++` peut aussi être invoqué par `g++` sur FreeBSD.

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

  Chacune de ces commandes va produire un exécutable `foobar` à partir du fichier source C++ `foobar.cc`. Notez que, sur les systèmes Unix , les fichiers source C++ se terminent traditionnellement en `.C`, `.cxx` ou `.cc`, plutôt que le style MS-DOS `.cpp` (qui était déjà utilisé pour autre chose). `gcc` a utilisé cela pour trouver le type de compilateur à utiliser sur le fichier source; toutefois, cette restriction ne s'applique plus, aussi vous pouvez maintenant appeler vos fichiers C++ `.cpp` en toute impunité !

## 5.4.1. Questions et problèmes usuels sur `cc`

5.4.1.1. J'essaye d'écrire un programme qui utilise la fonction `sin()` et je reçois l'erreur suivante. Que cela signifie-t-il ?
5.4.1.2. J'ai écrit un programme simple pour m'exercer à l'utilisation de l'option `-lm`. Tout ce qu'il fait est d'élever 2,1 à la puissance 6.
5.4.1.3. Alors comment puis-je le réparer?
5.4.1.4. J'ai compilé un fichier appelé `foobar.c` et je ne trouve pas d'exécutable appelé `foobar`. Où est-il parti?
5.4.1.5. OK, j'ai un exécutable appelé `foobar`, je peux le voir en exécutant `ls`, mais quand je tape `foobar` à l'invite de commandes, la réponse est qu'il n'y a pas de tel fichier. Pourquoi le système ne le trouve pas?
5.4.1.6. J'ai appelé mon exécutable `test`, mais rien ne se passe quand je l'exécute. Que se passe-t-il?
5.4.1.7. J'ai compilé mon programme et il semble fonctionner au premier abord, puis il y a une erreur et le système a dit quelque chose comme “`core dumped`”. Que cela signifie-t-il?
5.4.1.8. Fascinant, mais que suis-je supposé faire ?
5.4.1.9. Quand mon programme a généré un fichier `core`, il a parlé d'une erreur “`segmentation fault`”. Qu'est-ce que c'est ?
5.4.1.10. Des fois quand je reçoit une erreur “`core dump`”, il est précisé “`bus error`”. Il est dit dans mon livre Unix qu'il s'agit d'un problème matériel mais l'ordinateur semble toujours fonctionner. Est-ce vrai ?
5.4.1.11. Toute cette affaire de fichier `core` semble être assez utile, si je peux le faire apparaître quand je le désire. Puis-je faire cela, ou dois-je attendre la prochaine erreur ?

5.4.1.1. J'essaye d'écrire un programme qui utilise la fonction `sin()` et je reçois l'erreur suivante. Que cela signifie-t-il ?

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

Lors de l'utilisation des fonctions mathématiques comme `sin()`, vous devez dire à `cc` de lier avec la librairie math, comme :

    % cc -o foobar foobar.c -lm

5.4.1.2. J'ai écrit un programme simple pour m'exercer à l'utilisation de l'option `-lm`. Tout ce qu'il fait est d'élever 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;
    }

j'ai effectué la compilation comme suit :

    % cc temp.c -lm

et comme expliqué ici, mais j'obtiens ceci quand je l'exécute :

    % ./a.out
    2.1 ^ 6 = 1023.000000

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

Quand le compilateur voit que vous appelez une fonction, il vérifie s'il a déjà un prototype pour celle-ci. S'il ne l'a pas vu, il suppose que la fonction retourne un int, ce qui n'est absolument pas ce que vous voulez ici.

5.4.1.3. Alors comment puis-je le réparer?

Les prototypes des fonctions mathématiques sont dans `math.h`. Si vous incluez ce fichier, le compilateur sera capable de trouver le prototype et il arrêtera de faire des trucs étranges à vos calculs!

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

    int main() {
    ...

Après avoir recompilé comme précédemment, exécutez :

    % ./a.out
    2.1 ^ 6 = 85.766121

Si vous utilisez quelques fonctions mathématiques que ce soit, incluez toujours `math.h` et n'oubliez pas de lier avec la librairie math.

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

Souvenez-vous, `cc` appellera l'exécutable `a.out` sauf si vous lui dites de faire autrement. Utilisez l'option `-o `nomfichier``:

    % cc -o foobar foobar.c

5.4.1.5. OK, j'ai un exécutable appelé `foobar`, je peux le voir en exécutant `ls`, mais quand je tape `foobar` à l'invite de commandes, la réponse est qu'il n'y a pas de tel fichier. Pourquoi le système ne le trouve pas?

A l'inverse de MS-DOS, Unix ne regarde pas dans le répertoire courant lorsqu'il essaye de trouver un exécutable que vous voulez exécuter, sauf si vous lui avez dit de le faire. Vous pouvez soit taper `./foobar`, ce qui signifie “exécute le fichier nommé `foobar` dans le répertoire courant”, soit changer votre variable d'environnement `PATH` de façon à ce qu'elle ressemble à quelque chose comme

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

Le point à la fin signifie “regarde dans le repertoire courant s'il n'est dans aucun autre”.

5.4.1.6. J'ai appelé mon exécutable `test`, mais rien ne se passe quand je l'exécute. Que se passe-t-il?

La plupart des systèmes Unix ont un programme appelé `test` dans `/usr/bin` et l'interpréteur prend celui-ci avant de vérifier dans le répertoire courant. Soit vous tapez

    % ./test

soit vous choisissez un meilleur nom pour votre programme !!

5.4.1.7. J'ai compilé mon programme et il semble fonctionner au premier abord, puis il y a une erreur et le système a dit quelque chose comme “`core dumped`”. Que cela signifie-t-il?

Le nom core dump date des tous premiers jours d'Unix, quand les machines utilisaient la mémoire centrale[5]pour stocker les données. Simplement, si le programme a échoué sous certaines conditions, le système va écrire le contenu de la mémoire centrale sur le disque dans un fichier appelé `core`, que le programmeur peut ensuite examiner de près pour trouver ce qui s'est mal passé.

5.4.1.8. Fascinant, mais que suis-je supposé faire ?

Utilisez `gdb` pour analyser le fichier `core` (voir Section 5.6).

5.4.1.9. Quand mon programme a généré un fichier `core`, il a parlé d'une erreur “`segmentation fault`”. Qu'est-ce que c'est ?

Cela signifie simplement que votre programme a essayé d'effectuer une opération illégale sur la mémoire; Unix est conçu pour protéger le système d'exploitation et les autres programmes des programmes crapuleux.

Les causes habituelles de cela sont :

- Essayer d'écrire dans un pointeur `NULL`, par exemple

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

- Utiliser un pointeur qui n'a pas été initialisé, par exemple

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

  Le pointeur va avoir une valeur aléatoire qui avec de la chance, pointera dans une zone de la mémoire qui n'est pas disponible pour votre programme et le noyau va tuer votre programme avant qu'il ne fasse des dommages. Si vous êtes malchanceux, il pointera quelque part dans votre propre programme et altèrera une de vos structures de données, faisant planter votre programme mystérieusement.

- Essayer d'accèder la mémoire au-delà de la fin d'un tableau, par exemple

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

- Essayer de stocker quelque chose dans la mémoire en lecture seule, par exemple

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

  Les compilateurs Unix mettent souvent les chaînes comme `"Ma chaine"` dans des zones de mémoire en lecture seule.

- Faire des trucs sales avec `malloc()` et `free()`, par exemple

      char bar[80];
      free(bar);

  ou

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

Faire une de ces fautes ne conduit pas toujours à une erreur, mais elles sont toujours de mauvais entrainements. Certains systèmes et compilateurs sont plus tolérants que d'autres, ce qui explique pourquoi des programmes qui fonctionnent bien sur un système peuvent planter si vous les essayer sur un autre.

5.4.1.10. Des fois quand je reçoit une erreur “`core dump`”, il est précisé “`bus error`”. Il est dit dans mon livre Unix qu'il s'agit d'un problème matériel mais l'ordinateur semble toujours fonctionner. Est-ce vrai ?

Non, heureusement non (sauf si bien sûr vous avez réellement un problème matériel...). Cela est habituellement une manière de dire que vous avez accédé à la mémoire d'une façon que vous n'auriez pas dû.

5.4.1.11. Toute cette affaire de fichier `core` semble être assez utile, si je peux le faire apparaître quand je le désire. Puis-je faire cela, ou dois-je attendre la prochaine erreur ?

Oui, ouvrez une autre console ou xterm, faites

    % ps

pour trouver l'identifiant du processus de votre programme, et faites

    % kill -ABRT identifiant

où ``identifiant`` est l'identifiant du processus que vous avez trouvé.

Ceci est utile si votre programme est bloqué dans une boucle infinie, par exemple. Si votre programme arrive à bloquer le signal `SIGABRT`, il y a d'autres signaux qui ont des effets similaires.

Alternativement, vous pouvez créer un fichier `core` depuis votre programme, en appelant la fonction `abort()`. Voir la page de manuel en ligne de abort(3) pour en savoir plus.

Si vous voulez créer un fichier `core` depuis l'extérieur de votre programme, mais ne voulez pas que le processus s'arrêt,vous pouvez utiliser le programme `gcore`. Voir la page de manuel en ligne de gcore(1) pour plus d'informations.

### Notes

[1] | Pour être vraiment précis, `cc` convertit à ce niveau le code source dans son propre p-code indépendant de la machine plutôt qu'en langage assembleur.
[2] | Au cas où vous ne le sauriez pas, un tri binaire est un manière efficace de trier les éléments et le tri à bulles n'en est pas une.
[3] | Les raisons de ceci sont enterrées dans les brumes de l'histoire.
[4] | Notez, nous n'avons pas utilisé l'option `-o` pour spécifier le nom de l'exécutable, aussi nous obtiendrons un exécutable du nom de `a.out`. Produire une version de déverminage du nom de `foobar` est laissé en exercice pour le lecteur!
[5] | NDT: je n'ai pas trouvé de meilleure traduction pour core memory

Précédent | Sommaire | Suivant
Introduction à la programmation | Niveau supérieur | 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>.