Як компілятори c ++ знаходять зовнішню змінну?


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, крім того, щоб позначати їх як змінні, що мають зовнішні посилання. Linker - це те, що намагається вирішити посилання між усіма компільованими файлами об'єктів.
SPlatten

Відмінне (якщо дивне) питання! Граючи з вашим кодом 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 надається зовнішній зв'язок, що робить програму неправильною.


Програма в прикладі неправильно сформована, оскільки ніде не визначено зовнішню зв'язок . Це не стосується прикладу ОП.
н. 'займенники' м.

3
@ n.'pronouns'm. Але правило поширюється на блок перекладу: Якщо всередині одиниці перекладу одна і та ж сутність оголошена як із внутрішнім, так і із зовнішнім зв’язком, програма неправильно формується. .
Даніель Лангр

2
Відповідь стосується лише C ++ 17 та пізніших версій, див. Вирішення питання 426 CWG . Мені здається, що GCC був правильним до цієї зміни.
волоський горіх

ОК, схоже, я читав попереднє видання стандарту.
н. 'займенники' м.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.