home *** CD-ROM | disk | FTP | other *** search
-
- ==========================================================================
- APPRENTISSAGE du C version ALB_10
-
- CHAPITRE 7: DES DIRECTIVES ET DES MACROS.
- ==========================================================================
-
-
- Nous avons déjà mentionné les trois phases de la fabrication d'un
- programme:
- l'écriture du code source,
- la compilation,
- la création de liens entre les différentes parties.
-
- Nous allons nous intéresser à la seconde phase. Il existe en C un
- ensemble de directives qui permettent de donner des instructions au
- compilateur. Nous en avons déjà rencontré deux, #include et #define.
- Nous allons étudier, plus en détails, les principales d'entre elles.
-
- Elles commencent avec le symbole # et doivent être calées à gauche de
- votre texte. Le reste de la ligne doit rester vide, sauf le code /* qui
- permet des commentaires. Si la directive prend plus d'une ligne, il faut
- utiliser le symbole \ avant de passer à la ligne suivante.
-
-
- 1. INSERER DU TEXTE AVEC LA DIRECTIVE #include.
- ================================================
-
- Nous avons déjà utilisé cette directive pour appeler des fichiers
- contenant des librairies standards. Le compilateur inclut le texte du
- fichier dans notre code. Il est allé le chercher dans le répertoire
- standard des fichiers "include". Par exemple: c:\tcwin\include
-
- Vous pouvez vous même créer un fichier qui contienne vos propres
- fonctions, par exemple fonction.h, et l'appeler dans votre programme.
-
- #include"fonction.h" /* avec cette fois ci des guillemets anglais.*/
-
- Le compilateur cherchera ce fichier dans le répertoire standard où
- vous pouvez placer votre fichier de fonctions. S'il ne l'y trouve pas, il
- cherchera dans le répertoire courant, qui est celui à partir duquel on a
- appelé le compilateur, et qui en général est celui de votre programme.
- Mais il est plus prudent d'indiquer le chemin complet du fichier.
- Chapitre 7: Des directives et des macros page 1
-
-
-
-
- 2. REMPLACER UNE CHAINE DE CARACTERES AVEC LA DIRECTIVE #DEFINE.
- =================================================================
-
- #define identificateur Chaîne de caractères
-
- La chaîne commence après le premier espace qui suit l'identificateur.
- Le compilateur remplacera dans tout le code, l'identificateur par la
- chaîne. On trouve à cette directive plusieurs utilisations.
-
- 2.1. Remplacement d'une constante ou d'un texte.
- ------------------------------------------------
- => Exemple n°1:
-
- #define dim 16 // définit la dimension de chaînes de caractères ou
- // de tableaux.
- void main( void)
- {
- char chaine0[dim], chaine1[dim* 2];
- double tableau[dim- 1];
- ...
- }
- L'identificateur dim sera remplacé par sa valeur. Si par la suite nous
- devons modifier la dimension de notre chaîne, nous n'aurons à changer que
- la valeur de la constante.
-
- => Exemple n°2:
- #define PI 3.14159265358979323846
- #define C 0.5772156649 // constante d'Euler, mathématicien suisse(1707- 1783).
-
- Dans tout notre code les identificateurs PI ou C seront remplacés par
- les valeurs de ces constantes.
- ************
- Examinez les fichiers: CH07_01.C
- CH07_02.C
- ************
- Vous pouvez ainsi vous constituer un fichier de constantes, l'appeler
- par exemple ctes.h, le placer dans le sous répertoire "include" et
- l'appeller comme une bibliothèque standard:
-
- #include<ctes.h> ou mieux #include"ctes.h".
-
- Vous pouvez aussi le placer dans le même répertoire que CH07_02.C, mais
- par prudence il vaudra mieux appeler le fichier avec son chemin d'accès
- complet:
- #include"c:\albulus\chap_07\ctes.h"
-
- ( Si vous avez placé le manuel dans un répertoire nommé albulus).
-
- Pour afficher avec printf() des réels en notation dite scientifique, on
- utilise le symbole %le ou %lE au lieu de %lf.
-
- => Remarque: nous avons déjà plusieurs constantes à notre disposition par
- exemple dans le fichier math.h que nous avons utilisé pour sa
- bibliothèque de fonctions mathématiques. Appelez ce fichier sur votre
- éditeur. A la fin du fichier vous trouverez une série de constantes que
- vous pouvez utiliser directement SI VOUS ETES EN C++.
- Chapitre 7: Des directives et des macros page 2
-
-
-
- /* Constantes arrondies à 21 decimales dans math.h et seulement en C++. */
- #define M_E 2.71828182845904523536
- #define M_LOG2E 1.44269504088896340736
- #define M_LOG10E 0.434294481903251827651
- #define M_LN2 0.693147180559945309417
- #define M_LN10 2.30258509299404568402
- #define M_PI 3.14159265358979323846
- #define M_PI_2 1.57079632679489661923
- #define M_PI_4 0.785398163397448309616
- #define M_1_PI 0.318309886183790671538
- #define M_2_PI 0.636619772367581343076
- #define M_1_SQRTPI 0.564189583547756286948
- #define M_2_SQRTPI 1.12837916709551257390
- #define M_SQRT2 1.41421356237309504880
- #define M_SQRT_2 0.707106781186547524401
-
- 2.2. Macro-instructions.
- ------------------------
-
- 2.2.1. Généralités.
-
- Il est possible de remplacer l'identificateur par ce qui ressemble
- fort à une définition de fonction avec un nom et des arguments.
- Le texte de remplacement sera une quasi définition de fonction.
-
- #define nom_de_la_macro( arguments) définition de la macro
-
- La macro se place en général en tête, avant les définitions de fonctions.
- Sa portée s'exerce du point où elle se trouve à la fin du fichier qui la
- limite. La parenthèse qui ouvre la liste des arguments doit suivre le nom
- de la macro, sans espace ni tabulation.
- On peut limiter sa portée en incluant à l'endroit désiré une directive
- #undef nom_de_la_macro. La définition est annulée pour la suite du code.
-
- 2.2.2. Macros et fonctions.
-
- * Pourquoi substituer une macro à une fonction classique?
- Parce que la gestion d'une fonction est lourde, réservation d'une zone de
- mémoire, création de nouvelles variables,...
- Dans le cas d'une macro, le compilateur remplace directement dans le code
- l'argument par sa définition. C'est plus rapide.
-
- L'usage d'une macro à la place d'une fonction se justifie pour des
- applications qui utilisent un petit nombre de fonctions et en particulier
- pour celles qui appellent une fonction dans une boucle.
-
- * Mais:
- 1° Un contrôle est fait sur le nombre d'arguments mais pas sur leur
- type. Pour une fonction le compilateur détecte cette erreur .
- 2° Le nom d'une fonction est associé à une adresse. Une fonction peut
- donc appeler une autre fonction, ce qui n'est pas possible pour une
- macro.
- 3° Il se produit parfois,quand on fait appel à des arguments, ce que
- l'on nomme des "effets de bords". Ce sont souvent des conflits non
- résolus entre plusieurs définitions. Les erreurs sont parfois très
- délicates à identifier. Nous en étudierons un cas.
- Chapitre 7: Des directives et des macros page 3
-
-
-
- 2.2.3. Exemples.
- ************
- Examinez le fichier: CH07_03.C
- ************
- Ces macros ont été écrites par des développeurs réputés: Wayne Hamilton,
- Dave Knapp et Thad Smith que nous connaissons déjà. Remarquez que les
- variables sont toujours soigneusement mises entre parenthèses dans les
- définitions elles mêmes entre parenthèses.
-
- Les fonctions contenues dans les bibliothèques sont accessibles pour
- les macros. C'est logique puisque les macros sont en fait dans le corps de
- la fonction principale où elles ont été recopiées par le compilateur.
- Nous remarquons l'utilisation de trois fonctions de la bibliothèque math.h:
-
- double floor( double x) donne l'arrondi par défaut,
- l'arrondi par excès est donné par ceil().
- atan( x) donne l'Arc tangente et nous connaissons pow( x, y).
-
- Il y a une définition simple de PI à partir d'un cas particulier, comme
- souvent en calcul numérique. Le calcul de atan() ne se fait certainement
- pas avec la série à convergence lente que nous avons étudié. Notez que
- l'utilisation de la macro n'évite pas dans ce cas l'appel à math.h.
- Analysez la macro fround( n, d), c'est simple et élégant. Il semble qu'il
- n'y ait pas une lettre de trop.
- ************
- Examinez le fichier: CH07_04.C
- ************
-
- C'est un exemple type d'utilisation de macros. Max et Min sont courtes,
- inscrites une seule fois dans le code et incluses dans une boucle.
- La macro Produit est très simple. Elle a été ajoutée pour montrer que
- la substitution de texte ne se fait pas à l'intérieur d'une chaîne:
- i*j n'a pas remplacé Produit dans "n i= %d , j= %d , Produit= %d"
-
- Dans ce cas le programme doit être plus rapide qu'en faisant appel à une
- fonction. Essayez de le vérifier avec le procédé utilisé dans CH06_04.C.
- Ecrivez la fonction équivalente. Donnez à "Fin" la valeur 30000. Supprimez
- la ligne d'impression dans la boucle.
-
- ************
- Contrôlez vous avec: CH07_05.C
- ************
- Examinez le fichier: CH07_06.C
- ************
- 1° La première ligne d'affichage montre la nécessité des parenthèses.
-
- Produit1( a+ 1, b+ 1)= ( 2+ 1 * 2+ 1 + 3+ 1 * 3+ 1)= 12
-
- Produit2( a+ 1, b+ 1)= ( (2+ 1)*( 2+ 1) + (3+ 1)*( 3+ 1))= 25
- qui est bien le résultat que nous attendions.
-
- 2° La seconde donne un exemple d'effet de bords. On comprend encore que:
-
- Produit2( a++, b++)= ( 2*( 2+ 1) + 3* (3+ 1) )= 18
-
- mais je n'ai pas trouvé pour ma part d'explication satisfaisante à:
-
- Produit2( a+ 1, b+ 1)= 61!!!!
- peut être: 61= 5* 5 + 6* 6 avec un effet de l'incrémentation qui
- suit? Si vous avez une explication convaincante, écrivez moi.
- Chapitre 7: Des directives et des macros page 4
-
-
-
- Une conclusion possible est de n'employer les macros:
-
- ========================================================================
- => que lorsque la vitesse d'exécution est le critère principal.
-
- => et de toujours rechercher une extrème simplicité du code pour
- éviter des effets de bords difficiles à maîtriser.
- ========================================================================
-
-
- From: JOSEPH CARNAGE
- City: DUNEDIN FL
- -- Never EVER pass a #defined macro an incremented or decremented
- value (++,or --) or an assignment as a parameter. This is because
- many #defined macros may reference their arguments multiple times.
- This is especially true of the macros #defined in ctype.h.
- #define iscsymf(c) (isalpha(c) || ((c) == '_'))
- if called as so:
- iscsym(var++);
- it will be expanded by the preprocessor to:
- (isalpha(var++) || ((var++) == '_'))
- with var being incremented twice.
-
-
- 2.2.4. Une macro peut utiliser des types
- différents de variables.
-
- Nous avions noté, pour en souligner le danger, qu'une macro ne
- vérifiait pas le type de ses arguments.
- C'est aussi la seule méthode en C actuel qui permette d'écrire de
- pseudo fonctions pouvant utiliser des types différents de variables.
-
- ************
- Etudiez le fichier: CH07_07.C
- ************
-
- 1° La macro calcule la valeur actuelle Vo d'une somme V au taux de t pour
- une durée de n années, soit Vo= V* (1+ t/100)^(-n). Elle s'écrit sur
- plusieurs lignes avec le symbole "\" et respecte l'intervalle entre
- l'identificateur et le symbole "{" qui marque le début de la déclaration.
- Nous avons mis des parenthèses là où elles paraissent indispensables. Dans
- une application nous en aurions mis partout!
-
- i= ( (t)/100);
- Vo= ( (S)*(pow( ( 1+(i)), (- n))); // prudence ou pusillanimité!
-
- 2° La macro utilise trois variables qui lui sont passées et deux variables
- i et Vo qui seront déclarées dans le corps du programme principal. Nous
- avons défini pour ces deux variables le type le plus long compatible avec
- la conversion automatique des types que fait notre compilateur.
-
- 3° L'intérêt du programme est qu'il montre qu'il est possible d'utiliser
- la même macro pour traiter des variables de type float et double. Nous
- savons que cela n'est pas possible avec des fonctions.
- En C++, pour répondre aux besoins des programmeurs, les développeurs ont
- conçu le concept de "template" qui permet d'utiliser des fonctions
- ( et des classes) de types différents.
- Chapitre 7: Des directives et des macros page 5
-
-
-
- 4° Notez deux petites choses:
- => la manière de faire afficher par printf() le symbole %,
- printf("... %% ...)
- le premier est interprété comme le début d'un groupe de caractères
- qui indiquent un champ, le second comme un caractère à afficher.
- => La valeur que nous avons attribué à somme_f n'a pas été prise en
- compte par la variable de type float à partir du huitième chiffre.
- L'erreur que nous constatons sur Vo, lors du calcul en float en est
- la conséquence directe.
-
-
- 2.2.5. Les directives de compilation
- sous conditions.
-
- Ce sont #if, #else, #elif (équivalent de else if), #ifdef et #ifndef
- qui permettent de savoir si une macro ou une constante ont dèjà été
- définies, #endif qui termine obligatoirement une boucle où figure if.
- Nous en avons vu des exemples dans CH07_03.C et ctes.h. Cela permettait
- d'éviter une double définition dans le cas d'une utilisation simultanée
- de plusieurs fichiers dans le même programme.
-
- #ifndef PI // si Pi n'a pas été défini ailleurs,
- #define PI (4*atan(1)) // nous le définissons ici.
- #endif // fin de la boucle if.
-
-
- 2.2.6. Les opérateurs de substitution de chaîne #
- et de fusion d'éléments avec ##.
- ************
- Etudiez le fichier: CH07_08.C
- ************
- 1° Dans la première macro, l'opérateur # entre les parenthèses de
- printf(), permet de convertir l'argument en une chaîne.
- Notez certaines particularités, une série d'espacements est réduite à
- un seul, tous les caractères ne peuvent être pris en compte.
-
- 2° Dans la seconde nous utilisons également l'opérateur de fusion qui
- permet de fusionner deux éléments si ceux-ci sont séparés par ##. On
- peut ajouter comme dans l'exemple un espace de part et d'autre des ##.
- L'espace et les ## sont supprimés et les éléments de part et d'autre
- fusionnés. Nous aurons en fait dans le code:
-
- printf("\n La variable est: Z5= %d\n", Z5)
-
- 2.2.7. La directive #pragma.
-
- Son utilisation dépend du compilateur que vous utilisez. Consultez
- votre documentation.
-
- #pragma nom_ de_la _directive
-
- Parmi ceux qui sont utilisés par les compilateurs de Borland:
-
- => #pragma exit et #pragma startup qui permettent d'indiquer au programme
- que des fonctions doivent être appelées lors du lancement, avant
- l'appel de main() ou juste avant la fin.
- => #pragma hdrfile et #pragma hdrstop qui facilitent la gestion des
- fichiers d'en-tête.
- => #pragma inline permet l'insertion de code en assembleur.
- Fin du chapitre 7: Des directives et des macros page 6
-
-
-