Как компиляторы c ++ находят переменную extern?

15

Я компилирую эту программу с помощью g ++ и clang ++. Есть разница:
g ++ печатает 1, а clang ++ печатает 2.
Кажется, что
g ++: внешняя переменная определена в самой короткой области видимости.
clang ++: внешняя переменная определена в самой короткой глобальной области видимости.

Есть ли в C ++ спецификация?

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;

версия: g ++: 7.4.0 / clang ++: 10.0.0
компиляция: $ (CXX) main.cpp other.cpp -o extern.exe

Эдди Куо
источник
4
Компилятор ничего не делает с extern, кроме как помечает их как переменные, которые имеют внешние ссылки, компоновщик - это то, что пытается разрешить ссылки между всеми скомпилированными объектными файлами.
Сплаттен
Отличный (если странный) вопрос! Играя с вашим кодом MSVCи clang-cl(оба дают 2), кажется, что extern int iоба полностью игнорируются: даже если я не буду ссылаться на other.cppфайл, программа будет собираться и запускаться.
Адриан Моул
1
@SPlatten Предположительно, так как компоновщику не нужно «разрешать» ссылку на него i, он не пытается.
Адриан Моул
3
Связанную старую ошибку GCC можно найти здесь и соответствующую открытую ошибку Clang здесь
грецкий орех

Ответы:

11

[basic.link/7] должен быть соответствующей частью стандарта. В текущем проекте говорится:

Имя функции, объявленной в области видимости блока, и имя переменной, объявленной в externобъявлении области видимости блока, имеют связь. Если такая декларация прикреплена к названному модулю, программа некорректна. Если существует видимое объявление сущности со связью, игнорирующее сущности, объявленные вне самой внутренней охватывающей области пространства имен, так что объявление области блока будет (возможно, неправильно сформированным) повторным объявлением, если два объявления появятся в одной и той же декларативной области, Объявление блока блока объявляет ту же сущность и получает связь предыдущей декларации. Если существует более одного такого совпадающего объекта, программа является некорректной. В противном случае, если соответствующий объект не найден, объект области блока получает внешнюю связь.Если в модуле перевода одна и та же сущность объявлена ​​как с внутренней, так и с внешней связью, программа является некорректной.

Обратите внимание, что следующий пример почти точно соответствует вашему случаю:

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

Итак, программа должна быть плохо сформирована. Объяснение ниже примера:

Без объявления в строке № 2 объявление в строке № 3 будет связано с объявлением в строке № 1. Поскольку объявление с внутренней связью скрыто, # 3 получает внешнюю связь, что делает программу плохо сформированной.

Даниэль Лангр
источник
Программа в этом примере плохо сформирована, потому что нигде не определена внешняя связь . Это не так с примером OP.
нет. местоимения м.
3
@ n.'pronouns'm. Но правило относится к единице перевода: если в единице перевода одна и та же сущность объявлена ​​как с внутренней, так и с внешней связью, программа является некорректной. ,
Даниэль Лангр
2
Ответ относится только к C ++ 17 и более поздним версиям , см. Решение проблемы CWG 426 . Мне кажется, что GCC был прав до этого изменения.
грецкий орех
ОК, похоже, что я читал предыдущую редакцию стандарта.
нет. местоимения м.