我意识到,乍一看,我的问题似乎与此处与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)
这意味着在这种情况下发生了两件事:
extern "C" { int my_var1; } 在翻译单元中实例化一个my_var1用C链接调用的变量.
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; 在外部只读段中声明一个正确的外部变量.看到这里:
[
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在第二个代码示例中看到未定义的引用.
| 归档时间: |
|
| 查看次数: |
314 次 |
| 最近记录: |