- 我应该在 C++ 程序中包含什么,
stdio.h
或者cstdio
?为什么? - 为什么有两个提供相同功能的头文件?
- 标准对此有何规定?
- 我应该如何包含其他此类标题,是否有我应该遵循的基本规则?
2 回答
考虑以下程序:
#include<stdio.h>
int main()
{
printf("Hello World");
return 0;
}
#include<cstdio>
int main()
{
printf("Hello World");
return 0;
}
两者都按预期工作。那么哪种用法更合适呢? 答案是:都没有!惊讶吗?继续阅读。
出于兼容性原因,C++ 标准库提供了所有标准 C 头文件,而 C++ 作为一种语言也提供了所有等效的头文件。作为惯例,
- 没有 C++ 标准库头文件(除了为了 C 兼容性而包含的头文件)有任何文件扩展名,并且
- C 头文件的所有 C++ 等效项都以
cxxxxx
.
C++ 标准在附件 D(规范)兼容性特性下提到了这一点:
§2 提到了重要的区别点。适用于上述示例的这条规则意味着:
- 包括cstdio会在 std 命名空间中导入符号名称,也可能在全局命名空间中。
- 包括stdio.h会在全局命名空间中导入符号名称,也可能在 std 命名空间中。
让我们将此规则应用于我们的示例代码并衡量优缺点:
示例 1: 这会将 stdio.h 中的所有符号带入全局命名空间。优点是您可以在没有任何限制的情况下使用这些符号,因为它们是在全局命名空间中导入的。缺点是您最终会用许多您可能永远不会使用的符号名称污染全局名称空间。这可能会导致符号名称冲突。在 C++ 中,始终将全局命名空间视为雷区,并尽可能避免它。
示例 2: 这是一个非常糟糕的做法,因为无法保证实现会将符号放在全局命名空间中,标准根本不要求这样做。我们只是依赖于一种特定编译器实现的行为。我们不能也不应该假设所有编译器都会这样做。所以严格来说,该程序未经标准批准,并且这种用法不能在所有实现中移植。
那么正确的用法是什么?
正确的用法是使用cstdio
并完全限定符号名称,或者通过using
声明将它们带入范围。这保证了我们使用的所有符号都存在于std
命名空间中,并且我们不会污染全局命名空间。正确用法示例:
#include<cstdio>
using std::printf;
int main()
{
printf("Hello World");
return 0;
}
请注意,指令using namespace std;
,尤其是在标头中,不是一个好的选择,您应该始终使用using
声明。
请注意,我们认为stdio.h
vs.cstdio
这里只是一个示例用例,实际上它适用于所有mostcxxxx
和xxxx.h
标头,除了少数 like<math.h>
和<cmath>
。
由于这篇文章有点旧,我想分享以下内容:
看代码:
Using X.h // Compatible with C language standard
---------------
#include <X.h>
int main() {
// Invoke X's corresponding function
return 0;
}
Using X // Not compatible with C language standard
--------------
#include <X>
int main() {
// Invoke X's corresponding function
return 0;
}
他们都编译和执行ok!
C++ 中哪一个更好?
关于C++11和C++17的规范:
C.5.1(来自 C++17 文档的部分)
对标头的修改 [diff.mods.to.headers]
- 为了与 C 标准库兼容,C++ 标准库提供了 D.5 中列举的 C 头文件,但在 C++ 中不推荐使用它们。
<stdatomic.h>
C 头文件、<stdnoreturn.h>
和没有 C++ 头文件,<threads.h>
C 头文件本身也没有
C++ 的一部分。
- C++ 头文件
<ccomplex>
(D.4.1) 和<ctgmath>
(D.4.4) 以及它们相应的 C 头文件<complex.h>
和<tgmath.h>
包含来自 C 标准库的任何内容,而仅包含来自 C++ 标准库的其他头文件。
D.5 C 标准库头文件 [depr.c.headers]
- 为了与 C 标准库兼容,C++ 标准库提供了表 141 中所示的 C 头文件。
C++11和C++17标准规范文档都声明使用仍然<X.h>
是为了与 C 标准兼容,尽管它们的使用被视为已弃用。
关于C++ 20 标准提案
他们正在审查“不赞成”在 C++20 中使用 C 库头文件。<X.h>
以绿色突出显示。到目前为止,C++11 和 C++17 的弃用被称为“弱推荐”,而保留“ C 标准库头文件 (c.headers) ”的“调整”如下所示:
“基本的 C 库头文件是一项必不可少的兼容性功能,而且不会很快消失。” (来自C++ 20 审查文档)
D.5 C 标准
库头文件 [depr.c.headers]弱推荐:除了上述之外,还要从 C++ 标准中删除相应的 C 标头,就像我们没有相应
<stdatomic.h>
的<stdnoreturn.h>
, 或<threads.h>
, 标头一样。如上所述,但有以下调整:20.5.5.2.1 C 标准库头文件 [c.headers]为了与 C 标准库兼容,C++ 标准库提供了表 141 中所示的 C 头文件。 表 141 — C 头文件
<assert.h> <inttypes.h> <signal.h> <stdio.h> <wchar.h>
<complex.h> <iso646.h> <stdalign.h> <stdlib.h> <wctype.h>
<ctype.h> <limits.h> <stdarg.h> <string.h>
<errno.h> <locale.h> <stdbool.h> <tgmath.h>
<fenv.h> <math.h> <stddef.h> <time.h>
<float.h> <setjmp.h> <stdint.h> <uchar.h>
标头的
<complex.h>
行为就像它只是包含标头一样<complex>
。标头的<tgmath.h>
行为就好像它仅包含标头<complex>
和<cmath>
.
Bjarne Stroustrup建议通过尽可能减少不兼容性来最大限度地提高C 和 C++ 语言之间的互操作性。其他人则不然,因为它使事情复杂化。
所以,它似乎<X.h>
不会去任何地方。最终,您可以同时使用两者。就个人而言,我会决定使用哪一个,归结为让你的代码向后兼容 C 代码。