C编程中包含ncurses.h是否也包含stdio.h?

Does including ncurses.h in C programming also include stdio.h?

我正在研究 ncurses.h 因为我想学习一些 c 的设计方面,并且偶然发现了一个声明说 stdio.h 在 ncurses

  #include <ncurses.h>        /*ncurses.h includes stdio.h*/

这是真的吗?如果是这样,有人可以向我解释为什么吗,如果那意味着我不必再包含它了?

此外,这是否会引起任何问题,因为我没有在常规意义上准确定义它

它确实包含 stdio.h,是的,这意味着您不需要再次包含 stdio.h。 这是有效的,因为编译器将在代码中放置 #include 指令的位置插入 #include' 头文件的内容。这意味着不仅 ncurses.h 包含在您的代码中您指定的位置,而且 stdio.h 在被放入您的代码之前已经包含在该文件中的适当位置。

stdio.h 的后续包含将不会对您的程序产生影响,因为使用了 include guards

如果您在自己的代码中使用 stdio.h 中的任何内容,我认为将 #include <stdio.h> 指令添加到您自己的源文件中是一种很好的做法。我的理由是,它直接与下一个阅读您的代码的开发人员沟通,即此处使用了 stdio 功能,而不依赖于隐式信息。

是的,包括 <ncurses.h> 几乎肯定会包括 <stdio.h>。但我建议不要利用这一点。

ncurses 的文档确实说:

NOTES
The header file <curses.h> automatically includes the header files <stdio.h> and <unctrl.h>.

(ncurses.h 是(通常?)一个符号 link 到 curses.h。)

Thomas Dickey,ncurses 的主要维护者, 告诉 我们这 回到 1994 年,所以你极不可能遇到 ncurses 不执行此操作的实现。

ncurses 支持的 X/Open Curses 标准说(强调):

The inclusion of <curses.h> may make visible all symbols from the headers <stdio.h>, <term.h>, <termios.h>, and <wchar.h>.

此外,<ncurses.h>and/or中定义了一些函数 <curses.h> 采用 FILE 类型的参数,定义在 <stdio.h>。 (可以想象,有些东西可以使用 FILE 不包括 <stdio.h> 的类型,但这是人为的 愚蠢,这种可能性不值得担心。)

所以 #include <ncurses.h> 实际上 保证包含 <stdio.h>.

说了这么多,我建议不要利用这个 保证。如果你的 C 源文件依赖于声明的东西 <stdio.h>,作为一种风格,你应该有一个明确的 #include <stdio.h>,尽管它完全是多余的。 更一般地说,C 源文件应该有一个 #include 指令 对于它所依赖的任何 header。这是一个风格和问题 可维护性,不是绝对要求。参见,例如, this question。 C 标准保证包含 <stdio.h> 不止一次 不会造成问题。

一般来说,即使一个 header 包含另一个作为其实现的一部分,您也应该始终相信 C 标准和您正在使用的任何 API 的文档。如果它说某个 header 文件是包含某个项目的文件,或者它说要访问某个项目,请包含此 header,您应该遵循该文件。

  1. 如果同一个文件具有包含保护(即 #pragma once#ifndef _HEADER_NAME ... #endif),则多次包含同一文件并无害处
  2. 如果您省略 C 标准(或 API 文档)所说的包含特定声明或定义的正确文件,那么您在技术上依赖于实现细节。这意味着您的编译器或库的下一个版本可以很好地删除方便的嵌套包含,这会使您的程序短几行。

ncurses 的 curses.h 包含 <stdio.h>,因为 curses.h 声明的一些(X/Open Curses)标准函数使用 FILE。 C 标准将 FILE 称为 "object type",有些人可能会按字面意思理解为需要 typedef。然而,有些实现中 FILE 是用 #define 定义的符号,例如

#define   FILE    struct _iobuf

(引用自多个 BSD:4.3BSD、SunOS 4、Ultrix)。在不深入研究它的历史的情况下,我认为使它成为 typedef 的更改来自 AT&T 代码,因为它出现在 SVr4 实现中。

出于这个原因(也是保证 FILE 被正确声明的最简单方法),curses.h 将包括 stdio.h.

现在...您可能会遇到其他一些 header 文件,这些文件需要包含另一个 header 文件才能使它们有用。一个长期存在的例子是 stat 函数,即

#include <sys/types.h>
#include <sys/stat.h>

但在相关的 (system-specific) 文档中,两个 header 都显示在手册页摘要中。 statPOSIX 文档没有指出这一点,但在示例中使用了它。一些较新的实现包括(或以某种迂回方式提供)<sys/stat.h> 中所需的 <sys/types.h> 中的定义,但是在编写 可移植 代码时,这并不是什么靠。

curses 从未以这种方式记录,例如,您不太可能找到显示

的手册页
#include <stdio.h>
#include <curses.h>

因为最初的 4BSD curses 包括 <stdio.h>。有趣的是,4.2BSD curses header 没有使用 stdio.h 中的任何定义。它还包括 sgtty.h(类似于 termios.h),但在 header 文件中使用了这些定义。也许最初的开发者认为包含 stdio.h 是个好主意。无论如何,在后来的 curses 版本中,确实 使用了 FILE,并且由于从 curses.h 中包含 stdio.h. 的先例已经确立,没有人考虑像 <sys/types.h><sys/stat.h>.

那样拆分事情

ncurses 的文档在 NOTES 部分中提到了 header 文件,其中 curses.h 包括:

The header file <curses.h> automatically includes the header files <stdio.h> and <unctrl.h>.

该说明可追溯到 1994 中的 ncurses 1.8.7,因此您不太可能遇到声明不正确的 ncurses 版本。

X/Open Curses 有类似的说法:

The inclusion of <curses.h> may make visible all symbols from the headers <stdio.h>, <term.h>, <termios.h> and <wchar.h>.

例如,

HPUX curses 包括来自 <curses.h><term.h> 以在 curses.h 中声明 setupterm,但 ncurses(和 Solaris curses)没有。 AIX curses 包括 <term.h><termios.h>。同样,ncurses(和 Solaris curses)没有。 X/Open Curses 说 "may make visible" 因为包含 header 文件不一定会使 所有 符号可见(需要考虑 ifdef)。

然而,这些函数在任何 X/Open Curses(即任何比 1990 更新的版本)中使用 FILE,使得 <curses.h> 需要 <stdio.h>:

现在...包括 <stdio.h> before <curses.h> 可能不会有任何区别(除了给你的程序增加混乱)。 <curses.h> 中可能(但不太可能)重新定义某些符号,这会改变 <stdio.h> 中标准功能的行为。由于您可能遇到的 stdio.h 的任何实现都具有 include-guards(1990 年代的功能——在 SunOS 4 的 1994 header 中没有出现),包括 <stdio.h> after <curses.h> 也不太可能改变程序的行为(同样,混乱是唯一的结果)。

Include-guards 自 1990 年代以来一直是系统 header 的首选样式。我有一个脚本(称为 Include),用于我在 1998 年编写的 test-compiling headers,以检查给定的 header 包括必要的 headers(例如在本例中)以提供 header 文件中使用的所有必要的类型定义和符号。