web-dev-qa-db-fra.com

Pourquoi l'initialisation d'une variable externe localement dans une fonction génère-t-elle une erreur?

Ce code compile bien:

extern int i = 10;

void test()
{
    std::cout << "Hi" << i << std::endl;
}

Alors que celui-ci donne une erreur:

void test()
{
    extern int i = 10;
    std::cout << "Hi" << i << std::endl;
}

erreur: 'i' a 'extern' et initializer

Je lis dans C++ Primer

Toute déclaration incluant un initialiseur explicite est une définition . Nous pouvons fournir un initialiseur sur une variable définie comme extern, mais cela annule l'extern. Un externe qui a un initialiseur est un définition. C’est une erreur de fournir un initialiseur sur un externe dans un une fonction.

Quelqu'un peut-il expliquer pourquoi cela devrait être une erreur si cela est fait localement dans une fonction, alors que la même chose est autorisée dans une portée globale?

23
Amit Tomar

La raison pour laquelle définir une variable externe dans une fonction n'a pas de sens est la suivante:

Lorsque vous déclarez un symbole extern, vous indiquez au compilateur de lier toutes les occurrences de cette valeur dans le même symbole. Toute occurrence de extern int i; dans votre programme serait lié à la définition externe i. Regardez cet exemple:

#include <iostream>
using namespace std;

extern int i;
int i = 10;
void test()
{
    std::cout << "Hi" << i << std::endl;
}

int main()
{
    extern int i;
    i++;
    test();
}

Cet exemple devrait générer hi11. Cependant, si nous supprimons l'externe inside dans main, il en sortira 10. En effet, sans extern, i ne crée pas de lien vers le i global, mais crée sa propre copie locale de i. 

La raison pour laquelle définir un extern i dans une fonction n’a pas de sens, c’est ce qui si nous permettions à une fonction de "définir" i. Quelle fonction est exécutée en premier? Quand est-ce défini?

Supposons que l'exemple suivant soit valide, quelle serait la sortie ???

#include <iostream>
using namespace std;

extern int i;
int i = 10;
void test()
{
    std::cout << "Hi" << i << std::endl;
}

void test2() {
    extern int i = 1000;
    std::cout<< "HI" << i << std::endl;
}

void test3() {
    extern int i;
    i = 1000;
    std::cout<< "HI" << i << std::endl;
}

int main()
{
    extern int i;
    i++;
    test();
    i = 0;
    test2();
}

Le résultat de test2 doit-il être 0 ou 1 000? Regardez également mon test3. Ici, nous disons clairement: liez mon i au i défini en externe et attribuez-lui la valeur 1000. C'est très différent d'essayer d'initialiser une valeur.

En bref, les variables externes n’ont vraiment de sens que comme globales et doivent être définies dans une portée globale. Dans vos exemples, la première version ne compile pas non plus pour moi. Je trouve cela intéressant. Il serait peut-être utile de consulter la documentation sur les normes pour voir si cela est défini de manière concise ou si votre compilateur pourrait gérer cela de manière à ajouter une protection supplémentaire ... 

16
ChrisCM

En ajoutant un initialiseur à la déclaration, cela devient une définition de la variable globale. Cela équivaut à la même définition sans extern, ce qui est ce que votre livre veut dire quand il dit qu'il "écrase l'extern".

Bien que les variables globales puissent être déclarées (à l'aide de extern) à l'intérieur d'une fonction, elles ne peuvent pas y être définies, mais uniquement dans la portée de l'espace de noms. C'est pourquoi le deuxième extrait est une erreur.

Si vous voulez savoir pourquoi les concepteurs de C (d'où ces règles sont arrivées en C++) ont choisi d'autoriser les déclarations mais pas les définitions, alors je crains de ne pas connaître suffisamment l'historique de la langue pour pouvoir y répondre.

6
Mike Seymour

Au début, vous devriez vous familiariser avec la conception du lien et la signification du lien externe:

Un nom est dit avoir linkage lorsqu'il peut désigner le même objet, référence, fonction, type, modèle, espace de nom ou valeur sous forme de nom introduit par une déclaration d'une autre portée: 

Lorsqu'un nom a external linkage, l'entité qu'il désigne peut être désignée par des noms figurant dans le domaine d’application d’autres unités de traduction ou de autres portées de la même unité de traduction.
- 3.5.6.2 n3242

La fonction de static qui diffère de extern, extern est simplement une requête, static est une commande.

Le nom d'une fonction déclarée dans la portée du bloc et le nom d'un les variables déclarées par une déclaration externe de portée de bloc ont un lien. 

  • S'il existe une déclaration visible d'une entité avec une liaison ayant le même nom et le même type, ignorant les entités déclarées en dehors de la portée d'espace de noms la plus interne, la déclaration de portée de bloc déclare cette même entité et reçoit la liaison de la déclaration précédente. 
  • S'il existe plus d'une entité correspondante, le programme est mal formé.
  • Sinon, si aucune entité correspondante n'est trouvée, l'entité de portée de bloc reçoit une liaison externe. 

- 3.5.6.6 n3242

Par conséquent, dans la portée du bloc, la procédure ci-dessous est recommandée:

     extern int i;//declare it,request the linkage according to 3.5.6.6 above
     i = 10;//modify it when has link to a defination

Pour la déclaration globale globale, il est possible de convertir le formulaire

     extern int i =10;

à

     extern int i;//include in .hpp is recommended 
     int i =10;//global or namespace variable defination
4
yuan

Les variables extern sont initialisées avant l'exécution d'une fonction: en.cppreference.com/w/cpp/language/initialization#Non-local_variables

S'il était déclaré static plutôt que extern dans un bloc de fonction, il aurait toujours une durée de stockage static, mais son 'linkage serait local par rapport à cette fonction vs external . Ainsi, il serait initialisé lors de la première exécution de cette ligne dans la fonction: en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

Donc, il est correct d’initialiser les variables static dans un bloc de fonction, mais pas d’y initialiser les variables extern.

0
Don Slowik