Recherche…


Introduction

Le langage C est traditionnellement un langage compilé (par opposition à interprété). Le standard C définit les phases de traduction , et le produit de leur application est une image de programme (ou un programme compilé). En , les phases sont listées au § 5.1.1.2.

Remarques

Extension de nom de fichier La description
.c Fichier source. Contient généralement des définitions et du code.
.h En tête de fichier. Contient généralement des déclarations.
.o Fichier objet. Code compilé en langage machine.
.obj Extension alternative pour les fichiers objets.
.a Fichier de bibliothèque. Paquet de fichiers objet.
.dll Bibliothèque de liens dynamiques sous Windows.
.so Objet partagé (bibliothèque) sur de nombreux systèmes de type Unix.
.dylib Bibliothèque de liens dynamiques sur OSX (variante Unix).
.exe , .com Fichier exécutable Windows. Formé en liant des fichiers d'objet et des fichiers de bibliothèque. Dans les systèmes de type Unix, il n'y a pas d'extension de nom de fichier spécial pour le fichier exécutable.
Drapeaux de compilation POSIX c99 La description
-o filename Nom du fichier de sortie, par ex. ( bin/program.exe , program )
-I directory rechercher des en-têtes dans le direrctory .
-D name définir le name macro
-L directory rechercher des bibliothèques dans le directory .
-l name bibliothèque de liens libname .

Les compilateurs sur les plates-formes POSIX (Linux, mainframes, Mac) acceptent généralement ces options, même si elles ne s'appellent pas c99 .

Drapeaux GCC (GNU Compiler Collection) La description
-Wall Active tous les messages d'avertissement communément acceptés pour être utiles.
-Wextra Permet plus de messages d'avertissement, peut être trop bruyant.
-pedantic Forcer les avertissements lorsque le code viole la norme choisie.
-Wconversion Activer les avertissements sur la conversion implicite, utilisez-les avec précaution.
-c Compile les fichiers source sans liaison.
-v Imprime les informations de compilation.
  • gcc accepte les drapeaux POSIX et beaucoup d'autres.
  • De nombreux autres compilateurs sur les plates-formes POSIX ( clang , compilateurs spécifiques à un fournisseur) utilisent également les indicateurs répertoriés ci-dessus.
  • Voir également Invoquer GCC pour de nombreuses autres options.
Drapeaux TCC (Tiny C Compiler) La description
-Wimplicit-function-declaration Avertir de la déclaration de fonction implicite.
-Wunsupported Avertissez à propos des fonctionnalités GCC non prises en charge qui sont ignorées par TCC.
-Wwrite-strings Les constantes de chaîne doivent être de type const char * au lieu de char *.
-Werror Abandonner la compilation si des avertissements sont émis.
-Wall Activer tous les avertissements, sauf les -Werror , -Wunusupported et -Wwrite strings .

Le lieur

Le travail de l'éditeur de liens consiste à lier un groupe de fichiers objets (fichiers .o ) à un exécutable binaire. Le processus de liaison implique principalement la résolution des adresses symboliques en adresses numériques . Le résultat du processus de liaison est normalement un programme exécutable.

Pendant le processus de liaison, l'éditeur de liens récupère tous les modules d'objet spécifiés sur la ligne de commande, ajoute un code de démarrage spécifique au système et tente de résoudre toutes les références externes du module objet avec des définitions externes dans d'autres fichiers objets peut être spécifié directement sur la ligne de commande ou peut être implicitement ajouté via des bibliothèques). Il va ensuite affecter des adresses de chargement pour les fichiers objet, c'est-à-dire qu'il spécifie où le code et les données vont se retrouver dans l'espace adresse du programme fini. Une fois qu'il a les adresses de chargement, il peut remplacer toutes les adresses symboliques dans le code objet par des adresses numériques "réelles" dans l'espace adresse de la cible. Le programme est prêt à être exécuté maintenant.

