User Tools

Site Tools


dev:c:compilation

Processus de compilation

La compilation s'effectue en plusieurs étapes. Nous allons dans un premier temps présenter les phases de compilation le plus complètement possible, puis voir comment effectuer les phases les plus fréquentes avec gcc.

Phases de compilation

Voici les différentes phases de compilation :

  • création d'objets binaires (.o) a partir des sources C (.c)
    • preprocesseur
    • transformation en assembleur
    • compilation du code assembleur en code binaire
  • création d'un binaire a partir de plusieurs objects (.o)
    • résolution des symboles, link, et génération de l'exécutable

preprocesseur

Dans le code, on trouve des directives pour le preprocesseur. Ces directives commencent par le signe #. Le préprocesseur supprime également les commentaires, afin de ne laisser que le code C qui devra être compilé.

Afin d'appelé le préprocesseur sur un source :

cc -E foo.c

Includes

#include permet d'inclure un fichier. Les en-têtes contiennent normalement des macros (#define), d'autres includes (#include), ainsi que des déclarations de fonctions ou autres symboles globaux.

Les en-têtes servent donc essentiellement a déclarer des interfaces qui pourront être appelé dans le code, sans avoir besoin de le re-déclarer plusieurs fois (ce qui serait inmaintenable).

#include <stdio.h>

Remplace la ligne par le contenu du fichier stdio.h, recherché dans les répertoires standard des en-têtes (et répertoire spécifiés explicitement). Cette notation est généralement utilisée pour les en-tête système ou de bibliothèque extérieures.

#include "common.h"

Remplace la ligne par le contenu du fichier common.h, recherché dans le répertoire du source, et à défaut dans les répertoires standard des en-têtes. Cette notation est généralement utilisé pour les sources “locales” au code en question.

Macros

#define permet de définir des macros. Une macro peux servir a définir des contantes (afin d'éviter les magic numbers), ou faciliter de l'écriture de certaines constructions facile. En général, il est conseillé de faire qu'une MACRO s'étende a quelque chose qui corresponde structurellement a une valeur, et/ou un appel de fonction.

/* définie simplement FOO, qui serait supprimé dans le code */
#define FOO
/* remplace HELLO par la chaine "Hello World\n" */
#define HELLO "Hello World\n"
/* remplace MIN, utilisé avec 2 arguments, par un ternaire renvoyant le minimum des deux valeurs */
#define MIN(a, b) ((a) > (b) ? (b) : (a))

Exemple de code à tester (juste pour l'exercice) :

#define FOO
#define HELLO "Hello World\n"
#define MIN(a, b) ((a) > (b) ? (b) : (a))

int main(int argc, char * argv[])
{
  printf("HELLO\n");
  return MIN(4 * 5 + 3, 42 - 42);
}

Et passer le préprocesseur sur le code en question.

tests

Le préprocesseur permet également de faire des tests, et de remplacer des blocs complet par des lignes vide au besoin. Cela permet d'avoir du code conditionnel, et donc avec un même source, suivant le contexte (architecture, options de compilation, …) de générer un code différent.

#ifdef DEBUG
  [code si DEBUG est défini]
#else
  [code si DEBUG n'est pas défini]
#endif

#ifndef NDEBUG
  [code si NDEBUG n'est pas défini]
#else
  [code si NDEBUG est défini]
#endif

Note: les closes else sont potionnelles.

Exemple:

int main(int argc, char * argv[])
{
#ifdef DEBUG
  printf("compiled in debug mode\n");
#endif
  printf("HELLO\n");
  return EXIT_SUCCESS;
}

génération de l'assembleur

La génération de l'assembleur peux se vérifier avec :

gcc -S foo.c

Cela génère un fichier foo.s qui correspond au code assembleur. Cela n'est utile que dans de très rare cas, ou l'on souhaite avoir de la micro-optimisation de code, ou que l'on est en train de débuger le compilateur.

compilation du code assembleur en code binaire

La génération d'un code binaire (.o) se fait grâce à :

gcc -c foo.c

Les différentes phases précédentes sont effectué, avec en plus la génération du binaire correspondant.

Cet objet est un objet binaire, mais pas un exécutable. Il exporte un certain nombre de symbole qui y sont défini, et a probablement besoin d'un certain nombre de symbole pour s'exécuter.

dev/c/compilation.txt · Last modified: 2012/04/25 12:00 by ze