C ++编译器如何找到extern变量?


15

我用g ++和clang ++编译该程序。有一个区别:
g ++打印1,而clang ++打印2。
似乎
g ++:extern变量在最短范围内定义。
clang ++:extern变量是在最短的全局范围内定义的。

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不做任何事情,只是将它们标记为具有外部引用的变量,而链接器将尝试解析所有已编译目标文件之间的链接。
SPlatten

一个很好的(如果很奇怪)的问题!在MSVC和中clang-cl(都给定2)使用您的代码,似乎extern int i两者都完全忽略了:即使我没有链接other.cpp文件,该程序也会生成并运行。
Adrian Mole

1
@SPlatten大概是因为链接器不需要“解析”对的引用i,所以它不会尝试。
Adrian Mole

3
相关旧暂停GCC的bug,可以发现这里和相应的开锵错误在这里
核桃

Answers:


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提供了外部链接,从而使程序格式不正确。


该示例中的程序格式错误,因为在任何地方都没有定义外部链接的i 。OP的示例并非如此。
n。代词

3
@n。'代词'。但是,该规则适用于翻译单元:如果在翻译单元中使用内部和外部链接声明同一实体,则程序格式错误。
Daniel Langr

2
答案仅适用于C ++ 17和更高版本,请参阅CWG问题426的分辨率。在我看来,GCC在更改之前是正确的。
胡桃

好的,好像我在阅读该标准的先前版本。
n。代词
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.