Namespaces
Variants

Scope

From cppreference.net

Chaque identifiant qui apparaît dans un programme C est visible (c'est-à-dire peut être utilisé) uniquement dans une portion potentiellement non contiguë du code source appelée sa portée .

Dans une portée, un identifiant peut désigner plus d'une entité uniquement si les entités se trouvent dans différents name spaces .

C possède quatre types de portées :

  • portée de bloc
  • portée de fichier
  • portée de fonction
  • portée de prototype de fonction

Table des matières

Portées imbriquées

Si deux entités différentes nommées par le même identifiant sont dans la portée en même temps, et qu'elles appartiennent au même espace de noms , les portées sont imbriquées (aucune autre forme de chevauchement de portée n'est autorisée), et la déclaration qui apparaît dans la portée interne masque la déclaration qui apparaît dans la portée externe :

// L'espace de nommage ici contient des identifiants ordinaires.
int a;   // la portée au niveau fichier du nom a commence ici
void f(void)
{
    int a = 1; // la portée au niveau bloc du nom a commence ici ; masque le a de portée fichier
    {
      int a = 2;         // la portée du a interne commence ici, le a externe est masqué
      printf("%d\n", a); // le a interne est dans la portée, affiche 2
    }                    // la portée au niveau bloc du a interne se termine ici
    printf("%d\n", a);   // le a externe est dans la portée, affiche 1
}                        // la portée du a externe se termine ici
void g(int a);   // le nom a a une portée de prototype de fonction ; masque le a de portée fichier

Portée de bloc

La portée de tout identifiant déclaré à l'intérieur d'une instruction composée , y compris les corps de fonctions, ou dans toute expression, déclaration ou instruction apparaissant dans une if , switch , for , while , ou do-while (depuis C99) , ou dans la liste de paramètres d'une définition de fonction commence au point de déclaration et se termine à la fin du bloc ou de l'instruction dans lequel il a été déclaré.

void f(int n)  // portée du paramètre de fonction 'n' commence
{         // le corps de la fonction commence
   ++n;   // 'n' est dans la portée et fait référence au paramètre de fonction
// int n = 2; // erreur : ne peut pas redéclarer l'identifiant dans la même portée
   for(int n = 0; n<10; ++n) { // portée du 'n' local à la boucle commence
       printf("%d\n", n); // affiche 0 1 2 3 4 5 6 7 8 9
   } // portée du 'n' local à la boucle se termine
     // le paramètre de fonction 'n' est de nouveau dans la portée
   printf("%d\n", n); // affiche la valeur du paramètre
} // portée du paramètre de fonction 'n' se termine
int a = n; // Erreur : le nom 'n' n'est pas dans la portée

Jusqu'à C99, les instructions de sélection et d'itération n'établissaient pas leur propre portée de bloc (bien que si une instruction composée était utilisée dans l'instruction, elle avait sa portée de bloc habituelle) :

enum {a, b};
int different(void)
{
    if (sizeof(enum {b, a}) != sizeof(int))
        return a; // a == 1
    return b; // b == 0 in C89, b == 1 in C99
}
(depuis C99)

Les variables de portée de bloc ont pas de liaison et une durée de stockage automatique par défaut. Notez que la durée de stockage pour les variables locales non-VLA commence lorsque le bloc est entré, mais jusqu'à ce que la déclaration soit vue, la variable n'est pas dans la portée et ne peut pas être accédée.

Portée de fichier

La portée de tout identifiant déclaré en dehors de tout bloc ou liste de paramètres commence au point de déclaration et se termine à la fin de l'unité de traduction.

int i; // portée de i commence
static int g(int a) { return a; } // portée de g commence (note : "a" a une portée de bloc)
int main(void)
{
    i = g(2); // i et g sont dans la portée
}

Les identifiants de portée de fichier ont une liaison externe et une durée de stockage statique par défaut.

Portée de fonction

Une étiquette (et uniquement une étiquette) déclarée à l'intérieur d'une fonction est dans la portée partout dans cette fonction, dans tous les blocs imbriqués, avant et après sa propre déclaration. Note : une étiquette est déclarée implicitement, en utilisant un identifiant autrement inutilisé avant le caractère deux-points précédant toute instruction.

void f()
{
   {   
       goto label; // label dans la portée même s'il est déclaré plus tard
label:;
   }
   goto label; // label ignore la portée du bloc
}
void g()
{
    goto label; // erreur : label non dans la portée dans g()
}

Portée du prototype de fonction

