ANNEXE


Najib TOUNSI,   Original: http://www.mescours.ma/C/annexeC.html

ntounsi@emi.ac.ma
Version: Sept. 2000, Dernière MàJ: Dec. 2021


A- Le Préprocesseur

B- L'Utilitaire make

C- Hackage


< Précédent Sommaire

ANNEXE A

Le Préprocesseur

On appelle préprocesseur un premier programme qui réalise un traitement initial avant le traitement principal effectué par un deuxième programme. Par exemple, dans le cas d'un compilateur, un préprocesseur, dit aussi précompilateur, prépare un programme à la compilation en exécutant certaines tâches initiales comme la substitution ou l'adjonction de textes par exemple. On dit directives de compilation dans ce cas. On doit les distinguer pour ne pas les confondre avec les instructions d'un programme.

Dans le préprocesseur C, les directives de compilation sont distinguées par un signe # en début de ligne. Elle réalisent un certain nombre de tâches comme des inclusions de fichiers sources dans le texte d'un programme, des définitions de symboles ou des compilations conditionnelles. Ce dernier aspect est intéressant, car il permet, sous conditions, de compiler uniquement certaines lignes de code.

I. Les Inclusions de Fichiers

L'inclusion dans un programme d'instructions provenant d'un autre fichier est une des prérogatives du préprocesseur. La directive #include permet d'insérer à l'endroit où elle figure, un fichier paramètre nécessaire à la compilation d'un programme. Ce fichier peut lui-même contenir d'autres directives include (sans récursivité). Selon le type de fichier en paramètre, la directive a deux formes:

#include <fichier.h>

#include "fichier"

Dans le premier cas, le fichier est cherché dans un répertoire standard (/usr/include sous UNIX), et l'usage veut que l'extension d'un tel fichier soit .h comme <stdio.h>. Sous ce répertoire, on trouve des fichiers bibliothèques écrits pour un usage très répandu: prototypes de fonctions, définitions de symboles de fonctions ou de constantes comme celles de POSIX etc...

Dans le second cas, le fichier est cherché selon la procédure standard de désignation: chemin absolu ou relatif. Les fichiers inclus de cette façon sont des fichiers utilisateurs servant comme précédemment à plusieurs programmes ou contenant des morceaux de programmes écrits séparément etc...

II. Les Macro-définitions

Elles sont introduites par la directive #define. Une macro, est en générale un diminutif pour quelque chose. Ce quelque chose peut être

Dans tous les cas, la manipulation est purement syntaxique. C'est du remplacement de texte sans interprétation. C'est pour cela qu'il est conseillé de parenthéser les expression de substitutions. La chaîne de substitution se termine à la fin de la ligne #define. Si elle est trop longue on utilise le caractère \ en fin de ligne pour indiquer que la ligne se termine sur la suivante.

#define LONGEXP (a + b + c \
+ d + e)

Une macro-définition finit (elle est oubliée) à l'apparition de la directive #undef concernant le même identificateur. Avec

#undef PI

PI n'est plus équivalent de 3.14159265, dans les lignes programmes qui suivent #undef.

On peut écrire

#define identificateur

sans valeur associé, rien que pour indiquer que identificateur est définie dans une zone de programme (voir aussi #ifdef).

III. Compilation Conditionnelle

Ici on a des directives de type if-then-else, qui permettent à certaines lignes d'être compilées si une condition est vérifiée. On a #if, #ifdef ou ifndef avec #else ou #elif et #endif.

Avec

#if expression entièr

l'expression, formée de constantes ou de symboles macro-définis, est évaluée, et si elle non zéro (vrai) les instructions qui suivent sont conservées et seront donc compilées.

Avec

#ifdef identificateur

on teste si l'identificateur est défini précédemment dans le programme avec #define. Le contraire aussi peut se faire avec

#ifndef identificateur

pour cette fois ci tester si un identificateur n'est pas défini.

On peut inclure des lignes #else ou #elif ( #if en cascade) avec la sémantique usuelle. Une ligne #endif termine un test.

#ifdef SEIZEBIT
long x,y;
#else
int x,y;
#endif

Ici, x et y seront déclarés dans le type désiré (entier sur 32 bits) selon la machine. Il suffit dans un programme et avant cette déclaration, de rajouter la ligne

#define SEIZEBIT

quand la machine a des entiers sur 16 bits.

Ce genre de directives est donc très utile et est très utilisé dans un but de portabilité, pour savoir les lignes qui ont besoin d'être recompilées quand on change de machine.

#ifndef est très utilisé dans les fichiers include dans le but d'éviter à un programme de les inclure plusieurs fois. Ce qui, outre le fait de rallonger inutilement un programme, bloque la compilation car des symboles seront doublement définis. Par exemple dans <stdio.h> on trouve

#ifndef _STDIO_H_
#define _STDIO_H_
...
      reste du fichier
...
#endif /* _STDIO_H_ */

Si l'un des morceaux constituant un programme contient #include <stdio.h>, tous autre morceau qui contient la même directive ne gênera pas, car le reste du fichier include sera ignoré vue que l'identificateur _STDIO_H_ est déjà défini. CQFD


ANNEXE B

VoirL'utilitaire MAKE (Pour C sous UNIX)


ANNEXE C

Hackage

Comme mentionné à la fin du chapitre III, voici d'autres exemples, mais plus courts, du programme qui se produit lui-même. Ils sont en nombre de 4, du plus long au plus court (et plus simple?!). Les sauts à la lignes sont là pour la mise en page.

Programme_1:

main(){char*p="main(){char*p=%c%s%c;(void)printf(p,34,p,34,10);}%c";
(void)printf(p,34,p,34,10);}

Programme_2: main(a){a="main(a){a=%c%s%c;printf(a,34,a,34);}";printf(a,34,a,34);}

Programme_3: main(){char*a="main(){char*a=%c%s%c;int b='%c';printf(a,b,a,b,b);}";
int b='"';printf(a,b,a,b,b);}

Programme_4: main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%c,34);}",34);}

Ces programmes sont extraits du répertoire /usr/games/lib/hackdir du système SUNOS-4.1 de Sun. (Racheté depuis par Oracle :-( ).


< Précédent Sommaire
CopyRight Najib TOUNSI (EMI)
ntounsi@emi.ac.ma

Original
: http://www.mescours.ma/C/annexeC.html
Dernière MàJ Sept. 2000