C ++

时间:2018-12-30 18:27:17

标签: c++

我意识到,乍看之下,我的问题可能看起来很明显,它是与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_var1my_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

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

    在翻译中
  1. extern "C" { int my_var1; } 实例化   通过C链接将一个名为my_var1的变量组合起来。

  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声明 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变量。

1 个答案:

答案 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的未定义引用。