La portée d'un nom introduit dans la liste des paramètres d'une déclaration de fonction qui n'est pas une définition se termine à la fin du déclarateur de fonction.

int f(int n,
      int a[n]); // n est dans la portée et fait référence au premier paramètre

Notez que s'il y a plusieurs déclarateurs ou des déclarateurs imbriqués dans la déclaration, la portée se termine à la fin du déclarateur de fonction englobant le plus proche :

void f ( // le nom de fonction 'f' est au niveau de la portée du fichier
 long double f,            // l'identifiant 'f' est maintenant dans la portée, le 'f' de portée fichier est masqué
 char (**a)[10 * sizeof f] // 'f' fait référence au premier paramètre, qui est dans la portée
);
enum{ n = 3 };
int (*(*g)(int n))[n]; // la portée du paramètre de fonction 'n'
                       // se termine à la fin de son déclarateur de fonction
                       // dans le déclarateur de tableau, le n global est dans la portée
// (ceci déclare un pointeur vers une fonction retournant un pointeur vers un tableau de 3 int)

Point de déclaration

La portée des étiquettes de structure, d'union et d'énumération commence immédiatement après l'apparition de l'étiquette dans un spécificateur de type qui déclare l'étiquette.

struct Node {
   struct Node* next; // Node est dans la portée et fait référence à cette structure
};

La portée d'une constante d'énumération commence immédiatement après l'apparition de son énumérateur définissant dans une liste d'énumérateurs.

enum { x = 12 };
{
    enum { x = x + 1, // le nouveau x n'est pas dans la portée jusqu'à la virgule, x est initialisé à 13
           y = x + 1  // le nouvel énumérateur x est maintenant dans la portée, y est initialisé à 14
         };
}

La portée de tout autre identifiant commence juste après la fin de son déclarateur et avant l'initialiseur, s'il y en a un :

int x = 2; // portée du premier 'x' commence
{
    int x[x]; // portée du nouveau x déclaré commence après le déclarateur (x[x]).
              // Dans le déclarateur, le 'x' externe est toujours dans la portée.
              // Ceci déclare un tableau VLA de 2 int.
}
unsigned char x = 32; // portée du 'x' externe commence
{
    unsigned char x = x;
            // portée du 'x' interne commence avant l'initialiseur (= x)
            // cela n'initialise pas le 'x' interne avec la valeur 32,
            // cela initialise le 'x' interne avec sa propre valeur indéterminée
}
unsigned long factorial(unsigned long n)
// déclarateur se termine, 'factorial' est dans la portée à partir de ce point
{
   return n<2 ? 1 : n*factorial(n-1); // appel récursif
}

En tant que cas particulier, la portée d'un nom de type qui n'est pas une déclaration d'un identifiant est considérée comme commençant juste après l'emplacement dans le nom de type où l'identifiant apparaîtrait s'il n'était pas omis.

Notes

Avant C89, les identifiants avec liaison externe avaient une portée de fichier même lorsqu'ils étaient introduits dans un bloc, et pour cette raison, un compilateur C89 n'est pas tenu de diagnostiquer l'utilisation d'un identifiant extern qui est sorti de la portée (une telle utilisation est un comportement indéfini).

Les variables locales dans le corps d'une boucle peuvent masquer les variables déclarées dans la clause d'initialisation d'une boucle for en C (leur portée est imbriquée), mais ne peuvent pas le faire en C++.

Contrairement au C++, le C n'a pas de portée de struct : les noms déclarés dans une déclaration struct/union/enum sont dans la même portée que la déclaration de la struct (sauf que les membres de données sont dans leur propre espace de noms des membres ):

struct foo {
    struct baz {};
    enum color {RED, BLUE};
};
struct baz b; // baz est dans la portée
enum color x = RED; // color et RED sont dans la portée

Références

  • Norme C23 (ISO/CEI 9899:2024) :
  • 6.2.1 Portées des identificateurs, noms de types et littéraux composés (p: TBD)
  • Norme C17 (ISO/CEI 9899:2018) :
  • 6.2.1 Portées des identificateurs (p: 28-29)
  • Norme C11 (ISO/CEI 9899:2011) :
  • 6.2.1 Portées des identificateurs (p: 35-36)
  • Norme C99 (ISO/CEI 9899:1999) :
  • 6.2.1 Portées des identificateurs (p: 29-30)
  • Norme C89/C90 (ISO/CEI 9899:1990) :
  • 3.1.2.1 Portées des identificateurs

Voir aussi

Documentation C++ pour Portée