正确的包含<cstdio>和<stdio.h>的顺序是什么?

4
我需要使用系统特定的函数,例如ftello()(根据POSIX标准在stdio.h中定义)。 我还需要使用标准的C++特性,例如std::sprintf()(根据ISO C++标准在cstdio中定义)。
据我所知,仅包含<cstdio>不能保证定义非标准的C++内容,因此我想我必须同时包含两者。 我很久以前就读到过,(例如)使用gcc可能会出现包含文件顺序的问题。
那么,包括<cstdio><stdio.h>的正确顺序是什么? 我正在寻找尽可能跨平台的解决方案(至少适用于gcc、suncc、intel C++/linux和mingw)。
6个回答

2

经过更多的研究,我最终得出结论,先包含C++头文件,后包含C头文件是正确的做法。例如,考虑以下来自gcc的C++0x头文件:

/usr/include/c++/4.3/tr1_impl/cstdint:


// ...
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS
#include_next <stdint.h>
// ...

它定义了两个C99宏,然后才包含C99 stdint.h头文件。这是因为在C99中,stdint.h的某些特性是可选的,只有在定义了这些宏时才可用。然而,在C++0x中,所有stdint.h特性都是强制性的。 现在,如果我先包含C99 stdint.h,然后再包含cstdint,由于stdint.h中的头文件保护,我将无法获得C++0x的强制性特性。 有人可能会认为这是编译器供应商的问题,但这是不正确的。stdint.h是一个系统捆绑的头文件(在这种情况下来自glibc),它是一个C99头文件,不知道任何关于C++0x(毕竟它可以是旧系统)或gcc的事情。编译器实际上不能修复所有的系统头文件(在这种情况下始终启用C++模式中的这些特性),但它必须在这些系统上提供C++0x支持,因此供应商使用这个解决方法。

2

对于系统头文件,包含的顺序通常不应是错误的来源。

对于其他头文件,请参考类似的 SO 问题


谢谢你的回答。我在这里找到了一个类似的问题: http://gcc.gnu.org/ml/libstdc++/2003-01/msg00210.html我之前偶然发现过它,但是我不记得细节了。头文件肯定是系统级别的。虽然这是一个系统/编译器问题,但我想问的是,也许有人知道如何处理这种问题的可靠方法。 - Alex

2

我不知道任何确切的规则,但一般来说我会先包含低级系统库再包含高级别库。

因此在这种情况下,stdio.h是C语言头文件(在我看来)更接近机器,<cstdio> 是 C++ 标准库,我认为它更抽象。

我自己倾向于先包含 stdio.h 再包含 cstdio,但我不知道确切的理由来支持这种做法。


0
据我的观察,ftello()和sprintf()均包含在stdio.h中。
对于非标准头文件,包含顺序可能很重要,您必须检查依赖结构以找出哪个依赖于另一个,并按正确的顺序包含它们。
因此,应该将包含文件的依赖关系包含在包含文件中,而不是依赖于“用户”确保它们被正确地包含。

是的,ftello() 和 sprintf() 来自 stdio.h,但我在谈论 std::sprintf(),它可能与 sprintf() 不同。事实上,它可能不同,因为 sprintf() 通常是编译器定义的宏,而 std::sprintf() 则保证是一个函数(你可以获取其地址)。所以,我需要同时使用 stdio.h 中的 ftello() 和 cstdio 中的 std::sprintf()。两者都来自标准包含文件,但由于这两个文件之间有一种特殊关系,我不知道哪种顺序可以在各种编译器/操作系统组合上可靠地工作。 - Alex

0

你在无谓地担心。 <cstdio> 的内容“好像被包含”在 <stdio.h> 中(17.4.1.2 Headers/4)。声明和定义(但不是宏)在 std 命名空间中。

因此,你可能需要编写 std::ftello()。为了提高可移植性,加入一个 using std::ftello; 就可以不用再关心这个问题了。


ftello()不在C++标准中,而且std命名空间中也没有ftello()(至少在gcc的libstdc++中)。无论如何还是谢谢! - Alex
这正是我的观点。C++标准并没有明确指定stdio.h中包含了什么内容。它只是说,cstdio包含了stdio.h中的任何内容,但在std命名空间中。如果libstdc++将ftello放在stdio.h中而不是cstdio中,则违反了17.4.1.2。 - MSalters

0

我将最具体的内容放在顶部,最不具体的内容放在底部。例如对于源文件,它应该看起来像这样:

  1. 预编译头(如果有)
  2. 此源文件的头文件
  3. 项目包含文件
  4. 最具体的库,例如此项目中的库或公司库
  5. 最不具体的库,例如“系统”库,如boost或SDL
  6. 标准C++
  7. 标准C
  8. 操作系统头文件

我的理由是,更具体的头文件通常会包含更通用的头文件,并希望通过宏修改它们。如果这些已经被包括,则包括保护或默认宏值将破坏系统库的行为。

另一个理由是,这种排序可防止您从其他头文件“隐藏”依赖项,即当它们自己之前始终包括了系统库时,这些头文件将无法独立存在于其他源文件中。


这也是我的包含顺序。:) 只是出于某种原因,我将5移动到8之后。 - Lightness Races in Orbit

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