【发布时间】:2011-12-26 14:39:02
【问题描述】:
是否需要extern "C" {} 块才能在 C++ 程序中包含标准 C 标头。只考虑在 C++ 中没有对应的标准 C 头文件。
例如:
extern "C" {
#include <fcntl.h>
#include <unistd.h>
}
【问题讨论】:
是否需要extern "C" {} 块才能在 C++ 程序中包含标准 C 标头。只考虑在 C++ 中没有对应的标准 C 头文件。
例如:
extern "C" {
#include <fcntl.h>
#include <unistd.h>
}
【问题讨论】:
系统 C 标头通常已经包含一个 extern "C" 块,由 #ifdef __cplusplus 保护。这样,当编译为 C++ 时,函数会自动声明为 extern "C",您无需手动执行此操作。
例如在我的系统上unistd.h 和fcntl.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
【讨论】:
标准未指定 C++ 中 <fcntl.h> 和 <unistd.h> 的行为(因为它们也不是 C89 标准的一部分)。也就是说,我从未见过一个平台(a)存在并且(b)实际上需要包装在 extern "C" 块中。
<stdio.h>、<math.h> 和其他标准 C 头文件的行为由 C++03 标准的 D.5 节指定。它们不需要extern "C" 包装块,并且它们将它们的符号转储到全局命名空间中。但是,附件 D 中的所有内容均已“弃用”。
这些标头的规范 C++ 形式是 <cstdio>、<cmath> 等,它们由 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 <cstdio>,然后调用 std::printf。
【讨论】:
是的,你知道。但是,许多系统(尤其是 Linux)已经像您一样添加了 extern "C" 括号。请参阅(在 Linux 上)文件 /usr/include/unistd.h/usr/include/features.h 和在 /usr/include/sys/cdefs.h 中定义并在许多 Linux 系统包含文件中使用的宏 __BEGIN_DECLS。
所以在 Linux 上,您通常可以避免使用 extern "C",但它不会造成伤害(而且,恕我直言,在这种情况下提高可读性)。
【讨论】:
不,您应该使用 C++ 包装头文件(例如 <cstdio>)。那些会为您处理所有这些。
如果它是一个没有这些的标头,那么是的,您需要将它们包装在 extern "C" {} 中。
ETA:值得注意的是,许多实现将在 .h 文件中包含如下所示的包装器,这样您就可以不用自己动手了。
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
【讨论】:
<cstdio> 等标头在技术上将其定义放在 std 命名空间中。 (许多实现也将它们放在顶级命名空间中,但这不是标准所说的。)
最好让编译器知道,这样编译为 C++ 时它可以期待 C 代码。您可能还会发现头文件本身包含extern "C" { 作为保护。
例如,我系统上的curses.h 包含:
#ifdef __cplusplus
extern "C" {
...
【讨论】:
extern "C" 不是这样做的。它不会改变代码的解释。它甚至不适用于代码。它是一个语言链接指令,指示编译器生成与链接器对 C 期望的符号兼容的符号。它使 C++ 代码可从 C 调用,但不会更改代码生成。
extern "C",生成的目标代码都是一样的。该指令仅适用于符号命名。它与调用约定无关。
我只是仔细检查了 GNU 编译器的 stdlib.h,并且声明不使用 extern "C" 作为声明。
编辑:
if defined __cplusplus && defined _GLIBCPP_USE_NAMESPACES
define __BEGIN_NAMESPACE_STD namespace std {
因此,如果定义了 _GLIBCPP_USE_NAMESPACES,那么包含旧标头将在 std 上放置声明?
【讨论】: