我需要一个extern "C"块来包含标准的POSIX C头文件吗?

17

在C++程序中,我需要使用extern "C" {}块来包含标准C头文件吗?只考虑在C++中没有对应项的标准C头文件。

例如:

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

刚刚发现一个与你的问题类似的问题:为什么在C++中需要extern“C”{ #include <foo.h> }? - AusCBloke
7个回答

23
系统的C语言头文件通常已经包括了一个由 #ifdef __cplusplus守卫的extern "C"块。这样,当编译为C++时,函数会自动声明为extern "C",因此您无需手动执行此操作。

例如,在我的系统上,unistd.hfcntl.hsys/cdefs.h中定义的宏__BEGIN_DECLS__END_DECLS开始和结束:
/* 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

10

<fcntl.h><unistd.h>在C++中的行为未被标准指定(因为它们也不是C89标准的一部分)。尽管如此,我从未见过一个需要将它们包装在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条款中所述的内容外,每个头文件的名称应该和对应的C语言标准库头文件名字相同,根据ISO/IEC 9899:1990 Programming Languages C(第7条)或 ISO/IEC:1990 Programming Languages—C AMENDMENT 1: C Integrity (第7条)进行指定。在C++标准库中,除了被定义为宏的名称外,声明和定义都处于命名空间std的范围内(3.3.5)。因此,在C++中使用(例如)printf的标准、非废弃、规范的方法是# include ,然后调用std::printf。

3
是的,你需要。然而,许多系统(尤其是Linux)已经像你所做的那样添加了一个extern "C"括号。在Linux上,请参见文件/usr/include/unistd.h/usr/include/features.h以及在许多Linux系统包含文件中使用的宏__BEGIN_DECLS/usr/include/sys/cdefs.h中定义。

因此,在Linux上,你通常可以避免使用extern "C",但这并不会有害(并且,在这种情况下,我认为它可以提高可读性)。


“不会有影响”?但是我被告知:“在大多数情况下,您甚至不需要这样做(额外的extern“C”{}),因为它们是系统头文件。实际上,这样做可能会破坏某些仅具有C++导出的内容。”这是真的吗?我刚在这里提出了一个问题Linux系统头文件大多数都兼容C++吗? - Rick

1

不,你应该使用C++的包装头文件(例如像<cstdio>这样)。它们会为你处理所有的事情。

如果是一个没有这些的头文件,那么是的,你需要用extern "C" {}来包装它们。

预计时间:值得注意的是,许多实现会在.h文件中包含包装器,就像下面这样,这样你就可以省去自己做的麻烦。

#ifdef  __cplusplus
extern "C" {
#endif

#ifdef  __cplusplus
}
#endif

值得注意的是,<cstdio>等头文件在技术上将它们的定义放在了std命名空间中。(许多实现也将它们放在顶层命名空间中,但这不是标准规定的。) - Nemo

0

宏__BEGIN_DECLS定义在/usr/include/sys/cdefs.h中,并在许多Linux系统包含文件中使用。

enter image description here


-1

我刚刚仔细检查了 GNU 编译器的 stdlib.h,发现它的声明没有使用 extern "C"。

编辑:

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

因此,如果包括旧标头,则会在定义了std提供的_GLIBCPP_USE_NAMESPACES时放置声明吗?


-1

在编译为 C++ 时,让编译器知道这一点是一个好主意,这样它就可以期望编译 C 代码。您可能还会发现头文件本身包含 extern "C" { 作为保护。

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

#ifdef __cplusplus
extern "C" {
...

2
“让编译器知道,这样在编译为C++时就可以期望C代码” - 这不是extern "C"的作用。它不会改变代码的解释,甚至不适用于代码。它是一种语言链接指令,指示编译器生成与链接器期望的C兼容的符号。它使得C++代码可从C中调用,但不会改变代码生成。 - IInspectable
@IInspectable,“指示编译器生成与链接器期望的C兼容的符号”并不意味着它适用于代码,或者至少适用于代码的调用约定吗?这绝对让编译器(以及链接器)知道一些信息。 - Tanz87
@Tanz87:它不会改变代码生成的一点。生成的目标代码与使用或不使用“extern"C"”是相同的。该指令仅适用于符号命名。它对调用约定没有影响。 - IInspectable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接