在C++命名空间中包含C头文件 - 这是一种标准行为吗?

21

我一直认为C头文件必须包含在C++程序的顶层。然而,我无意中发现C++允许在子命名空间中包含C头文件。

namespace AAA {
    extern "C" {
        #include "sqlite3.h"     // C API.
    }
}

然后,所有的C类型和函数将被放置在命名空间中。更有趣的是,所有链接的C函数也都在正常工作!我还发现这可能会导致一些预处理器问题,但除此之外,它似乎运行得很好。

这是标准行为吗?(我正在使用Clang 3.x)如果是,这个特性的名称是什么,我在标准中可以找到这个特性的位置在哪里?


3
#include 基本上就是复制粘贴。只要该文件中的内容在命名空间内合法,编译器就认为可以使用它。 - Brandin
宏只是文本替换。 - SwiftMango
你可以使用 cpp 独立实用程序或编译器的 -E 标记(GCC 和 CLang 都支持它)来获取 "已处理源代码"(即所有 #include 均被注入,所有宏和 #ifdef 均被解析)。因此,如果预处理器提供了有效的 C++,那就没问题。如果没有,则必须以某种方式处理它。 - user3159253
我更新了问题以添加遗漏的部分。所有链接的函数也都运行良好。 - eonil
@Eonil为什么不呢?它们在extern "C"块中,因此它们的链接是未混淆的标准C内容。 - casey
@casey 我发布了这个问题,因为我不知道 extern 到底是做什么的,而且在 C++ 中可以通过命名空间访问函数,所以我认为 C++ 对名称进行了混淆。 - eonil
2个回答

10

你甚至可以做一些奇怪的事情,例如

//test.c
int
    #include "main.h"
{
    return 1;
}

//main.h
main(void)

预处理器宏在进行任何语法检查之前会被展开。上面的示例将被展开为

int
main(void)
{
    return 1;
}

这是合法的代码。虽然你真的应该避免这样的例子,但有些情况下,将其包含到另一个元素中非常有用。在你的问题中,这取决于编译过程中名称的混淆程度。如果头文件中的所有定义都声明为extern "C",则对象文件中将搜索未混淆的名称,但如果包含实现的对象文件与消费代码中的定义不使用相同的命名空间并且没有声明extern "C",则不是这种情况。


5
这是一个标准行为吗?
是的 - 这种行为受到标准支持,因为C++编译器实际上没有“C”与“C ++”代码之分,除非使用extern"C"来禁止名称修饰。如果不禁止名称修饰,可能会在连接时出现“未解析的符号”错误,如果尝试与在头文件中定义的外部符号(extern变量、函数)的C库链接。
如果这是[标准功能],那么这个功能的名称是什么,在标准中哪里可以找到提到这个功能?
这只是#include工作方式的结果,它定义在16.2源文件包含[cpp.include]中,关键是:
[an #include] causes the replacement of that directive by the entire contents of the source file identified by the specified sequence between the " delimiters.
因此,“C”头文件发生的任何事情都完全像周围的namespace/extern语句和大括号存在于头文件顶部和底部一样...当编译的下一个阶段开始时,源代码来自哪里是无关紧要的(除了显示正确关联源文件的错误消息)。

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