Namespaces
Variants

Storage-class specifiers

From cppreference.net

Spécifiez la durée de stockage et la liaison des objets et fonctions :

  • auto - durée automatique et aucune liaison
  • register - durée automatique et aucune liaison ; l'adresse de cette variable ne peut pas être prise
  • static - durée statique et liaison interne (sauf au niveau bloc)
  • extern - durée statique et liaison externe (sauf si déjà déclaré interne)
  • _Thread_local (jusqu'au C23) thread_local (depuis C23) - durée de stockage des threads
(depuis C11)

Table des matières

Explication

Les spécificateurs de classe de stockage apparaissent dans les déclarations et les expressions de littéral composé (depuis C23) . Au plus un spécificateur peut être utilisé , sauf que _Thread_local (jusqu'à C23) thread_local (depuis C23) peut être combiné avec static ou extern pour ajuster la liaison (depuis C11) . Les spécificateurs de classe de stockage déterminent deux propriétés indépendantes des noms qu'ils déclarent : la durée de stockage et la liaison .

1) Le auto spécificateur est uniquement autorisé pour les objets déclarés au niveau de la portée de bloc (sauf les listes de paramètres de fonction). Il indique une durée de stockage automatique et aucune liaison, qui sont les valeurs par défaut pour ce type de déclarations.
2) Le spécificateur register est uniquement autorisé pour les objets déclarés au niveau de la portée de bloc, y compris les listes de paramètres de fonction. Il indique une durée de stockage automatique et aucune liaison (ce qui est la valeur par défaut pour ce type de déclarations), mais suggère en plus à l'optimiseur de stocker la valeur de cette variable dans un registre du processeur si possible. Que cette optimisation ait lieu ou non, les variables déclarées register ne peuvent pas être utilisées comme arguments pour l' opérateur d'adressage , ne peuvent pas utiliser _Alignas (jusqu'à C23) alignas (depuis C23) (depuis C11) , et les tableaux register ne sont pas convertibles en pointeurs.
3) Le static spécifie à la fois la durée de stockage statique (sauf s'il est combiné avec _Thread_local ) (depuis C11) et la liaison interne (sauf s'il est utilisé au niveau du bloc). Il peut être utilisé avec des fonctions au niveau du fichier et avec des variables aux niveaux du fichier et du bloc, mais pas dans les listes de paramètres de fonction.
4) Le extern spécifie une durée de stockage statique (sauf s'il est combiné avec _Thread_local (jusqu'en C23) thread_local (depuis C23) ) (depuis C11) et une liaison externe. Il peut être utilisé avec les déclarations de fonctions et d'objets dans les portées de fichier et de bloc (à l'exclusion des listes de paramètres de fonction). Si extern apparaît sur une redéclaration d'un identifiant déjà déclaré avec une liaison interne, la liaison reste interne. Sinon (si la déclaration précédente était externe, sans liaison, ou n'est pas dans la portée), la liaison est externe.
5) _Thread_local (jusqu'au C23) thread_local (depuis C23) indique la durée de stockage de thread . Il ne peut pas être utilisé avec les déclarations de fonctions. S'il est utilisé sur une déclaration d'objet, il doit être présent sur chaque déclaration du même objet. S'il est utilisé sur une déclaration de portée de bloc, il doit être combiné avec soit static soit extern pour déterminer la liaison.
(depuis C11)

Si aucun spécificateur de classe de stockage n'est fourni, les valeurs par défaut sont :

extern pour toutes les fonctions
extern pour les objets au niveau du fichier
auto pour les objets au niveau du bloc

Pour toute structure ou union déclarée avec un spécificateur de classe de stockage, la durée de stockage (mais pas la liaison) s'applique à leurs membres, de manière récursive.

Les déclarations de fonctions au niveau du bloc peuvent utiliser extern ou rien du tout. Les déclarations de fonctions au niveau du fichier peuvent utiliser extern ou static .

Les paramètres de fonction ne peuvent utiliser aucun spécificateur de classe de stockage autre que register . Notez que static a une signification particulière dans les paramètres de fonction de type tableau.

Durée de stockage

Chaque objet possède une propriété appelée durée de stockage , qui limite la durée de vie de l'objet. Il existe quatre types de durée de stockage en C :

  • automatic durée de stockage automatique. Le stockage est alloué lorsque le bloc dans lequel l'objet a été déclaré est entré et désalloué lorsqu'il est quitté par n'importe quel moyen ( goto , return , atteignant la fin). Une exception concerne les VLA ; leur stockage est alloué lorsque la déclaration est exécutée, non à l'entrée du bloc, et désalloué lorsque la déclaration sort de la portée, non lorsque le bloc est quitté (depuis C99) . Si le bloc est entré récursivement, une nouvelle allocation est effectuée pour chaque niveau de récursion. Tous les paramètres de fonction et les objets de portée de bloc non static ont cette durée de stockage, ainsi que les littéraux composés utilisés au niveau du bloc (jusqu'à C23)
  • static durée de stockage statique. La durée de stockage est l'exécution entière du programme, et la valeur stockée dans l'objet est initialisée une seule fois, avant la fonction main . Tous les objets déclarés static et tous les objets avec liaison interne ou externe qui ne sont pas déclarés _Thread_local (jusqu'à C23) thread_local (depuis C23) (depuis C11) ont cette durée de stockage.
  • thread durée de stockage. La durée de stockage correspond à l'exécution entière du thread dans lequel il a été créé, et la valeur stockée dans l'objet est initialisée lors du démarrage du thread. Chaque thread possède son propre objet distinct. Si le thread qui exécute l'expression accédant à cet objet n'est pas celui qui a exécuté son initialisation, le comportement est défini par l'implémentation. Tous les objets déclarés _Thread_local (jusqu'en C23) thread_local (depuis C23) ont cette durée de stockage.
(depuis C11)

Liaison

La liaison fait référence à la capacité d'un identifiant (variable ou fonction) à être référencé dans d'autres portées. Si une variable ou fonction avec le même identifiant est déclarée dans plusieurs portées, mais ne peut pas être référencée depuis toutes, alors plusieurs instances de la variable sont générées. Les liaisons suivantes sont reconnues :

  • aucune liaison . La variable ou la fonction ne peut être référencée que depuis la portée dans laquelle elle se trouve (portée de bloc). Toutes les variables de portée de bloc qui ne sont pas déclarées extern ont cette liaison, ainsi que tous les paramètres de fonction et tous les identifiants qui ne sont pas des fonctions ou des variables.
  • liaison interne . La variable ou fonction peut être référencée depuis toutes les portées dans l'unité de traduction courante. Toutes les variables de portée de fichier déclarées static ou constexpr (depuis C23) ont cette liaison, et toutes les fonctions de portée de fichier déclarées static (les déclarations de fonctions statiques sont uniquement autorisées au niveau de la portée de fichier).
  • external linkage . La variable ou fonction peut être référencée depuis n'importe quelle autre unité de traduction dans l'ensemble du programme. Toutes les variables de portée fichier qui ne sont pas déclarées static ou constexpr (since C23) ont cette liaison, toutes les déclarations de fonctions de portée fichier qui ne sont pas déclarées static , toutes les déclarations de fonctions de portée bloc, et, de plus, toutes les variables ou fonctions déclarées extern ont cette liaison, sauf si une déclaration antérieure avec liaison interne est visible à ce point.

Si le même identifiant apparaît avec une liaison interne et externe dans la même unité de traduction, le comportement est indéfini. Ceci est possible lorsque les définitions provisoires sont utilisées.

Liaison et bibliothèques

Les déclarations avec liaison externe sont généralement mises à disposition dans les fichiers d'en-tête afin que toutes les unités de traduction qui #include le fichier puissent faire référence aux mêmes identifiants définis ailleurs.

Toute déclaration avec une liaison interne qui apparaît dans un fichier d'en-tête résulte en un objet séparé et distinct dans chaque unité de traduction qui inclut ce fichier.

Interface de la bibliothèque, fichier d'en-tête "flib.h" :

#ifndef FLIB_H
#define FLIB_H
void f(void);              // déclaration de fonction avec liaison externe
extern int state;          // déclaration de variable avec liaison externe
static const int size = 5; // définition d'une variable en lecture seule avec liaison interne
enum { MAX = 10 };         // définition de constante
inline int sum (int a, int b) { return a + b; } // définition de fonction inline
#endif // FLIB_H

Implémentation de la bibliothèque, fichier source "flib.c" :

#include "flib.h"
static void local_f(int s) {} // définition avec liaison interne (utilisée uniquement dans ce fichier)
static int local_state;       // définition avec liaison interne (utilisée uniquement dans ce fichier)
int state;                       // définition avec liaison externe (utilisée par main.c)
void f(void) { local_f(state); } // définition avec liaison externe (utilisée par main.c)

Code d'application, fichier source "main.c" :

#include "flib.h"
int main(void)
{
    int x[MAX] = {size}; // utilise la constante et la variable en lecture seule
    state = 7;           // modifie state dans flib.c
    f();                 // appelle f() dans flib.c
}

Mots-clés

auto , register , static , extern , _Thread_local thread_local

Notes

Le mot-clé _Thread_local est généralement utilisé via la macro de commodité thread_local , définie dans l'en-tête <threads.h> .

(jusqu'à C23)

Les typedef et constexpr (depuis C23) sont formellement listés comme des spécificateurs de classe de stockage dans la grammaire du langage C, mais ne spécifient pas de stockage.

Le spécificateur auto est également utilisé pour l'inférence de type.

(depuis C23)

Les noms au niveau de la portée du fichier qui sont const et non extern ont une liaison externe en C (comme valeur par défaut pour toutes les déclarations de portée de fichier), mais une liaison interne en C++.

Exemple

#include <stdio.h>
#include <stdlib.h>
// static storage duration
int A;
int main(void)
{
    printf("&A = %p\n", (void*)&A);
    // automatic storage duration
    int A = 1;   // hides global A
    printf("&A = %p\n", (void*)&A);
    // allocated storage duration
    int* ptr_1 = malloc(sizeof(int));   // start allocated storage duration
    printf("address of int in allocated memory = %p\n", (void*)ptr_1);
    free(ptr_1);                        // stop allocated storage duration
}

Sortie possible :

&A = 0x600ae4
&A = 0x7ffefb064f5c
address of int in allocated memory = 0x1f28c30

Références

  • Norme C23 (ISO/CEI 9899:2024) :
  • 6.2.2 Liaisons des identificateurs (p: 35-36)
  • 6.2.4 Durées de stockage des objets (p: 36-37)
  • 6.7.1 Spécificateurs de classe de stockage (p: 97-100)
  • Norme C17 (ISO/CEI 9899:2018) :
  • 6.2.2 Liaisons des identificateurs (p : 29-30)
  • 6.2.4 Durées de stockage des objets (p : 30)
  • 6.7.1 Spécificateurs de classe de stockage (p : 79)
  • Norme C11 (ISO/CEI 9899:2011) :
  • 6.2.2 Liaisons des identificateurs (p: 36-37)
  • 6.2.4 Durées de stockage des objets (p: 38-39)
  • 6.7.1 Spécificateurs de classe de stockage (p: 109-110)
  • Norme C99 (ISO/CEI 9899:1999) :
  • 6.2.2 Liaisons des identificateurs (p : 30-31)
  • 6.2.4 Durées de stockage des objets (p : 32)
  • 6.7.1 Spécificateurs de classe de stockage (p : 98-99)
  • Norme C89/C90 (ISO/CEI 9899:1990) :
  • 3.1.2.2 Liaisons des identificateurs
  • 3.1.2.4 Durées de stockage des objets
  • 3.5.1 Spécificateurs de classe de stockage

Voir aussi

Documentation C++ pour Spécificateurs de classe de stockage