Namespaces
Variants

Lookup and name spaces

From cppreference.net

Lorsqu'un identifiant est rencontré dans un programme C, une recherche est effectuée pour localiser la déclaration qui a introduit cet identifiant et qui est actuellement dans la portée . Le C permet à plusieurs déclarations du même identifiant d'être simultanément dans la portée si ces identifiants appartiennent à différentes catégories, appelées espaces de noms :

1) Espace de noms des étiquettes : tous les identifiants déclarés comme labels .
2) Noms de balises : tous les identifiants déclarés comme noms de structs , unions et types énumérés . Notez que les trois types de balises partagent un même espace de noms.
3) Noms des membres : tous les identifiants déclarés comme membres de toute struct ou union . Chaque struct et union introduit son propre espace de noms de ce type.
4) Espace de noms des attributs globaux : jetons d'attribut définis par la norme ou préfixes d'attributs définis par l'implémentation.
5) Noms d'attributs non standard : noms d'attributs suivant les préfixes d'attributs. Chaque préfixe d'attribut possède un espace de noms distinct pour les attributs définis par l'implémentation qu'il introduit.
(depuis C23)
6) Tous les autres identifiants, appelés identifiants ordinaires pour les distinguer de (1-5) (noms de fonctions, noms d'objets, noms typedef, constantes d'énumération).

Au moment de la recherche, l'espace de noms d'un identifiant est déterminé par la manière dont il est utilisé :

1) l'identifiant apparaissant comme opérande d'une instruction goto est recherché dans l'espace de noms des étiquettes.
2) l'identifiant qui suit le mot-clé struct , union , ou enum est recherché dans l'espace de noms des étiquettes.
3) l'identifiant qui suit l'opérateur d' accès aux membres ou d'accès aux membres via un pointeur est recherché dans l'espace de noms des membres du type déterminé par l'opérande gauche de l'opérateur d'accès aux membres.
4) L'identifiant qui apparaît directement dans un spécificateur d'attribut ( [ [ ... ] ] ) est recherché dans l'espace de noms global des attributs.
5) L'identifiant qui suit le jeton :: suivant un préfixe d'attribut est recherché dans l'espace de noms introduit par le préfixe d'attribut.
(depuis C23)
6) tous les autres identifiants sont recherchés dans l'espace de noms des identifiants ordinaires.

Table des matières

Notes

Les noms des macros ne font partie d'aucun espace de noms car ils sont remplacés par le préprocesseur avant l'analyse sémantique.

Il est courant d'injecter les noms de struct/union/enum dans l'espace de noms des identifiants ordinaires en utilisant une déclaration typedef :

struct A { };       // introduit le nom A dans l'espace de noms des balises
typedef struct A A; // d'abord, la recherche de A après "struct" en trouve un dans l'espace de noms des balises
                    // puis introduit le nom A dans l'espace de noms ordinaire
struct A* p;        // OK, ce A est recherché dans l'espace de noms des balises
A* q;               // OK, ce A est recherché dans l'espace de noms ordinaire

Un exemple bien connu du même identifiant utilisé dans deux espaces de noms est l'identifiant stat de l'en-tête POSIX sys/stat.h . Il désigne une fonction lorsqu'il est utilisé comme identifiant ordinaire et indique une structure lorsqu'il est utilisé comme tag.

Contrairement à C++, les constantes d'énumération ne sont pas des membres de structure, et leur espace de noms est l'espace de noms des identifiants ordinaires, et puisqu'il n'y a pas de portée de structure en C, leur portée est la portée dans laquelle la déclaration de structure apparaît :

struct tagged_union {
   enum {INT, FLOAT, STRING} type;
   union {
      int integer;
      float floating_point;
      char *string;
   };
} tu;
tu.type = INT; // Correct en C, erreur en C++

Si un attribut standard, un préfixe d'attribut ou un nom d'attribut non standard n'est pas pris en charge, l'attribut non valide lui-même est ignoré sans provoquer d'erreur.

(depuis C23)

Exemple

void foo (void) { return; } // espace de noms ordinaire, portée fichier
struct foo {      // espace de noms d'étiquette, portée fichier
    int foo;      // espace de noms membre pour cette struct foo, portée fichier
    enum bar {    // espace de noms d'étiquette, portée fichier
        RED       // espace de noms ordinaire, portée fichier
    } bar;        // espace de noms membre pour cette struct foo, portée fichier
    struct foo* p; // OK : utilise le nom "foo" d'étiquette/portée fichier
};
enum bar x; // OK : utilise bar d'étiquette/portée fichier
// int foo; // Erreur : espace de noms ordinaire foo déjà dans la portée
//union foo { int a, b; }; // Erreur : espace de noms d'étiquette foo dans la portée
int main(void)
{
    goto foo; // OK utilise "foo" de l'espace de noms d'étiquette/portée fonction
    struct foo { // espace de noms d'étiquette, portée bloc (masque la portée fichier)
       enum bar x; // OK, utilise "bar" de l'espace de noms d'étiquette/portée fichier
    };
    typedef struct foo foo; // OK : utilise foo de l'espace de noms d'étiquette/portée bloc
                            // définit foo ordinaire de portée bloc (masque la portée fichier)
    (foo){.x=RED}; // utilise foo ordinaire/portée bloc et RED ordinaire/portée fichier
foo:; // espace de noms d'étiquette, portée fonction
}

Références

  • Norme C17 (ISO/CEI 9899:2018) :
  • 6.2.3 Espaces de noms des identificateurs (p: 29-30)
  • Norme C11 (ISO/CEI 9899:2011) :
  • 6.2.3 Espaces de noms des identificateurs (p: 37)
  • Norme C99 (ISO/CEI 9899:1999) :
  • 6.2.3 Espaces de noms des identificateurs (p: 31)
  • Norme C89/C90 (ISO/IEC 9899:1990) :
  • 3.1.2.3 Espaces de noms des identificateurs

Voir aussi

Documentation C++ pour Name lookup