Cela inclut à la fois les fichiers objet créés par le compilateur à partir de vos fichiers de code source et les fichiers objets pré-compilés pour vous et collectés dans des fichiers de bibliothèque. Ces fichiers portent des noms se .a par .a ou .so , et vous n'avez normalement pas besoin de les connaître, car l'éditeur de liens sait où se trouvent la plupart d'entre eux et les relie automatiquement si nécessaire.

Invocation implicite de l'éditeur de liens

Comme le pré-processeur, l'éditeur de liens est un programme distinct, souvent appelé ld (mais Linux utilise collect2 , par exemple). Tout comme le pré-processeur, l'éditeur de liens est automatiquement appelé pour vous lorsque vous utilisez le compilateur. Ainsi, la manière normale d'utiliser l'éditeur de liens est la suivante:

% gcc foo.o bar.o baz.o -o myprog

Cette ligne indique au compilateur de relier trois fichiers objets ( foo.o , bar.o et baz.o ) dans un fichier exécutable binaire nommé myprog . Maintenant, vous avez un fichier appelé myprog que vous pouvez exécuter et qui, espérons-le, fera quelque chose de cool et / ou utile.

Invocation explicite de l'éditeur de liens

Il est possible d'appeler directement l'éditeur de liens, mais cela est rarement conseillé et est généralement très spécifique à la plate-forme. C'est-à-dire que les options qui fonctionnent sous Linux ne fonctionneront pas nécessairement sous Solaris, AIX, macOS, Windows et tout autre plate-forme. Si vous travaillez avec GCC, vous pouvez utiliser gcc -v pour voir ce qui est exécuté en votre nom.

Options pour l'éditeur de liens

L'éditeur de liens prend également des arguments pour modifier son comportement. La commande suivante indique à gcc de lier foo.o et bar.o , mais inclut également la bibliothèque ncurses .

% gcc foo.o bar.o -o foo -lncurses

C'est en fait (plus ou moins) équivalent à

% gcc foo.o bar.o /usr/lib/libncurses.so -o foo

(bien que libncurses.so puisse être libncurses.a , qui est juste une archive créée avec ar ). Notez que vous devez lister les bibliothèques (par chemin ou via les options -lname ) après les fichiers objets. Avec les bibliothèques statiques, l'ordre dans lequel elles sont spécifiées est important. souvent, avec les bibliothèques partagées, l'ordre n'a pas d'importance.

Notez que sur de nombreux systèmes, si vous utilisez des fonctions mathématiques (à partir de <math.h> ), vous devez spécifier -lm pour charger la bibliothèque de mathématiques, mais Mac OS X et macOS Sierra ne le requièrent pas. Il existe d'autres bibliothèques qui sont des bibliothèques distinctes sur Linux et d'autres systèmes Unix, mais pas sur les threads macOS - POSIX, et les bibliothèques POSIX en temps réel et réseau sont des exemples. Par conséquent, le processus de liaison varie entre les plates-formes.

Autres options de compilation

C'est tout ce que vous devez savoir pour commencer à compiler vos propres programmes C. En règle générale, nous vous recommandons également d'utiliser l'option de ligne de commande -Wall :

% gcc -Wall -c foo.cc

L'option -Wall fait que le compilateur vous avertit des constructions de code légales mais douteuses, et vous aidera à détecter beaucoup de bogues très tôt.

Si vous voulez que le compilateur vous envoie plus d'avertissements (y compris les variables déclarées mais non utilisées, en oubliant de retourner une valeur, etc.), vous pouvez utiliser cet ensemble d'options, car -Wall , malgré le nom, ne tourne pas tous les avertissements possibles sur:

% gcc -Wall -Wextra -Wfloat-equal -Wundef -Wcast-align -Wwrite-strings -Wlogical-op \
>     -Wmissing-declarations -Wredundant-decls -Wshadow …

Notez que clang a une option -Weverything qui -Weverything vraiment tous les avertissements dans le clang .

Types de fichier

