Présentation
Comme je vous l'ai
dit dans l'introduction, un identificateur est un nom qui permet de
désigner une entité du langage. Oui, mais quelles entités ? La norme C11
nous en donne huit (en comptant les membres de structures/unions/énumérations à part).
- un objet ;
- une fonction ;
- une étiquette de structure/union/énumération, qui correspond au nom que vous donnez à votre structure/union/énumération ;
- un membre de structure/union/énumération ;
- une définition de type (typedef) ;
- une étiquette, utilisée pour l'instruction de saut goto ;
- une macro ;
- un paramètre de macro.
Pour rappel (ou non), en C, un objet est une zone mémoire pouvant contenir des données
2.
Comme un exemple vaut souvent mieux qu'un long discours, voici un code
déclarant un identificateur pour chacune des entités présentées ci-dessus.
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #define identificateur_de_macro(identificateur_de_parametre_de_macro)
struct identificateur_d_etiquette_de_structure { identificateur_de_membre_de_structure; };
typedef int identificateur_de_definition_de_type;
void identificateur_de_fonction(void) { int identificateur_d_objet;
identificateur_d_etiquette: ; }
|
Notez que je ne parlerai pas des identificateurs de macro et de
paramètre de macro dans la suite du tutoriel, ces derniers n'existant
plus après le traitement du code par le préprocesseur.
Portée, espaces de noms et masquage
Vous avez peut-être
remarqué que j'ai utilisé le terme « déclaration » dans la présentation,
ce n'est pas anodin, il s'agit d'un concept fondamental du langage C
permettant la création d'identificateurs.
La notion de portéeUne déclaration déclare un identificateur, c'est à dire qu'elle le rend
utilisable, visible pour la suite du programme. On dit qu'une
déclaration confère une
portée à l'identificateur, c'est à dire une portion du programme où il sera utilisable. Il existe quatre types de portée :
- au niveau d'un bloc ;
- au niveau d'un fichier ;
- au niveau d'une fonction ;
- au niveau d'un prototype.
Cependant, je n'aborderai pas la portée au niveau d'un prototype dans la
suite de ce cours, étant donné le peu d'intérêt de cette dernière.
Au niveau d'un blocUne portée au niveau d'un bloc signifie qu'un identificateur est
utilisable, visible de sa déclaration jusqu'à la fin du bloc dans lequel
il est déclaré. Ainsi, dans le code suivant :
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 | void f(void) { int n = 10; }
void g(void) { n = 20; /* Incorrect */ }
|
L'identificateur « n » ne peut pas être utilisé dans le bloc de la fonction g car il a une portée limitée au bloc de la fonction f. De même, le code suivant est erroné :
Code : C -
1 2 | int *p = &a; /* Incorrect */ int a = 10;
|
car au moment de la déclaration de l'identificateur « p »,
l'identificateur « a » n'est pas encore déclaré, il est donc utilisé en
dehors de sa portée.
Au niveau d'un fichierUne portée au niveau d'un fichier signifie qu'un identificateur est
utilisable, visible de sa déclaration jusqu'à la fin du fichier dans
lequel il est déclaré. Pour obtenir un identificateur ayant une portée
au niveau d'un fichier, il est nécessaire de le déclarer en dehors de
tout bloc, par exemple comme ceci :
Code : C - main.c -
1 2 3 4 5 6 7 8 9 10 11 12 13 | int n;
void f(void) { n = 10; }
void g(void) { n = 20; }
|
Dans ce code, l'identificateur « n » a une portée au niveau du fichier
et peut par conséquent être aussi bien utilisé dans la fonction f que dans la fonction g.
Au niveau d'une fonctionUne portée au niveau d'une fonction signifie qu'un identificateur est
utilisable, visible dans toute la fonction où il est déclaré et ce, peu
importe la position de sa déclaration. Cette portée est propre aux
identificateurs d'étiquette, utilisés par l'instruction de saut goto.
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | int main(void) { int n = 0;
test: if (!n) goto dix; else goto fin;
dix: n = 10; goto test;
fin: return 0; }
|
Comme vous le voyez, les identificateurs « dix » et « fin » peuvent être
utilisés avant leur déclaration car ils ont une portée au niveau de la
fonction main.
La notion d'espaces de nomsUne règle importante à retenir est qu'il ne peut pas exister deux
identificateurs de même nom dans la même portée, à moins qu'ils ne
fassent partie d'
espaces de noms différents.
C'est quoi, un espace de noms ?
Bonne question.
Le concept n'est pas facile à définir, mais est par contre très facile à
comprendre avec un exemple. Sachez tout d'abord qu'il existe quatre
espaces de noms :
- un dédié aux identificateurs d'étiquettes ;
- un dédié aux identificateurs d'étiquettes de structures/unions/énumérations ;
- un dédié aux identificateurs de membres de structures ou unions ;
- un dédié à tous les autres identificateurs.
Comme promis, voici un exemple.
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | int main(void) { struct test { int test; };
struct test test;
goto test; test: test.test = 10; return 0; }
|
Comme vous le voyez, il y a quatre identificateurs déclarés avec le nom « test » :
- un identificateur d'étiquette de structure (struct test, ligne 4) ;
- un identificateur de membre de structure (int test, ligne 5) ;
- un identificateur d'objet (struct test test, ligne ;
- un identificateur d'étiquette (test:, ligne 11).
Ces quatre identificateurs ont tous une portée au niveau du bloc de la fonction main.
Ce code ne pose pourtant aucun problème, tout simplement parce que ces
derniers appartiennent à quatre espaces de noms différents. Tout risque
de confusion est évité de par :
- le contexte d'utilisation de l'identificateur (l'instruction goto attend un identificateur d'étiquette) ;
- l'utilisation de mots-clés (struct/union/enum pour désigner l'identificateur d'étiquette d'une structure/union/énumération) ;
- l'utilisation d'opérateurs (l'opérateur . ou -> pour accéder aux membres d'une structure/union) ;
- la syntaxe de la déclaration (par exemple les deux points suivant la déclaration d'un identificateur d'étiquette).
La notion de masquageNous venons de voir qu'il ne pouvait pas exister deux identificateurs de même nom dans la même portée, quid
maintenant de deux identificateurs de même nom ayant des portées
différentes ? Autrement dit, que se passe-t-il par exemple dans ce cas
ci ?
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 | #include <stdio.h>
int n = 10;
int main(void) { int n = 20;
printf("%d\n", n); return 0; }
|
En fait, dans un tel cas, c'est l'identificateur ayant la portée la plus faible qui sera privilégié. On dit qu'il
masque celui ou ceux ayant une portée plus élevée
(en l'occurrence celui ayant une portée au niveau d'un fichier). Je dis
« celui ou ceux » car les identificateurs déclarés dans un sous-bloc
ont une portée plus faible que ceux déclarés dans le bloc supérieur.
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <stdio.h>
int n = 10;
int main(void) { int n = 20;
if (n == 20) { int n = 30;
printf("%d\n", n); }
return 0; }
|
Dans cet exemple, il y a trois identificateurs d'objet portant tous les trois le nom « n » :
- le premier a une portée au niveau du fichier ;
- le second au niveau du bloc de la fonction main ;
- et le troisième au niveau du bloc du if.
L'identificateur ayant une portée au niveau du fichier est donc masqué
par celui ayant une portée au niveau du bloc de la fonction main, qui est lui-même masqué par celui ayant une portée au niveau du bloc du if. Si l'on exécute ce petit programme, il affichera donc 30.
Liaisons et définitions
Dans le chapitre
précédent, nous avons entre autres vu que les identificateurs étaient
confinés à une portée et que cette dernière ne pouvait s'étendre au delà
d'un fichier. Cependant, si cela s'arrêtait là, il ne serait pas
possible d'utiliser des objets ou des fonctions d'autres fichiers.
Autrement dit, l'exemple ci-dessous serait incorrect et il serait
nécessaire de n'utiliser qu'un seul fichier source, ce qui serait assez
peu commode.
Code : C - autre.c -
1 2 3 4 5 | int f(void) { return 1; }
|
Code : C - main.c -
1 2 3 4 5 6 7 8 9 | int f(void);
int main(void) { f(); return 0; }
|
La notion de liaisonPrésentationHeureusement, il existe une solution : la notion de
liaison. Chaque identificateur peut disposer d'une liaison qui peut être de deux types : externe ou interne.
Grâce à cette notion, il est possible de considéré un groupe
d'identificateurs comme faisant référence à un même objet ou à une même
fonction. En fait, elle permet de préciser que :
- tous les identificateurs avec liaison externe d'un même programme font référence au même objet ou à la même fonction ;
- tous les identificateurs avec liaison interne d'un même fichier font référence au même objet ou à la même fonction.
Ainsi, si je reprends l'exemple donné au début de ce chapitre et que
l'on considère que tous les identificateurs de fonction « f » ont une
liaison externe ; on peut en déduire qu'en fait, ils font tous référence
à la même fonction : celle du fichier autre.c.
Code : C - autre.c -
1 2 3 4 5 | int f(void) { return 1; }
|
Code : C - main.c -
1 2 3 4 5 6 7 8 9 | int f(void);
int main(void) { f(); /* Retournera 1 */ return 0; }
|
La même logique peut être appliquée pour une liaison interne, mis à part
que le regroupement se limite à un fichier. En conséquence, dans
l'exemple ci-dessous, si l'on considère tous les identificateurs de
fonction « f » comme ayant une liaison interne, tous ceux situés dans le
fichier main.c font référence à la fonction de ce fichier, alors que celui du fichier autre.c fait référence à celle située en son sein.
Code : C - autre.c -
1 2 3 4 5 | int f(void) { return 1; }
|
Code : C - main.c -
1 2 3 4 5 6 7 8 9 10 11 12 13 | int f(void) { return 2; }
int main(void) { f(); /* Retournera 2 */ return 0; }
|
Conditions d'attributionMaintenant que vous connaissez la notion de liaison, il reste encore à
déterminer dans quelles conditions cette dernière est attribuée à un
identificateur. En fait, la présence d'une liaison et son type sont
déterminés par la position de la déclaration de l'identificateur ainsi
que par l'utilisation des mots-clés extern et static. Concrètement, cela se détermine suivant les règles exposées ci-dessous.
Un identificateur de fonction ou d'objet ayant une portée au niveau d'un fichier a une liaison externe, sauf si sa déclaration est précédée du mot-clé static, auquel cas il a une liaison interne.
Code : C -
1 2 3 4 5 | int a; /* Liaison externe */ static int b; /* Liaison interne */
void f(void); /* Liaison externe */ static void g(void); /* Liaison interne */
|
Un identificateur d'objet déclaré à l'intérieur d'un bloc n'a pas de liaison sauf s'il est précédé du mot-clé extern (voyez la règle suivante).
Code : C -
1 2 3 | { int a; /* Pas de liaison */ }
|
Un identificateur d'objet ou de fonction dont la déclaration est précédée du mot-clé extern a une liaison externe sauf si une déclaration du même identificateur la précède, auquel cas il a la même liaison que ce dernier.
Notez que dans le cas où une déclaration d'un identificateur de fonction n'est précédée, ni du mot-clé static, ni du mot-clé extern, le mot-clé extern est implicitement ajouté.
Ces règles peuvent paraître quelque peu indigestes, aussi, voici un exemple illustrant chacune de ces dernières.
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | /* * `a' est un identificateur d'objet déclaré en dehors de tout bloc. * Il a donc une liaison externe. */ int a;
/* * `b' est un identificateur d'objet déclaré en dehors de tout bloc. * Sa déclaration est précédée du mot-clé `static'. * Il a donc une liaison interne. */ static int b;
/* * `c' est un identificateur d'objet déclaré en dehors de tout bloc. * Sa déclaration est précédée du mot-clé `extern'. * Aucune déclaration du même identificateur ne le précède. * Il a donc une liaison externe. */ extern int c;
/* * `f' est un identificateur de fonction. * Sa déclaration n'est pas précédée du mot-clé `extern' ou `static'. * Dès lors, il faut faire comme si elle était précédée du mot-clé `extern'. * Aucune déclaration du même identificateur ne le précède. * Il a donc une liaison externe. */ void f(void);
/* * `g' est un identificateur de fonction. * Sa déclaration est précédée du mot-clé `static'. * Il a donc une liaison interne. */ static void g(void);
/* * `h' est un identificateur de fonction. * Sa déclaration est précédée du mot-clé `extern'. * Aucune déclaration du même identificateur ne le précède. * Il a donc une liaison externe. */ extern void h(void);
int main(void) { /* * `a' est un identificateur d'objet déclaré à l'intérieur d'un bloc. * Sa déclaration est précédée du mot-clé `extern'. * Il existe déjà une autre déclaration de celui-ci avec liaison externe. * Il a donc une liaison externe. */ extern int a;
/* * `b' est un identificateur d'objet déclaré à l'intérieur d'un bloc. * Sa déclaration est précédée du mot-clé `extern'. * Il existe déjà une autre déclaration de celui-ci avec liaison interne. * Il a donc une liaison interne. */ extern int b;
/* * `c' est un identificateur d'objet déclaré à l'intérieur d'un bloc. * Sa déclaration n'est pas précédé du mot-clé `extern'. * Il n'a donc pas de liaison. */ int c;
/* * `d' est un identificateur d'objet déclaré à l'intérieur d'un bloc. * Sa déclaration est précédée du mot-clé `extern'. * Aucune déclaration du même identificateur ne le précède. * Il a donc une liaison externe. */ extern int d;
/* * `g' est un identificateur de fonction. * Sa déclaration n'est pas précédée du mot-clé `extern'. * Dès lors, il faut faire comme si elle était précédée du mot-clé `extern'. * Il existe déjà une autre déclaration de celui-ci avec liaison interne. * Il a donc une liaison interne. */ void g(void);
return 0; }
|
Notez que le mot-clé static ne peut être utilisé, pour modifier la liaison d'un identificateur, qu'
en dehors de tout bloc et ce, aussi bien pour les identificateurs d'objet que les identificateurs de fonction !
La notion de définitionJe vous ai dit que la notion de liaison permettait de grouper des
identificateurs et de les considérer comme faisant référence au même
objet ou à la même fonction. Je vous ai également dit que tous les
identificateurs avec liaison externe d'un même programme font référence
au même objet ou à la même fonction et que tous les identificateurs avec
liaison interne d'un même fichier font référence au même objet ou à la
même fonction.
Cependant, il y a un corollaire qui découle de ces deux règles : il ne
peut exister qu'un seul objet ou qu'une seule fonction qui puisse être
référencé par le groupe d'identificateurs.
Au fond, c'est assez logique. Prenez l'exemple ci-dessous,
l'identificateur de fonction « f » déclaré dans le bloc de la fonction main a une liaison interne. Cependant, laquelle des deux fonctions désigne-t-il ? La première ? La deuxième ? Les deux ?
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static int f(void) { return 1; }
static int f(void) { return 2; }
int main(void) { f(); return 0; }
|
Il est impossible de le dire, il faudrait qu'il n'existe qu'une seule
fonction ou, dit plus formellement, qu'il n'y ait qu'une seule
définition de la fonction f. Qu'est-ce qu'une définition ? C'est ce que nous allons voir tout de suite.
Les identificateurs de fonctionUne définition d'un identificateur de fonction est une déclaration qui comporte le corps de la fonction.
Autrement dit, le code ci-dessous est une définition de
l'identificateur de fonction « f », car il comporte le corps de la
fonction, alors que le suivant est une déclaration de l'identificateur
de fonction « f ».
Code : C - Définition de l'identificateur de fonction « f » -
1 2 3 4 5 | int f(void) { return 1; }
|
Code : C - Déclaration de l'identificateur de fonction « f » -
Les identificateurs d'objetUne définition d'un identificateur d'objet est une déclaration qui alloue l'objet qu'il référence
. Vous voilà bien peu avancé.
Heureusement, il y a une règle simple et absolue pour différencier une
déclaration et une définition d'un identificateur d'objet : une
déclaration d'un identificateur d'objet, en dehors de tout bloc,
comportant une initialisation est une définition. Dans tous les autres cas, il s'agit d'une déclaration.
Code : C -
1 2 3 4 | int a; /* Déclaration */ static int b; /* Déclaration */ extern int c; /* Déclaration */ int d = 10; /* Définition */
|
Cependant, il y a une petite (
) subtilité : les déclarations d'identificateurs d'objet, en dehors de tout bloc, à l'exception de celles précédées du mot-clé extern, sont appelées des
définitions potentielles.
Et, dans le cas où un fichier comprend une ou plusieurs définitions
potentielles d'un identificateur d'objet mais aucune définition de cet
identificateur, une définition est implicitement incluse au début du
fichier avec un initialiseur valant zéro.
Je vois d'ici vos regards hagards et terrifiés face à cette règle, rassurez vous, nous allons revoir cela en douceur.
Avant toute chose, il est nécessaire que vous fassiez bien la différence
entre une déclaration, une définition potentielle et une définition
d'un identificateur d'objet. Pour ce faire, voici un petit exemple
simple.
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* * Cette déclaration ne comporte pas d'initialisation. * Elle n'est pas précédée du mot-clé `extern'. * Il s'agit donc d'une définition potentielle. */ int n;
/* * Cette déclaration comporte une initialisation. * Il s'agit donc d'une définition. */ extern int n = 10;
/* * Cette déclaration ne comporte pas d'initialisation. * Elle n'est pas précédée du mot-clé `extern'. * Il s'agit donc d'une définition potentielle. */ static int n;
/* * Cette déclaration ne comporte pas d'initialisation. * Elle est précédée du mot-clé `extern'. * Il s'agit donc d'une déclaration. */ extern int n;
|
Ensuite, reprenons cette horrible règle pas à pas à l'aide du code ci-dessous.
Code : C -
1 2 3 4 5 6 7 | int n;
int main(void) { return n; }
|
Comme vous le voyez, nous avons un fichier comprenant une définition
potentielle de l'identificateur d'objet « n », mais aucune définition de
cet identificateur.
Ce que dit l'obscure règle que je vous ai présentée auparavant, c'est
que dans le cas où un fichier comprend une ou plusieurs définitions
potentielles d'un identificateur mais aucune définition de cet
identificateur (ce qui est le cas de notre fichier), une définition est
implicitement inclue au début de ce fichier avec un initialiseur valant
zéro. Autrement dit, appliquée à notre exemple, cela donne ceci :
Code : C -
1 2 3 4 5 6 7 8 9 | /* Définition implicite */ int n = 0; int n;
int main(void) { return n; }
|
Formalisation de l'interdictionMaintenant que nous avons vu la notion de définition, il m'est possible
de formaliser ce que je vous ai dit au début de la présentation de cette
notion : il ne peut exister qu'un seul objet ou qu'une seule fonction
qui puisse être référencée par un groupe d'identificateur. Ou, dit de
manière plus formelle :
- il ne peut y avoir qu'une seule définition d'un même identificateur avec liaison externe dans tout le programme ;
- il ne peut y avoir qu'une seule définition d'un même identificateur avec liaison interne dans un même fichier.
Certains compilateurs (gcc
pour ne citer que lui) sont par défaut capables de gérer certains cas
de définitions multiples. Sachez cependant qu'il s'agit d'une extension
non standard. Dans le cas de gcc, il est possible de désactiver cette extension en utilisant l'option -fno-common.
Le code ci-dessous est donc incorrect car il comporte plus d'une définition avec liaison interne de l'identificateur « n ».
Code : C -
1 2 3 4 5 6 7 8 9 | static int n = 10; static int n = 20;
int main(void) { return n; }
|
De même, le code qui suit est faux car il existe plus d'une définition
avec liaison externe de l'identificateur « n » dans tout le programme
(n'oubliez pas la définition implicite !).
Code : C - autre.c -
Code : C - main.c -
1 2 3 4 5 6 7 | int n;
int main(void) { return n; }
|
Notez enfin que si un identificateur apparaît dans un fichier avec à la
fois une liaison externe et interne, le résultat est indéterminé.
Code : C - autre.c -
1 2 3 4 5 | int f(void) { return 1; }
|
Code : C - main.c -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int f(void);
static int f(void) { return 2; }
int main(void) { f(); return 0; }
|
Dans cet exemple, l'identificateur « f » du fichier main.c a à la fois une liaison externe et interne. Il est donc impossible de dire à quelle fonction il fait référence.
En brefQue retenir de ce chapitre si ce n'est qu'il est affreusement théorique et complexe ?
En fait, il est possible d'en déduire une méthode générale afin de
partager des variables ou des fonctions entre plusieurs fichiers source.
Étant donné que les fichiers d'en-têtes sont très souvent inclus dans
plusieurs fichiers (pensez à ceux de la bibliothèque standard par
exemple), ces derniers ne doivent contenir que des déclarations. En
effet, si ce n'est pas le cas, vous allez vous retrouver avec des
définitions multiples (explicites ou implicites) et, dès lors,
rencontrer des erreurs lors de la compilation. Les fichiers source,
quant à eux, recueillent donc les définitions.
Ainsi, lorsque vous souhaitez utiliser une ou plusieurs variables ou
fonctions définies dans un autre fichier, vous incluez le ou les
fichiers d'en-tête comprenant leur déclarations dans les fichiers source
où vous souhaitez les utiliser. Cette méthode a l'avantage d'éviter
d'avoir à réécrire toutes les déclarations dans chaque fichier.
L'exemple ci-dessous illustre ce qui vient d'être exposé.
Code : C - autre.h -
1 2 3 4 5 6 7 | #ifndef AUTRE_H #define AUTRE_H
extern int n; /* Déclaration */ extern void setn(int); /* Déclaration */
#endif /* AUTRE_H */
|
Code : C - autre.c -
1 2 3 4 5 6 7 8 9 10 11 12 | /* Inclusion des déclarations */ #include "autre.h"
/* Définition potentielle */ int n;
/* Définition */ void setn(int a) { n = a; }
|
Code : C - main.c -
1 2 3 4 5 6 7 8 9 10 11 12 | /* Inclusion des déclarations */ #include <stdio.h> #include "autre.h"
int main(void) { printf("%d\n", n); /* 0 */ setn(99); printf("%d\n", n); /* 99 */ return 0; }
|
Les noms
Nous allons à présent
terminer notre tour d'horizon des identificateurs avec un sujet plus
léger et nettement plus simple (ouf !) : le nom des identificateurs.
Caractères utilisables
Un nom est composé d'une suite de lettres et de chiffres. Oui, mais
quelles lettres et quels chiffres ? La liste exhaustive nous est donnée
par la norme.
Citation : Caractères utilisables
a b c d e f g h i j k l m
n o p q r s t u v w x y z
A B C D E F G H I J K L M
N O P Q R S T U V W X Y Z
0 1 2 3 4 5 6 7 8 9 _
Sachez qu'un nom ne peut pas commencer par un chiffre, il doit obligatoirement débuter par une lettre ou par un underscore.
Noms réservés par le langageNous savons désormais de quels caractères peuvent être composés nos
noms. Cependant, tous les noms ne sont pas utilisables. En effet,
certains sont réservés par le langage C lui-même et ne sont donc pas
disponibles.
Code : C - Mots-clés du langage C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | auto if unsigned break inline void case int volatile char long while const register _Alignas continue restrict _Alignof default return _Atomic do short _Bool double signed _Complex else sizeof _Generic enum static _Imaginary extern struct _Noreturn float switch _Static_assert for typedef _Thread_local goto union
|
Il est à noter que certaines implémentations réservent aussi les mots asm et fortran. Il est donc également préférable de les éviter.
Noms réservés par la bibliothèque standard
À côté des noms réservés par le langage lui-même, il y a ceux réservés
par la bibliothèque standard. En fait, tous les noms de fonctions (par
exemple printf) ou de variables (par exemple errno)
utilisés par celle-ci sont à éviter, même si vous n'incluez pas
l'en-tête les utilisant. Renseignez-vous sur les différents en-têtes
pour obtenir les noms qu'ils emploient.
En plus de cela, la bibliothèque standard réserve certains types de noms dans des portées particulières. Ainsi, sont interdits :
- les noms commençant par un underscore et une lettre majuscule ou commençant par deux underscore et ce, peu importe leur portée;
- les noms commençant par un underscore et ayant une portée au niveau d'un fichier.
Afin de bien cerner cette interdiction, voici un petit d'exemple.
Code : C -
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #define _HELLO /* Interdit */ #define __HELLO /* Interdit */ #define __hello /* Interdit */ #define _hello /* Interdit car il a une portée au niveau d'un fichier */
struct _structure { /* Interdit pour les même motifs */ int _membre; /* Permis */ };
int main(void) { int _variable; /* Permis car il n'a pas une portée au niveau d'un fichier */ int auto; /* Interdit car c'est un mot réservé par le langage */ return 0; }
|
Remarquez enfin que dans le cas de l'en-tête <errno.h>, les noms de macro commençant par un E et un chiffre ou une lettre majuscule ne doivent pas non plus être employés, de même pour l'en-tête <signal.h> et les noms de macro commençant par SIG ou SIG_ et une lettre majuscule.