extern “C”:为什么 Visual C++ 在变量和函数的冲突链接规范上表现不同?

extern "C": Why does Visual C++ behave different on conflicting linkage specs for variables vs. for functions?

提问人:MattTT 提问时间:6/9/2023 最后编辑:MattTT 更新时间:6/9/2023 访问量:81

问:

如果变量在没有其他地方声明(例如在头文件中),然后 定义,则 Visual C++ 编译器会使用 C++ 链接对其进行编译,恕不另行通知:extern "C"extern "C"

extern IntStruct ifcStruct;
extern int       ifcVar;
/* ... */

extern "C"
{
IntStruct ifcStruct;
int       ifcVar = 0;
}

dumpbin /symbols test.obj显示带有错误名称的 C++ 符号:

00C 00000000 SECT4  notype       External     | ?ifcStruct@@3UIntStruct@@A (struct IntStruct ifcStruct)
00D 00000008 SECT4  notype       External     | ?ifcVar@@3HA (int ifcVar)

但是,当一个函数被声明为 without 然后用extern "C"

extern void ifcf ();
/* ... */
extern "C" void ifcf () {}

然后,Visual C++ 编译器会给出不同的错误消息:

test.cpp(20): error C2732: linkage specification contradicts earlier specification for 'ifcf'
test.cpp(20): note: see declaration of 'ifcf'

对于标准引起的这种差异,是否有解释?


无论是否使用 .
函数声明是用还是没有 .
变量声明需要 - 否则就是“重新定义”。但是,无论是在声明中还是在定义中指定,还是在两者中指定,都没有区别。在所有情况下,第一次出现定义语言链接。
extern "C"{...}externextern


编辑:正如 Karen 所说,函数上的行为由 Microsoft
cppreference.com 描述:

可以在没有链接规范的情况下重新声明函数 声明时带有语言规范,即第二个声明 将重用第一语言链接。反之则不然:如果 第一个声明没有语言链接,假设为“C++”, 使用另一种语言重新声明是错误的。

C 可视化 C++ extern-C

评论

2赞 Some programmer dude 6/9/2023
extern "C"对于变量来说没有任何意义。使用变量可以解决什么问题?你认为你为什么需要它?extern "C"
0赞 MattTT 6/9/2023
@Someprogrammerdude:正如你所看到的,这些名字是被篡改的。因此,如果您想将 C 和 C++ 部分/模块结合起来,它可能会很有用。
0赞 molbdnilo 6/9/2023
该标准说“与具有语言链接的实体关联的某些属性特定于每个实现,此处不进行描述”,并且唯一提到的“格式错误”与冲突的函数声明有关。
0赞 Flip 6/9/2023
@Someprogrammerdude 它难道不有助于解决变量的命名空间问题吗?在这种情况下,感觉 Visual C++ 没有正确处理变量命名空间中的差异。

答:

0赞 Karen Baghdasaryan 6/9/2023 #1

这里开始

如果一个函数具有多个链接规范,则必须达成一致。将函数声明为同时具有 C 和 C++ 链接是错误的。此外,如果程序中出现两个函数声明,一个具有链接规范,另一个没有链接规范,则具有链接规范的声明必须排在最前面。任何已具有链接规范的函数的冗余声明都将被赋予第一个声明中指定的链接。

正如你所看到的,根据引文,带有说明符的函数声明应该首先出现。extern "C"

extern "C" void ifcf ();
/* ... */
extern void ifcf () {} // OK


////////////////////////////


extern void ifcf ();
/* ... */
extern "C" void ifcf () {} // extern "C" is after the first declaration. NOT OK

但是,情况有所不同extern "C" {...}

第一个告诉编译器,如果块内有具有外部链接的名称或符号,请使用 C 命名,否则使用 C++ 名称修改。但是,只要您在内部定义了名称,它们就具有内部链接,因此不使用 C 命名。从 9.11.5 可以看出extern "C" {...}

联动规格嵌套。当联动规格嵌套时,最里面的那个决定了语言 联动。链接规范不建立范围。链接规范应仅在以下情况下出现 命名空间范围 (6.4)。在链接规范中,指定的语言链接适用于函数类型 所有函数声明符、具有外部链接的函数名称和具有外部链接的变量名称 在链接规范中声明

评论

0赞 molbdnilo 6/9/2023
问题在于,在变量声明冲突的情况下,甚至没有警告。
0赞 Karen Baghdasaryan 6/9/2023
@molbdnilo我也编辑了答案来解决这个问题。
0赞 MattTT 6/9/2023
变量具有外部链接(我认为这就是 from dumpbin 的意思)。如果我交换行的顺序,C++ 链接获胜。我编辑了这个问题来指出这一点。External
0赞 Karen Baghdasaryan 6/10/2023
如果它们在另一个翻译单元中定义,则它们具有外部链接。在内部,我们定义了两个变量,因此它们具有内部联系。extern "C" {...}