外部"C" ---何时*确切*使用?

extern "C"---when *exactly* to use?

本文关键字:确切 使用 何时 外部      更新时间:2023-10-16

如果您想将此问题标记为重复问题,请注意,我已经阅读了有关此主题的问题,但仍有一些不清楚的地方。我的印象是,在包含C头和链接C代码时使用了此构造(如果我错了,请纠正我)。这是否意味着我在不处理对象文件时永远不必使用"extern C"?如果我错了,为什么不能把旧的C代码编译成C++,因为它很可能是合法的C++代码?

我对此有点不确定,因为我发誓我在C++中使用旧的C源代码时遇到过这样的情况:链接器错误只能用"extern C"解决,而库头确实有

#ifdef __cplusplus
#extern "C"{
#endif
//......
#ifdef _cplusplus
}
#endif

他们周围。

编辑:很抱歉不清楚,但我想问的是,只有在包含C头并链接到预先存在的C对象文件时才需要"extern C",这是否属实?如果这是真的(似乎是从下面的评论中判断的),为什么库头周围有"extern C"子句,为什么不能将它们作为C++包含和编译?

C的名称篡改规则不同。C可以具有与C++不同的ABI。仅这些原因就要求您在C++代码中嵌入C代码时使用extern "C"。即使编译器可以编译C和C++代码,它也可能对这两种语言使用不同的名称篡改规则或ABI。

此外,你所说的"[C代码]很可能……合法的C++代码"并不完全正确,因为随着时间的推移,C和C++的分歧越来越大。它们有很多相似之处,但也有很多不同之处。

库本身是一个C对象文件,因此为了使用它,应用程序必须使用C-ABI来调用库中的函数,并且在对函数进行原型化时,需要向编译器提供适当的提示。

extern void libraryFunc();

如果该库实际上被编译为C,这是它支持CC++的唯一方法,那么您需要为C++编译器添加注释,该注释必须链接为C。

#ifdef __cplusplus // only true when compiling with a C++ compiler
extern "C" {
#endif
extern void libraryFunc();
#ifdef __cplusplus
}
#endif

对于C编译器,这读取

extern void libraryFunc();

对于C++编译器,这将读取

extern "C" {
extern void libraryFunc();
...
}

相当于

extern "C" void libraryFunc();

如果extern的复制让你感到困扰,可以考虑:

#if defined __cplusplus
# define C_EXTERN extern "C"
#else
# define C_EXTERN extern
#endif
EXTERN_C {
void foo();
}

编译器和链接器现在知道在尝试调用/链接该函数时使用C ABI。

请注意,C++ABI是C ABI(应用程序二进制接口)的超集,因此如果您想共享代码,C是LCD,需要成为您的通用接口。C完全不知道C++"名称篡改"等

这里基本上回答了这个问题:何时使用extern"C";简单地说?

但相关的一点是,在C++中编译时,函数的名称会被"篡改",以编码有关函数的某些信息(如参数类型)。由于C++总是以相同的方式破坏函数名,因此被破坏的名称调用和对象文件中的内容是一致的。

如果你用C编译一个名为"foo"的文件,而在你的C++文件中你有extern foo(int c);,那么C++编译会把"foo"修改成不同的东西,例如foo__Ic(我刚刚编好了,实际的修改看起来会不同)。

同时,在用C编译器编译的纯C代码中,目标代码定义了简单的foo符号。

然而,当整个东西被链接时,C++代码有一个它试图解析的外部符号foo__Ic,它与C对象文件中定义的符号foo不匹配。

希望能有所帮助。

还有一个案例尚未提及。extern "C"指定了C链接,但C并不是唯一的其他语言。C链接也被其他语言使用,这些语言过于晦涩,无法拥有自己广泛接受的链接。例如,Java也使用C链接。显然,您无法用C++编译Java代码,因此您确实需要C++端的extern "C"