Wie finden C ++ - Compiler eine externe Variable?

15

Ich kompiliere dieses Programm mit g ++ und clang ++. Es gibt einen Unterschied:
g ++ druckt 1, aber clang ++ druckt 2.
Es scheint, dass
g ++: die externe Variable im kürzesten Bereich definiert ist.
clang ++: Die externe Variable wird im kürzesten globalen Bereich definiert.

Hat die C ++ - Spezifikation eine Spezifikation dazu?

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
kompilierung: $ (CXX) main.cpp other.cpp -o extern.exe

Eddie Kuo
quelle
4
Der Compiler macht nichts mit extern, außer sie als Variablen mit externen Referenzen zu markieren. Der Linker versucht, die Links zwischen allen kompilierten Objektdateien aufzulösen.
SPlatten
Eine ausgezeichnete (wenn auch seltsame) Frage! Wenn Sie mit Ihrem Code in MSVCund clang-cl(beide geben 2) herumspielen, scheint das extern int ivon beiden völlig ignoriert zu werden: Auch wenn ich die other.cppDatei nicht verlinke , wird das Programm erstellt und ausgeführt.
Adrian Mole
1
@SPlatten Vermutlich versucht er es nicht, da der Linker den Verweis auf inicht auflösen muss.
Adrian Mole
3
Verwandte alte suspendierte GCC-Fehler finden Sie hier und entsprechende offene Clang-Fehler hier
Walnuss

Antworten:

11

[basic.link/7] sollte der relevante Teil des Standards sein. Im aktuellen Entwurf heißt es:

Der Name einer im Blockbereich deklarierten Funktion und der Name einer Variablen, die durch eine Blockbereichsdeklaration deklariert wurde, externsind verknüpft. Wenn eine solche Deklaration an ein benanntes Modul angehängt ist, ist das Programm fehlerhaft. Wenn es eine sichtbare Deklaration einer Entität mit Verknüpfung gibt, werden Entitäten ignoriert, die außerhalb des innersten umschließenden Namespace-Bereichs deklariert wurden, sodass die Blockbereichsdeklaration eine (möglicherweise falsch geformte) Neudeklaration wäre, wenn die beiden Deklarationen in derselben deklarativen Region erscheinen würden Die Blockbereichsdeklaration deklariert dieselbe Entität und erhält die Verknüpfung der vorherigen Deklaration. Wenn es mehr als eine solche übereinstimmende Entität gibt, ist das Programm schlecht geformt. Andernfalls erhält die Blockbereichsentität eine externe Verknüpfung, wenn keine übereinstimmende Entität gefunden wird.Wenn innerhalb einer Übersetzungseinheit dieselbe Entität sowohl mit interner als auch externer Verknüpfung deklariert wird, ist das Programm fehlerhaft.

Beachten Sie, dass das folgende Beispiel fast genau Ihrem Fall entspricht:

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
  }
}

Das Programm sollte also schlecht gestaltet sein. Die Erklärung befindet sich unter dem Beispiel:

Ohne die Erklärung in Zeile 2 würde die Erklärung in Zeile 3 mit der Erklärung in Zeile 1 verknüpft. Da die Deklaration mit interner Verknüpfung jedoch ausgeblendet ist, erhält # 3 eine externe Verknüpfung, wodurch das Programm schlecht geformt wird.

Daniel Langr
quelle
Das Programm im Beispiel ist schlecht geformt, da nirgendwo ein i mit externer Verknüpfung definiert ist. Dies ist beim Beispiel von OP nicht der Fall.
n. 'Pronomen' m.
3
@ n.'pronomen'm. Die Regel gilt jedoch für eine Übersetzungseinheit: Wenn innerhalb einer Übersetzungseinheit dieselbe Entität sowohl mit interner als auch externer Verknüpfung deklariert wird, ist das Programm fehlerhaft. .
Daniel Langr
2
Die Antwort gilt nur für C ++ 17 und höher, siehe Lösung des CWG-Problems 426 . Es scheint mir, dass GCC vor dieser Änderung korrekt war.
Walnuss
OK, es sieht so aus, als hätte ich die vorherige Ausgabe des Standards gelesen.
n. 'Pronomen' m.