在 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 与 C++ 中的外部函数

请问C++中啥是函数的定义性声明和函数的引用性声明,有啥区别

共享需要外部库的 Visual Studio 项目

是否可以在 C/C++ 中永远不使用标头保护?如果是这样,这种方法有啥优势吗?

仅标头模板的显式实例化声明(外部模板)

C 库的 C++ 包装器作为共享库