我意识到,乍看之下,我的问题可能看起来很明显,它是与extern
关键字相关的众多问题之一的明显重复,但我找不到任何答案,都讨论了外部“ C”和外部“ C” {}之间的区别。相反,我发现有人指出这两个结构是等效的,因为我认为这是合理的。不幸的是,经验证据表明它们确实不相同。
这里是一个例子:
extern "C" { const int my_var1 = 21; }
extern "C" const int my_var2 = 42;
const int my_var3 = 121;
int main() { }
在使用g++ externC.cpp
和gcc 7进行编译之后,我看到了明显的不同:
$ 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
my_var1
和my_var3
都具有 local 绑定和C ++伪装名称,而my_var2
具有 global 绑定和实际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);
}
当我尝试编译第二个程序时,链接器抱怨它找不到my_var2
的引用:
/tmp/ccfs9cis.o: In function `main':
externC.cpp:(.text+0xc): undefined reference to `my_var2'
collect2: error: ld returned 1 exit status
这意味着在这种情况下发生了两件事:
extern "C" { int my_var1; }
实例化
通过C链接将一个名为my_var1
的变量组合起来。
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声明 extern 变量的方法
链接(不幸的是,在C ++中,关键字extern
具有
已被重用)。
总而言之,我的问题是:任何人(也许是编译专家?)都可以解释extern "C"
和extern "C" {}
如此不同且行为如此不一致(至少对于我)方式?根据我在C ++方面的经验,我意识到,一旦您深入了解了给定的概念,即使是棘手且复杂的极端情况也开始显得相当合理和一致。只是,您需要非常清楚地看到整个图片。我相信就是这种情况。
非常感谢大家。
Edit[1]
[最后,我在这里发现了一个类似的问题 did ,只是我找不到它。抱歉。]
借助到目前为止的答案,即使我仍然想知道我们(C ++开发人员/ ISO委员会)是如何结束的,我现在仍然了解extern "C" {}
和extern "C"
之间的细微差别。解决这样的问题。这有点像使if (x) foo();
的行为与if (x) { foo(); }
略有不同。无论如何,有了这些新知识,我将有一些(希望)有趣的发现:
鉴于该转换:
extern "C" X
=> extern "C" { extern X }
总是正确的
其结果是:
使用C链接定义(实例化)const
变量的唯一方法
即使我们不想这样做,当前翻译单元中的变量还是使其extern
:编译器将根据是否使用值初始化变量来决定是实例化还是声明外部变量:在那种情况下,我们正在定义,否则我们只是在声明。
相同的逻辑(extern + const)也适用于具有C ++链接的常规const
变量。具有C链接的const
变量没有区别,只是缺少名称修饰。
从上面的语句可以得出结论,由于const
暗示了C ++(而不是C!)中的内部链接,因此extern
用于const
并不意味着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变量。答案 0 :(得分:9)
请参见here:
[{
extern "C" { ... }
]对所有函数类型,具有外部链接的函数名称以及具有在声明序列中声明的具有外部链接的变量的所有函数类型应用字符串规范的语言规范。
由于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
这说明了为什么对于extern "C" const int my_var2 = 42;
来说,变量具有外部链接并且名称无歧义。它还说明了为什么在第二个代码示例中看到对my_var2
的未定义引用。