web-dev-qa-db-fra.com

Qu'est-ce qui fait qu'une variable statique ne s'initialise qu'une seule fois?

J'ai remarqué que si vous initialisez une variable statique en C++ dans le code, l'initialisation ne s'exécute que la première fois que vous exécutez la fonction.

C'est cool, mais comment est-ce mis en œuvre? Cela se traduit-il par une sorte de déclaration tordue si? (si on lui donne une valeur, alors ..)

void go( int x )
{
    static int j = x ;
    cout << ++j << endl ; // see 6, 7, 8
} 

int main()
{
    go( 5 ) ;
    go( 5 ) ;
    go( 5 ) ; 
}
46
bobobobo

Oui, cela se traduit normalement par une instruction if implicite avec un indicateur booléen interne. Donc, dans l'implémentation la plus élémentaire, votre déclaration se traduit normalement par quelque chose comme

void go( int x ) {
  static int j;
  static bool j_initialized;

  if (!j_initialized) {
    j = x;
    j_initialized = true;
  }

  ...
} 

De plus, si votre objet statique a un destructeur non trivial, le langage doit obéir à une autre règle: ces objets statiques doivent être détruits dans l'ordre inverse de leur construction. Étant donné que l'ordre de construction n'est connu qu'au moment de l'exécution, l'ordre de destruction est également défini au moment de l'exécution. Ainsi, chaque fois que vous construisez un objet statique local avec un destructeur non trivial, le programme doit l'enregistrer dans une sorte de conteneur linéaire, qu'il utilisera plus tard pour détruire ces objets dans le bon ordre.

Inutile de dire que les détails réels dépendent de la mise en œuvre.


Il convient d'ajouter que lorsqu'il s'agit d'objets statiques de types "primitifs" (comme int dans votre exemple) initialisés avec des constantes de compilation, le compilateur est libre d'initialiser cet objet au démarrage. Vous ne remarquerez jamais la différence. Cependant, si vous prenez un exemple plus compliqué avec un objet "non primitif"

void go( int x ) {
  static std::string s = "Hello World!";
  ...

alors l'approche ci-dessus avec if est ce que vous devez vous attendre à trouver dans le code généré même lorsque l'objet est initialisé avec une constante de compilation.

Dans votre cas, l'initialiseur n'est pas connu au moment de la compilation, ce qui signifie que le compilateur doit retarder l'initialisation et utiliser cet implicite if.

51
AnT

Oui, le compilateur génère généralement un booléen caché "a-t-il été initialisé?" et un if qui s'exécute à chaque exécution de la fonction.

Il y a plus de matériel de lecture ici: Comment l'initialisation des variables statiques est-elle implémentée par le compilateur?

7
Jon

Bien qu'il s'agisse en effet "d'une sorte de torsion si", la torsion peut être plus que ce que vous imaginiez ...

Le commentaire de ZoogieZork sur la réponse d'AndreyT touche à un aspect important: le initialisation des variables locales statiques - on certains compilateurs y compris GCC - est par défaut thread-safe (une option de ligne de commande du compilateur peut la désactiver). Par conséquent, il utilise un mécanisme de synchronisation inter-threads (une opération mutex ou atomique quelconque) qui peut être relativement lent. Si vous ne seriez pas à l'aise - en termes de performances - avec l'utilisation explicite d'une telle opération dans votre fonction, alors vous devriez vous demander s'il existe une alternative à moindre impact à l'initialisation paresseuse de la variable (c'est-à-dire la construire explicitement vous-même de manière sécurisée pour les threads) quelque part juste une fois). Très peu de fonctions sont si sensibles aux performances que cela compte, cependant - ne laissez pas gâcher votre journée ou compliquez votre code, à moins que vos programmes ne soient trop lents et que votre profileur ne touche cette zone.

2
Tony Delroy

Ils ne sont initialisés qu'une seule fois car c'est ce que la norme C++ exige. La façon dont cela se produit dépend entièrement des fournisseurs de compilateurs. D'après mon expérience, un indicateur local caché est généré et utilisé par le compilateur.

1
Jeffrey Faust