Comment les compilateurs c ++ trouvent-ils une variable externe?

15

Je compile ce programme par g ++ et clang ++. Il y a une différence:
g ++ affiche 1, mais clang ++ affiche 2.
Il semble que
g ++: la variable externe est définie dans la portée la plus courte.
clang ++: la variable externe est définie dans la portée globale la plus courte.

La spécification C ++ a-t-elle des spécifications à ce sujet?

main.cpp

#include <iostream>
static int i;
static int *p = &i;

int main() {
  int i;
  {
    extern int i;
    i = 1;
    *p = 2;
    std::cout << i << std::endl;
  }
}

other.cpp

int i;

version: g ++: 7.4.0 / clang ++: 10.0.0
compilation: $ (CXX) main.cpp other.cpp -o extern.exe

eddie kuo
la source
4
Le compilateur ne fait rien avec extern, sauf les marquer comme des variables qui ont des références externes, l'éditeur de liens est ce qui essaie de résoudre les liens entre tous les fichiers objets compilés.
SPlatten
Une excellente (si étrange) question! En jouant avec votre code dans MSVCet clang-cl(les deux donnent 2), il semble que le extern int isoit complètement ignoré par les deux: même si je ne lie pas dans le other.cppfichier, le programme se construit et s'exécute.
Adrian Mole
1
@SPlatten Vraisemblablement, comme l'éditeur de liens n'a pas besoin de «résoudre» la référence i, il n'essaie pas.
Adrian Mole
3
Le vieux bug GCC suspendu connexe peut être trouvé ici et le bug Clang ouvert correspondant ici
noyer

Réponses:

11

[basic.link/7] devrait être la partie pertinente de la norme. Dans le projet actuel, il est écrit:

Le nom d'une fonction déclarée dans la portée de bloc et le nom d'une variable déclarée par une externdéclaration de portée de bloc ont un lien. Si une telle déclaration est attachée à un module nommé, le programme est mal formé. S'il existe une déclaration visible d'une entité avec liaison, en ignorant les entités déclarées en dehors de la portée d'espace de noms englobante la plus interne, de sorte que la déclaration de portée de bloc serait une redéclaration (éventuellement mal formée) si les deux déclarations apparaissaient dans la même région déclarative, la La déclaration de portée de bloc déclare la même entité et reçoit le lien de la déclaration précédente. S'il existe plusieurs entités de ce type, 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. Si, au sein d'une unité de traduction, la même entité est déclarée avec une liaison interne et externe, le programme est mal formé.

Notez que l'exemple suivant correspond presque exactement à votre cas:

static void f();
extern "C" void h();
static int i = 0;               // #1
void g() {
  extern void f();              // internal linkage
  extern void h();              // C language linkage
  int i;                        // #2: i has no linkage
  {
    extern void f();            // internal linkage
    extern int i;               // #3: external linkage, ill-formed
  }
}

Donc, le programme devrait être mal formé. L'explication est ci-dessous l'exemple:

Sans la déclaration à la ligne # 2, la déclaration à la ligne # 3 serait liée à la déclaration à la ligne # 1. Parce que la déclaration avec liaison interne est masquée, cependant, # 3 reçoit une liaison externe, ce qui rend le programme mal formé.

Daniel Langr
la source
Le programme dans l'exemple est mal formé car il n'y a pas de i avec une liaison externe définie nulle part. Ce n'est pas le cas avec l'exemple de OP.
n. «pronoms» m.
3
@ n.'pronouns'm. Mais la règle s'applique à une unité de traduction: si, au sein d'une unité de traduction, la même entité est déclarée avec des liens internes et externes, le programme est mal formé. .
Daniel Langr
2
La réponse ne s'applique qu'à C ++ 17 et versions ultérieures, voir la résolution du problème CWG 426 . Il me semble que GCC avait raison avant ce changement.
noyer
OK, il semble que je lisais l'édition précédente de la norme.
n. «pronoms» m.