C++中extern"C"与extern"C"{}的不同链接

vva*_*hev 10 c++

我意识到,乍一看,我的问题似乎与此处与extern关键字相关的众多问题中的一个明显重复,但我无法找到任何答案,讨论extern"C"和extern"C"之间的区别{ }.相反,我发现有几个人说这两个结构是等价的,因为我认为这是合情合理的.不幸的是,经验证据表明它们确实相同.

这是一个例子:

extern "C" { const int my_var1 = 21; }
extern "C" const int my_var2 = 42;
const int my_var3 = 121;

int main() { }
Run Code Online (Sandbox Code Playgroud)

用gcc 7编译后g++ externC.cpp,我看到了一个显着的差异:

$ readelf -s ./a.out | grep my_var
    34: 0000000000000694     4 OBJECT  LOCAL  DEFAULT   15 _ZL7my_var1
    35: 000000000000069c     4 OBJECT  LOCAL  DEFAULT   15 _ZL7my_var3
    59: 0000000000000698     4 OBJECT  GLOBAL DEFAULT   15 my_var2
Run Code Online (Sandbox Code Playgroud)

my_var1并且my_var3都具有本地绑定和C++错位名称,同时my_var2具有全局绑定和实际C链接.因此,它看起来extern "C" { }已被完全忽略,而类似的extern "C" 没有 {}确实有效.这对我来说太奇怪了.

如果我删除const并尝试读取变量,事情会变得更有趣:

#include <cstdio>

extern "C" { int my_var1; }
extern "C" int my_var2;
int my_var3;

int main() {
    printf("%d, %d, %d\n", my_var1, my_var2, my_var3);
}
Run Code Online (Sandbox Code Playgroud)

当我尝试编译第二个程序时,链接器抱怨它无法找到以下参考my_var2:

/tmp/ccfs9cis.o: In function `main':
externC.cpp:(.text+0xc): undefined reference to `my_var2'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

这意味着在这种情况下发生了两件事:

  1. extern "C" { int my_var1; } 在翻译单元中实例化一个my_var1用C链接调用的变量.

  2. extern "C" int my_var2; 声明了一个外部变量,其中extern我的意思是传统意义上的(如extern int x;),但与"C"联系.

从我的观点来看,与上述第一种情况中的行为不一致,使用const.换一种说法:

  • 在带有const的第一个程序中

    • extern "C"表现得像我预期的那样extern "C" {}[改变联系]

    • extern "C" {}相反,什么也没做

  • 在第二个程序中,没有const:

    • extern "C" {} 表现得像我原先预期的那样[改变联系]但是

    • extern "C"表现得像: extern "C" { extern int my_var2; } 这是用C链接声明外部变量的方式(不幸的是在C++中,关键字extern已被重用).

总而言之,我的问题是:任何人(可能是编译专家吗?)都可以解释原因背后的理论extern "C"extern "C" {}行为方式如此不同以及这种不一致(至少对我而言)的方式吗?在我使用C++的经验中,我意识到一旦你深入了解一个给定的概念,即使是棘手而复杂的角落情况也会开始变得非常合理和一致.只是,你需要非常清楚地看到整个画面.我相信就是这样.

非常感谢大家.


Edit[1]

[最后它发现这里确实存在类似的问题,只是我无法找到它.对不起.]

多亏了到目前为止的答案,我现在明白之间的细微差别extern "C" {}extern "C",即使我还是会很想了解我们(C++开发者/ ISO委员会)怎么会出现这样的解决方案.这有点像使得if (x) foo();行为略有不同if (x) { foo(); }.无论如何,鉴于这些新知识,我会做一些(希望)有趣的观察:

鉴于转换: extern "C" X=> extern "C" { extern X }总是正确的

它遵循:

  • const在当前转换单元中使用C链接定义(实例化)变量的唯一方法是制作它extern,即使我们不想要它:编译器将决定我们是实例化还是仅仅根据if来声明extern我们用一个值初始化变量:在这种情况下,我们正在定义,否则我们只是声明.

  • 相同的逻辑(extern + const)也适用于const具有C++链接的常规变量.const具有C链接的变量没有区别,除了缺少名称修改.

  • 从上述声明它遵循,因为const意味着内部联动在C++中(但不是在C!),将extern用于当const并不意味着extern,但只是较少的内部以上的extern不是静态的.

换一种说法:

  • const int var = 23;创建一个具有内部链接的全局变量,就像static int var = 23;放置在只读段中一样.
  • extern const int var = 23;创建具有常规(外部)链接的全局变量.在extern中和隐式static.结果int var = 23与之相同,只是const将它放在只读段中.
  • extern const int var; 在外部只读段中声明一个正确的外部变量.

Kev*_*vin 9

看到这里:

[ extern "C" { ... }]将语言规范string-literal应用于所有函数类型,具有外部链接的函数名称在declaration-seq中声明的具有外部链接的变量.

由于const int my_var1 = 21;具有内部连接,因此环绕extern "C" { }它不起作用.

也:

[ extern "C" ...]将语言规范string-literal应用于单个声明或定义.

直接包含在语言链接规范中的声明被视为包含extern说明符,用于确定声明的名称的链接以及它是否是定义.

extern "C" int x; // a declaration and not a definition
// The above line is equivalent to extern "C" { extern int x; }

extern "C" { int x; } // a declaration and definition
Run Code Online (Sandbox Code Playgroud)

这解释了为什么extern "C" const int my_var2 = 42;变量具有外部链接和未编号的名称.它还解释了为什么my_var2在第二个代码示例中看到未定义的引用.