La compilation de programmes C nécessite de travailler avec cinq types de fichiers:

  1. Fichiers source : ces fichiers contiennent des définitions de fonctions et portent des noms se .c par .c par convention. Remarque: .cc et .cpp sont des fichiers C ++; pas de fichiers C
    par exemple, foo.c

  2. Fichiers d'en-tête : ces fichiers contiennent des prototypes de fonctions et diverses instructions de pré-traitement (voir ci-dessous). Ils sont utilisés pour permettre aux fichiers de code source d'accéder à des fonctions définies en externe. Les fichiers d'en-tête se terminent par .h par convention.
    par exemple, foo.h

  3. Fichiers d'objets : Ces fichiers sont produits en tant que sortie du compilateur. Ils sont constitués de définitions de fonctions sous forme binaire, mais ils ne sont pas exécutables par eux-mêmes. Les fichiers objets se terminent par .o bien que sur certains systèmes d'exploitation (par exemple Windows, MS-DOS), ils se terminent souvent par .obj .
    par exemple, foo.o foo.obj

  4. Exécutables binaires : Ils sont produits en tant que sortie d'un programme appelé "linker". Le lieur relie un certain nombre de fichiers objets pour produire un fichier binaire pouvant être directement exécuté. Les exécutables binaires n'ont pas de suffixe spécial sur les systèmes d'exploitation Unix, bien qu'ils se terminent généralement par .exe sous Windows.
    par exemple, foo foo.exe

  5. Bibliothèques : Une bibliothèque est un fichier binaire compilé mais n'est pas en soi un exécutable (c.-à-d. Qu'il n'y a pas de fonction main() dans une bibliothèque). Une bibliothèque contient des fonctions pouvant être utilisées par plusieurs programmes. Une bibliothèque doit être livrée avec des fichiers d'en-tête contenant des prototypes pour toutes les fonctions de la bibliothèque. Ces fichiers d'en-tête doivent être référencés (par exemple, #include <library.h> ) dans tout fichier source utilisant la bibliothèque. L'éditeur de liens doit alors être référé à la bibliothèque pour que le programme puisse être compilé avec succès. Il existe deux types de bibliothèques: statique et dynamique.

    • Bibliothèque statique : Une bibliothèque statique (fichiers .a pour les systèmes POSIX et les fichiers .lib pour Windows - à ne pas confondre avec les fichiers de bibliothèque d'importation de DLL , qui utilisent également l'extension .lib ) est intégrée de manière statique dans le programme. Les bibliothèques statiques ont l'avantage que le programme sait exactement quelle version d'une bibliothèque est utilisée. Par ailleurs, les tailles des exécutables sont plus grandes car toutes les fonctions de bibliothèque utilisées sont incluses.
      par exemple, libfoo.a foo.lib
    • Bibliothèque dynamique : Une bibliothèque dynamique (fichiers .so pour la plupart des systèmes POSIX, .dylib pour les fichiers OSX et .dll pour Windows) est liée dynamiquement au moment de l'exécution par le programme. Celles-ci sont parfois appelées bibliothèques partagées, car une image de bibliothèque peut être partagée par de nombreux programmes. Les bibliothèques dynamiques ont l'avantage de prendre moins d'espace disque si plusieurs applications utilisent la bibliothèque. En outre, ils autorisent les mises à jour de la bibliothèque (corrections de bogues) sans avoir à reconstruire les fichiers exécutables.
      par exemple, foo.so foo.dylib foo.dll

Le préprocesseur

Avant que le compilateur C ne commence à compiler un fichier de code source, le fichier est traité dans une phase de prétraitement. Cette phase peut être réalisée par un programme séparé ou être complètement intégrée dans un exécutable. Dans tous les cas, il est appelé automatiquement par le compilateur avant que la compilation proprement dite ne commence. La phase de prétraitement convertit votre code source en un autre code source ou unité de traduction en appliquant des remplacements textuels. Vous pouvez le considérer comme un code source "modifié" ou "étendu". Cette source étendue peut exister en tant que fichier réel dans le système de fichiers, ou elle peut uniquement être stockée en mémoire pendant une courte période avant d'être traitée ultérieurement.

