【问题标题】:extern "C"---when *exactly* to use? [duplicate]extern "C"---什么时候*确切*使用? [复制]
【发布时间】:2013-12-23 06:16:13
【问题描述】:

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

我对此有点怀疑,因为我发誓我在使用 C++ 中的旧 C 源代码 时遇到过这样的情况,其中链接器错误只能使用“extern C”和库来解决标题确实有

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

在他们周围。

编辑:很抱歉不清楚,但我想问的是,“extern C”是否仅在包含 C 头文件并与预先存在的 C 目标文件链接时才需要?如果这是真的,(并且似乎从下面的 cmets 判断),为什么库头文件周围有“extern C”子句,为什么不能将它们包含并编译为 C++?

【问题讨论】:

  • "为什么旧的 C 代码不能直接编译为 C++?"即使 C 代码是有效的 C++,extern C 也用于链接编译为 C 的目标文件,这意味着您仍然需要使用基于 C 的调用约定。
  • @ChrisHayes 所以当我不使用预先存在的目标文件时确实如此,我不需要“extern C”?
  • 只是为了明确一点(我仍然看到一个密切的投票):其他问题涵盖主题“extern "C"是否需要if链接到C代码”,这个问题是“extern "C" 是否需要当且仅当链接到 C 代码”。请关注仅当。

标签: c++


【解决方案1】:

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

此外,您关于“[C 代码] 最有可能......合法的 c++ 代码”的断言并不完全正确,因为随着时间的推移,C 和 C++ 的分歧越来越大。它们有很多相似之处,但也有很多不同之处。

【讨论】:

  • 抱歉含糊不清。请参考上面的编辑,谢谢
  • 另请注意,不同的 C++ 编译器以及过去甚至同一编译器的不同版本都使用不同的名称修饰约定。因此,对于 C++ 程序来说,将其函数定义为“extrn”是很常见的,这样调用程序就可以使用不同的 C++ 编译器来使用它。
  • @user3109672:已更新。即使同一个编译器编译 C 代码和 C++ 代码,它也可能做不同的事情(它可能有两种模式,一种“C 模式”和一种“C++ 模式”,它可以在两种模式之间切换,每种模式都有不同的规则)。跨度>
  • @JamesAnderson 的评论在为 C/C++ 应用程序创建插件接口的上下文中特别重要(例如,您允许通过 dlopen 或 LoadLibraryEx 动态加载共享库)。如果您选择 C++ 链接,那么当您升级编译器时,由于 C++ ABI 更改,插件可能会在运行时失败。如果您选择 C ​​链接,则插件可能会继续运行,因为 C ABI 相对稳定得多。编译器更改。因此,为了防止插件用户/编写者需要为更改的 C++ ABI 重新编译插件,C 链接通常是一个很好的调用。
  • 我还应该提到 C doesn't have a standard ABI,但实际上 ABI 提供的更改频率远低于 C++。
【解决方案2】:

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

extern void libraryFunc();

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

#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++“名称修饰”等。

【讨论】:

    【解决方案3】:

    这基本上在这里得到了回答:When to use extern "C" in simple words?

    但相关的一点是,在 C++ 中编译时,函数的名称被“修改”以编码有关函数的某些信息(如参数类型)。由于 C++ 总是以相同的方式修改函数名,因此在修改后的名称调用和放入目标文件中的内容之间的一切都是一致的。

    如果你用 C 语言编译一个名为“foo”的文件,并且在你的 C++ 文件中你有 extern foo(int c);,那么 C++ 编译会将“foo”转换成不同的东西,例如,foo__Ic(我刚刚做了向上,实际的修改看起来会有所不同)。

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

    然而,当整个事情被链接时,C++ 代码有一个外部符号 foo__Ic 它试图解析,这与 C 目标文件中定义的符号 foo 不匹配不 .

    希望对您有所帮助。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2011-02-17
        • 2012-09-15
        • 2010-09-11
        • 2010-09-18
        • 2010-09-25
        • 1970-01-01
        • 1970-01-01
        • 2014-08-25
        • 2014-04-29
        相关资源
        最近更新 更多