在 C++ 共享库的标头中声明“外部“C””有啥影响?
Posted
技术标签:
【中文标题】在 C++ 共享库的标头中声明“外部“C””有啥影响?【英文标题】:What is the Effect of Declaring 'extern "C"' in the Header to a C++ Shared Library?在 C++ 共享库的标头中声明“外部“C””有什么影响? 【发布时间】:2010-04-06 19:20:41 【问题描述】:基于this question,我了解将 C 库与 C++ 代码链接起来的构造目的。现在假设如下:
我有一个用 C++ 编译器编译的“.so”共享库。头文件有一个'typedef stuct'和一些函数声明。如果标头包含 extern "C" 声明...
#ifdef __cplusplus
extern "C"
#endif
// typedef struct ...;
// function decls
#ifdef __cplusplus
#endif
... 效果如何?具体来说,我想知道该声明是否有任何不利的副作用,因为共享库被编译为 C++,而不是 C。
在这种情况下是否有任何理由使用 extern "C" 声明?
【问题讨论】:
你的库是只能从 C++ 调用还是你也需要从 C 调用你的库? 【参考方案1】:这很重要,因此编译器不会命名 mangle。 C++ 使用名称修饰来区分具有运算符重载的函数。
对二进制文件运行“/usr/bin/nm”以查看 C++ 对函数名称的作用: _ZSt8_DestroyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEiEvT_S7_SaIT0_E
extern "C" 防止该名称被修改。
IIRC,这使得程序可以在运行时动态链接符号。 “插件”类型的架构很常见。
【讨论】:
感谢您的信息。我是从客户端的角度考虑的,包括标题,而不是导出名称的库。【参考方案2】:在编译 C++ 时,方法名称会发生变化(重整) - 您将无法从另一个使用 C 的 dll/exe 调用该方法。
为了保留类和方法名称,您需要将它们编译为“C”而不进行名称修改。
该库仍然是一个 C++ 库,但它公开了它的一些声明(extern "c" 块中的声明)作为 C 方法。
【讨论】:
【参考方案3】:#ifdef
保护 extern
声明是告诉 C 链接器符号具有 C(未损坏)符号表条目。 #ifdef
确保对 C 编译器编译的代码单元(文件)没有影响。
【讨论】:
【参考方案4】:将extern "C"
用于 C++ API 的一个不利因素是它会阻止您出现函数重载:
extern "C"
// ILLEGAL - C linkage does not support function overloading
void foo(int x);
void foo(const char *str);
【讨论】:
使用extern "C"
的目的是让C和C++代码共享一个API,而C是最小的公分母,所以你必须遵守C编译器强制执行的规则。如果它是一个 C++ API,那么你就不会使用 extern "C"
并且只使用 C++ 编译器进行编译,一切都会好起来的。
@quamrana - 他的问题是“对 C++ 共享库有什么影响”。这个问题不清楚他是否需要能够从 C 代码调用他的库。【参考方案5】:
示例中的 #ifdef
意味着只有 C++ 编译器会看到 extern
包装了头文件,这意味着它将生成未损坏的名称。 C 编译器看不到 extern
(它不会理解),但总是生成未损坏的名称。
这意味着 C 和 C++ 编译器将在其目标文件中生成相同的符号,因此无论哪个编译器为声明的函数生成目标代码,所有目标文件都将成功链接,因为符号具有相同的链接和相同的名称。
对于静态链接或与共享库的链接应该没有任何影响。
【讨论】:
以上是关于在 C++ 共享库的标头中声明“外部“C””有啥影响?的主要内容,如果未能解决你的问题,请参考以下文章
请问C++中啥是函数的定义性声明和函数的引用性声明,有啥区别