Les commandes du préprocesseur commencent par le signe dièse ("#"). Il existe plusieurs commandes de préprocesseur; deux des plus importants sont:

  1. Définit :

    #define est principalement utilisé pour définir des constantes. Par exemple,

    #define BIGNUM 1000000
    int a = BIGNUM; 
    

    devient

    int a = 1000000;
    

    #define est utilisé de cette manière pour éviter d'avoir à écrire explicitement une valeur constante à différents endroits dans un fichier de code source. Ceci est important dans le cas où vous devez changer la valeur constante plus tard; il est beaucoup moins enclin à le modifier une fois, dans le #define , que de devoir le changer à plusieurs endroits dispersés dans le code.

    Étant donné que #define uniquement des recherches et des remplacements avancés, vous pouvez également déclarer des macros. Par exemple:

    #define ISTRUE(stm) do{stm = stm ? 1 : 0;}while(0)
    // in the function:
    a = x;
    ISTRUE(a);
    

    devient:

    // in the function:
    a = x;
    do {
        a = a ? 1 : 0;
    } while(0);
    

    En première approximation, cet effet est à peu près identique à celui des fonctions en ligne, mais le préprocesseur ne fournit pas de vérification de type pour les macros #define . Ceci est bien connu pour être sujet aux erreurs et leur utilisation nécessite une grande prudence.

    Notez également que le préprocesseur remplacera également les commentaires par des blancs, comme expliqué ci-dessous.

  2. Comprend :

    #include est utilisé pour accéder aux définitions de fonctions définies en dehors d'un fichier de code source. Par exemple:

     #include <stdio.h> 
    

    oblige le préprocesseur à coller le contenu de <stdio.h> dans le fichier de code source à l'emplacement de l'instruction #include avant qu'il soit compilé. #include est presque toujours utilisé pour inclure des fichiers d'en-tête, qui sont principalement des fichiers contenant des déclarations de fonctions et des instructions #define . Dans ce cas, nous utilisons #include pour pouvoir utiliser des fonctions telles que printf et scanf , dont les déclarations se trouvent dans le fichier stdio.h . Les compilateurs C ne vous autorisent pas à utiliser une fonction à moins qu’elle n’ait déjà été déclarée ou définie dans ce fichier; #include instructions #include sont donc le moyen de réutiliser le code précédemment écrit dans vos programmes C.

  3. Opérations logiques :

    #if defined A || defined B
    variable = another_variable + 1;
    #else
    variable = another_variable * 2;
    #endif
    

    sera changé pour:

    variable = another_variable + 1;
    

    si A ou B ont été définis quelque part dans le projet auparavant. Si ce n'est pas le cas, le préprocesseur le fera bien sûr:

    variable = another_variable * 2;
    

    Ceci est souvent utilisé pour le code, qui s'exécute sur différents systèmes ou compile sur différents compilateurs. Comme il existe des définitions globales, spécifiques au compilateur / système, vous pouvez tester ces définitions et toujours laisser le compilateur utiliser le code qu'il compilera à coup sûr.

  4. commentaires

    Le préprocesseur remplace tous les commentaires du fichier source par des espaces simples. Les commentaires sont indiqués par // jusqu'à la fin de la ligne, ou une combinaison de parenthèses ouvrantes /* et de fermeture */ .

Le compilateur

Une fois que le pré-processeur C a inclus tous les fichiers d'en-tête et développé toutes les macros, le compilateur peut compiler le programme. Pour ce faire, il convertit le code source C en un fichier de code objet, qui se termine par .o et contient la version binaire du code source. Le code objet n'est pas directement exécutable. Pour rendre un exécutable, vous devez également ajouter du code pour toutes les fonctions de la bibliothèque #include d dans le fichier (ce n'est pas la même chose que d'inclure les déclarations, ce que fait #include ). C'est le travail de l'éditeur de liens .

En général, la séquence exacte d'invocation d'un compilateur C dépend beaucoup du système que vous utilisez. Ici, nous utilisons le compilateur GCC, même s'il faut noter que beaucoup d'autres compilateurs existent:

