【问题标题】:Do I need an extern "C" block to include standard POSIX C headers?我是否需要一个外部“C”块来包含标准 POSIX C 标头?
【发布时间】:2011-12-26 14:39:02
【问题描述】:

是否需要extern "C" {} 块才能在 C++ 程序中包含标准 C 标头。只考虑在 C++ 中没有对应的标准 C 头文件。

例如:

extern "C" {
 #include <fcntl.h>
 #include <unistd.h>
}

【问题讨论】:

标签: c++ posix-api


【解决方案1】:

系统 C 标头通常已经包含一个 extern "C" 块,由 #ifdef __cplusplus 保护。这样,当编译为 C++ 时,函数会自动声明为 extern "C",您无需手动执行此操作。

例如在我的系统上unistd.hfcntl.h__BEGIN_DECLS 开头并以__END_DECLS 结尾,它们是在sys/cdefs.h 中定义的宏:

/* C++ needs to know that types and declarations are C, not C++.  */
#ifdef   __cplusplus
# define __BEGIN_DECLS  extern "C" {                                            
# define __END_DECLS }
#else
# define __BEGIN_DECLS
# define __END_DECLS
#endif

【讨论】:

    【解决方案2】:

    标准未指定 C++ 中 &lt;fcntl.h&gt;&lt;unistd.h&gt; 的行为(因为它们也不是 C89 标准的一部分)。也就是说,我从未见过一个平台(a)存在并且(b)实际上需要包装在 extern "C" 块中。

    &lt;stdio.h&gt;&lt;math.h&gt; 和其他标准 C 头文件的行为由 C++03 标准的 D.5 节指定。它们不需要extern "C" 包装块,并且它们将它们的符号转储到全局命名空间中。但是,附件 D 中的所有内容均已“弃用”。

    这些标头的规范 C++ 形式是 &lt;cstdio&gt;&lt;cmath&gt; 等,它们由 C++ 标准的第 17.4.1.2 (3) 节指定,其中说:

    <cassert> <ciso646> <csetjmp> <cstdio> <ctime> <cctype> <climits>
    <csignal> <cstdlib> <cwchar> <cerrno> <clocale> <cstdarg> <cstring>
    <cwctype>
    

    除了第 18 到 27 条中的说明外,每个标题的内容 cname 应与相应头文件 name.h 的相同,如 ISO/IEC 9899:1990 编程语言 C(第 7 条)中规定,或 ISO/IEC:1990 编程语言—C 修正案 1:C 完整性,(条款 7) 酌情,如同通过包含。在 C++ 标准库中, 但是,声明和定义(名称除外 在 C 中定义为宏)在 命名空间标准。

    因此,在 C++ 中使用(例如)printf 的标准、非弃用的规范方法是 #include &lt;cstdio&gt;,然后调用 std::printf

    【讨论】:

      【解决方案3】:

      是的,你知道。但是,许多系统(尤其是 Linux)已经像您一样添加了 extern "C" 括号。请参阅(在 Linux 上)文件 /usr/include/unistd.h/usr/include/features.h 和在 /usr/include/sys/cdefs.h 中定义并在许多 Linux 系统包含文件中使用的宏 __BEGIN_DECLS

      所以在 Linux 上,您通常可以避免使用 extern "C",但它不会造成伤害(而且,恕我直言,在这种情况下提高可读性)。

      【讨论】:

      • “不伤害”?但有人告诉我“在大多数情况下,你甚至不需要这样做(额外的 extern "C" {}),因为它们是系统头文件。你实际上可以破坏这样做,因为有些只有 C++ 导出”。真的吗?我刚刚在这里问了一个问题Are most linux system headers C++ compatiable?
      【解决方案4】:

      不,您应该使用 C++ 包装头文件(例如 &lt;cstdio&gt;)。那些会为您处理所有这些。

      如果它是一个没有这些的标头,那么是的,您需要将它们包装在 extern "C" {} 中。

      ETA:值得注意的是,许多实现将在 .h 文件中包含如下所示的包装器,这样您就可以不用自己动手了。

      #ifdef  __cplusplus
      extern "C" {
      #endif
      
      #ifdef  __cplusplus
      }
      #endif
      

      【讨论】:

      • 值得注意的是,&lt;cstdio&gt; 等标头在技术上将其定义放在 std 命名空间中。 (许多实现也将它们放在顶级命名空间中,但这不是标准所说的。)
      【解决方案5】:

      最好让编译器知道,这样编译为 C++ 时它可以期待 C 代码。您可能还会发现头文件本身包含extern "C" { 作为保护。

      例如,我系统上的curses.h 包含:

      #ifdef __cplusplus
      extern "C" {
      ...
      

      【讨论】:

      • "让编译器知道,以便在编译为 C++ 时可以期待 C 代码" - extern "C" 不是这样做的。它不会改变代码的解释。它甚至不适用于代码。它是一个语言链接指令,指示编译器生成与链接器对 C 期望的符号兼容的符号。它使 C++ 代码可从 C 调用,但不会更改代码生成。
      • @IInspectable 不“指示编译器生成与链接器期望的 C 兼容的符号” 暗示它适用于代码,或者至少适用于代码?它肯定会让编译器(以及链接器)知道一些事情。
      • @Tanz87:它不会改变代码生成一点点。不管有没有extern "C",生成的目标代码都是一样的。该指令仅适用于符号命名。它与调用约定无关。
      【解决方案6】:

      我只是仔细检查了 GNU 编译器的 stdlib.h,并且声明不使用 extern "C" 作为声明。

      编辑:

      if defined __cplusplus && defined _GLIBCPP_USE_NAMESPACES
      define __BEGIN_NAMESPACE_STD    namespace std {
      

      因此,如果定义了 _GLIBCPP_USE_NAMESPACES,那么包含旧标头将在 std 上放置声明?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-10-05
        • 1970-01-01
        • 2013-08-01
        • 2013-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多