Welcome to Coding : Sécurité Programmation Réseaux

Search   in  

 Create an Account Home | Submit News Your Account Content | Topics | Top 10  


Accueil
· Home
· Listing des Articles
· Top 10
· Repository des Exploits

Les sujets / parties
· C / C ++
· Visual Basic
· Asm
· Reseaux
· Java
· Securite
· Divers

Utile
· Listing des Articles

· Telecharger
· Le Forum
· Liens
· Proposer un article

Top20 des Downloads
· 1: Etude des reseaux generalites et protocoles
· 2: Cheval de troie en VB avec sources
· 3: Netcat 1.1
· 4: Keylogger
· 5: Etudes des reseaux hauts debits architectures et protocoles
· 6: Ecoute de port
· 7: Etude du Smart Spoofing
· 8: Win Packet Capture Utils
· 9: Tutorial on Traffic Interception on Switched Lan using ARP spoofing
· 10: Cours de C

User Info
Welcome, Anonymous
Nickname
Password
(Register)
Membership:
Latest: gold-os
New Today: 0
New Yesterday: 1
Overall: 2179

People Online:
Visitors: 40
Members: 0
Total: 40

  
Le preprocesseur C
Posted on Wednesday, August 24 @ 00:06:11 CEST
Topic: C
C

	Tutoriel sur le preprocesseur C, a quoi sert-il, commet on s'en sert ...

Le Préprocesseur CPar Godrik (godrik-article@mandragor.org)
Vous devez vous demander pourquoi j'écris sur le préprocesseur. Tout simplement parceque énormément de programmeur "amateur" le connaissent mal et très peu le connaissent bien.
A quoi ca sert?
Le préprocesseur est appelé par les compilateurs C avant la phase de compilation. Il sert à avoir du code plus simple à lire. Par exemple il permet d'inclure un fichier dans un autre.

Il faut quand même noter que l'on peut appeler le préprocesseur C sans compiler (sous linux: gcc -E ou encore cpp); ce qui peut avoir des avantages dans beaucoup de cas.

Voyons maintenant ce qu'il nous permet de faire!

Généralité
Les lignes sont analysées les unes après les autres. Si une ligne commence par un # alors c'est une directive du préprocesseur. Si une directive fini par un alors le est "oublié" et la directive se poursuit sur la ligne d'après. La norme du C inclut la norme du préprocesseur[0]

Macros
Une macro est un mot qui est reconnu par le préprocesseur. Elle peut ou non prendre des arguments. Lorsqu'elle est recontré dans une ligne, la macro est developpé.

Par convention, les macros se voient toujours nommées en majuscule.

La directive utilisée pour developé une macro est : #define suivit du nom de la macro (appelé identificateur) puis des arguments et du code en lui même. Ceci donne:

# //oui, la directive vide existe. #define TOTO //par la suite la macro TOTO sera definit #define TOTO 1 //desormais toute occurence de TOTO sera remplace par 1 //Mais on peut aussi faire #define MAX(X, Y) (X>Y?X:Y) //Rq: il ne faut pas d'espace entre MAX et la premiere parenthèse //desormais un appel a MAX (2, 3) sera remplace par (2>3?2:3)
Commentons ensemble cette dernière ligne... Nous voyons que c'est très puissant, cela nous permet de faire des pseudo-fonctions qui sont souvent utilisées et simplifient l'écriture. Il est à noter qu'il faut faire très attention lorsqu'on les utilise.

Tout d'abord, il faut bien comprendre que le préprocesseur ne fait aucune vérification de type; ainsi un appel à MAX(2, "toto") ne posera aucun problème au préprocesseur. Mais il faut aussi remarquer que chaque paramètre est évalué plusieurs fois. Demonstration:

#define MAX(X, Y) (X>Y?X:Y) MAX (2, 3) //(2>3?2:3) MAX(x++, y++) //(x++>y++?x++:y++) //ceci n'est tres probablement pas l'effet voulu! //un MAX (10000, "toto") ne donnera pas le meme resultat a chaque execution car, //le compilateur C utilisera l'adresse de "toto" qui est definit a l'edition de lien //Considerons une autre macro et voyons ses faiblesses #define CARRE(x) x*x
Comme vous l'aurez remarqué le préprocesseur n'évalue pas ses arguments; c'est ce qui pose problème avec le x++. Mais ce n'est pas le seul problème, des problèmes liées à la priorité des opérations peuvent se poser. C'est pour cela qu'il faut forcer le préprocesseur à les évaluer indépendament. Voyons :

#define CARRE(x) x*x CARRE(3+4) //3+4*3+4 => 3+12+4 #define CARRE(x) ((x)*(x)) CARRE(3+4) //((3+4)*(3+4)) => 7*7
Une dernière chose dont je n'ai pas encore parlé est l'appel de fonction dans une macros. En effet, la fonction sera appelée plusieurs fois.

Un oubli... Il est possible d'annuler la reconnaissance d'un identificateur pas le préprocesseur en utilisant la directive #undef suivie de l'identificateur.

Pour conclure sur les macros, il faut être très prudent lorsque l'on écrit des macros. De nombreux développeurs font encore ce genre d'erreur qui leur coûtent un temps précieux. En règle géneral, préferez les appels de fonctions au macro quand la vitesse n'est pas une de vos préoccupations. En C++ l'utilisation de fonction inline cumule les avantages des fonctions et ceux des macros; Comme qui dirait: "les fonctions inline: c'est bon; mangez-en".

Inclusion de fichier
Le préprocesseur C nous permet d'inclure un fichier dans le fichier courant. Ceci s'avère très pratique lors de l'écriture de programme de taille conséquente.

Souvent l'édition de lien nous permet de séparer notre programme en plusieurs fichier, mais pour faire des appels d'un fichier à l'autre il faut que les deux objets définissent les mêmes symboles (en C). C'est pourquoi on utilise l'inclusion de fichier contenant les prototypes de nos fonctions dans des fichier d'en-tête.

--fichier main.c-- #include "fnct.h" int main () { return fnct(); } --fichier fnct.h-- int fnct (); --fichier fnct.c-- #include "fnct.h" int fnct () { return -1; }
Le code ci-dessus me semble suffisement éloquent pour que je ne le commente pas. J'entends deja la question: "Pourquoi des fois on ecrit #include et d'autre fois #include "fnct.h"? ". La norme du préprocesseur definit les deux et nous dit que la forme <> recherche le fichier dans une suite de répertoire dépendant de l'implémentation; tandis que la forme "" la recherche d'abord dans le fichier source original associé (la norme le dit comme ca, c'est pas moi qui complique.)

Pratiquement parlant, la plupart des compilateurs (en fait j'en ai jamais vu qui ne respectait pas ca) definisent une suite de répertoire où l'on peut trouver les fichier d'en-tête dit "système" où l'on met généralement les fichiers d'en-têtes des bibliothèques installées sur la machine. Tandis que la forme en "" désigne le répertoire où se trouve le fichier source que l'on est en train de compiler. Avec GCC c'est la variable INCLUDE_DIR qui fait foi; on peut toutefois le modifier à la compilation à l'aide de l'option -I.

Compilation conditionelle
Quel art que faire de la compilation conditionelle! Elle permet en effet de regler énormement de problème. L'un des plus courant étant la gestion des différents OS supportés par le logiciel; elle permet aussi de gerer différente bibliothèque ou encore sert a simplifier le développement...

La compilation conditionelle se fait a l'aide de la directive #if par rapport à une valeur booléene (C'est fou ca, c'est une mode ?:) ). Tout les operateurs "classiques" du C existent, mais il en existe un propre au préprocesseur qui est defined et qui permet de savoir si un identificateur a été défini plus tôt dans le fichier.

Voyons tout de suite un exemple:

#if 1>0 //ceci est toujours ecrit #else //jamais ecrit #endif
Mais on peut aussi utiliser la compilation conditionelle pour s'assurer de la version d'un logiciel. Par exemple:

#if WINVER >= 5 //ici j'ai le droit au option de windows 2000 #elif WINVER >= 4 //ici j'ai le droit au option de NT 4 #else //ici a priori just a celle de NT 3.51 et + #endif
En C, on utilise souvent la compilation conditionelle pour s'assurer de la non multi-inclusion d'un fichier. Comme suis:

--fichier fonction.h-- #ifndef __FONCTION_H__ //#ifndef est un alias de #if !(defined (__FONCTION_H__)) #define __FONCTION_H__ /*En definissant l'identificateur dans le fichier d'en-tête. On peut savoir si le fichier a deja ete inclu ou non*/ /*toute les lignes du fichier d'en-tête*/ #endif --fin fonction.h-- Chaine de caractere
Le préprocesseur C propose deux opérateurs qui servent à manipuler des chaines de caractères. Le premier est # et est un opérateur de conversion de parametre effectif en chaine de caractere:

#define AFF(x) printf ("%s: %d ", #x, x); int toto = 2; AFF(toto); /*cette ligne est equivalente a printf ("%s: %d ", "toto", toto);*/ /*faisons remarque que le C concatene automatiquement les chaines de caractere. On peut donc ecrire*/ #define AFF(x) printf ("%s%d ", #x ": ", x); /*Ce qui produira exactement le meme resultat au final. NB: je ne sais pas si c'est la compilateur ou le préprocesseur qui fait cela. Des informations ?*/
Le dernier opérateur concerne les chaines de caractères est l'opérateur ## qui sert à concatener plusieurs paramètres ensemble.

#define CONCAT(x, y) x##y printf ("%s", CONCAT (toto, tata)); /*equivalent a printf ("%s", "tototata");*/ Autre
Le préprocesseur permet d'utiliser des constantes touours valides à toute les lignes: __LINE__: le numero de la ligne courante; __FILE__: le nom du fichier en cours de compilation (attention, pas les en-têtes); __DATE__ : la date de compilation; __TIME__ l'heure de compilation; et __STDC__ qui vaut un et qui n'est definit que dans les implémenations conformes à la norme C.

Le préprocesseur permet égalment d'utiliser #error qui arrêtera la compilation en affichant le message d'erreur qui suit. #warning quant à lui affiche un message sans stopper la compilation.

On pourra égalment utiliser #line qui permet de changer le numero de la ligne suivante de compilation (ce qui peut être utile dans des applications de debuggage). A la suite du numero de ligne on peut également spécifier un nom de fichier qui se substituera pour la ligne suivante au nom de fichier courant.

On peut configurer le préprocesseur en cours de compilation à l'aide de la directive #pragma. Cette directive est entierement laissé à la discrétion de l'implémentation([1] et [2]).

Conclusions
Pour conclure ce tutoriel; je dirais que le préprocesseur est très simple mais très puissant, l'utilisation de ses options peut grandement simplifier le dévelopement au quotidien.

Il ne faut pas avoir peur de l'utiliser mais toujours se rappeler qu'après compilation, il n'y à plus aucune trace des directives et qu'il est parfois préférable d'utiliser des astuces du langages plutot que le préprocesseur.

Référence
[0]: "Le langage C, Norme Ansi", "Brian W.Kernighan et Denis M. Ritchie". Plus particulierement 4.11 et A.12




 
Liens connexes
· Plus à propos de C
· Nouvelles transmises par Romain_Le_Guen


L'article le plus lu à propos de C:
Programmer un sniffeur (sniffer ?) de deux facons.


Article Rating
Average Score: 0
Votes: 0

Please take a second and vote for this article:

Excellent
Very Good
Good
Regular
Bad


Options

 Format imprimable Format imprimable


PHP-Nuke Copyright © 2005 by Francisco Burzi. This is free software, and you may redistribute it under the GPL. PHP-Nuke comes with absolutely no warranty, for details, see the license.
Page Generation: 0.68 Seconds