% gcc -Wall -c foo.c

% est l'invite de commande du système d'exploitation. Cela indique au compilateur d'exécuter le pré-processeur sur le fichier foo.c , puis de le compiler dans le fichier de code objet foo.o L'option -c signifie compiler le fichier de code source dans un fichier objet mais pas invoquer l'éditeur de liens. Cette option -c est disponible sur les systèmes POSIX, tels que Linux ou macOS; d'autres systèmes peuvent utiliser une syntaxe différente.

Si votre programme entier se trouve dans un fichier de code source, vous pouvez plutôt faire ceci:

% gcc -Wall foo.c -o foo

Cela dit au compilateur d'exécuter le pré-processeur sur foo.c , de le compiler puis de le lier pour créer un exécutable appelé foo . L'option -o indique que le mot suivant sur la ligne est le nom du fichier exécutable binaire (programme). Si vous ne spécifiez pas -o , (si vous tapez simplement gcc foo.c ), l'exécutable sera nommé a.out pour des raisons historiques.

En général, le compilateur effectue quatre étapes lors de la conversion d’un fichier .c fichier exécutable:

  1. pré-traitement - développe textuellement les directives #include et les macros #define dans votre fichier .c
  2. compilation - convertit le programme en assembleur (vous pouvez arrêter le compilateur à cette étape en ajoutant l'option -S )
  3. assembly - convertit l'assemblage en code machine
  4. linkage - lie le code objet aux bibliothèques externes pour créer un exécutable

Notez également que le nom du compilateur que nous utilisons est GCC, ce qui signifie à la fois "compilateur GNU C" et "collection de compilateurs GNU", selon le contexte. D'autres compilateurs C existent. Pour les systèmes d'exploitation de type Unix, beaucoup d'entre eux portent le nom cc , pour le "compilateur C", qui constitue souvent un lien symbolique vers un autre compilateur. Sur les systèmes Linux, cc est souvent un alias pour GCC. Sur macOS ou OS-X, cela indique le contraire.

Les normes POSIX c99 actuellement c99 comme nom de compilateur C - il prend en charge le standard C99 par défaut. Les versions antérieures de POSIX c89 comme compilateur. POSIX exige également que ce compilateur comprenne les options -c et -o que nous avons utilisées ci-dessus.


Remarque: L'option -Wall présente dans les deux exemples gcc indique au compilateur d'imprimer des avertissements sur les constructions douteuses, ce qui est fortement recommandé. C'est également une bonne idée d'ajouter d'autres options d'avertissement , par exemple -Wextra .

Les phases de traduction

À partir de la norme C 2011, répertoriée au § 5.1.1.2 Phases de traduction , la traduction du code source en image de programme (par exemple, l'exécutable) est répertoriée pour se produire en 8 étapes ordonnées.

  1. L'entrée du fichier source est associée au jeu de caractères source (si nécessaire). Les trigraphes sont remplacées à cette étape.
  2. Les lignes de continuation (lignes se terminant par \ ) sont épissées avec la ligne suivante.
  3. Le code source est analysé en espaces et en jetons de prétraitement.
  4. Le préprocesseur est appliqué, qui exécute les directives, développe les macros et applique les pragmas. Chaque fichier source extrait par #include subit les phases de traduction 1 à 4 (récursivement si nécessaire). Toutes les directives liées au préprocesseur sont alors supprimées.
  5. Les valeurs du jeu de caractères source dans les constantes de caractères et les littéraux de chaîne sont associées au jeu de caractères d'exécution.
  6. Les littéraux de chaîne adjacents sont concaténés.
  7. Le code source est analysé en jetons, qui constituent l'unité de traduction.
  8. Les références externes sont résolues et l'image du programme est formée.

Une implémentation d'un compilateur C peut combiner plusieurs étapes, mais l'image résultante doit toujours se comporter comme si les étapes ci-dessus avaient eu lieu séparément dans l'ordre indiqué ci-dessus.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow