【问题标题】:Should I include <stdio.h> in my header file, just so I can declare a function that takes a FILE*?我是否应该在我的头文件中包含 <stdio.h>,以便我可以声明一个接受 FILE* 的函数?
【发布时间】:2015-02-16 17:08:49
【问题描述】:

例如foo.h:

typedef struct foo_t foo_t;

/* Lots of function declarations dealing with foo_t... */

int foo_print(const foo_t *foo); /* Print foo to stdout. */
int foo_fprint(FILE *f, const foo_t *foo); /* Print foo to file f. */

我不想在foo.h 中乱扔foo.h 的用户可能不想包含的太多其他头文件,但我确实需要声明采用FILE* 等类型的函数。我怀疑我是第一个遇到这种困境的人,那么人们在这种情况下通常会怎么做呢?还是我想避免在我的头文件中包含stdio.h 被误导了?

编辑:

人们似乎不理解我的问题。为了澄清,这里有一些潜在的解决方案:

  1. 只需包含stdio.h,不用担心它会在我的客户代码中引起冲突(例如,如果他们碰巧有自己的函数恰好被称为getchar)。
  2. 使用#ifdef 查明stdio.h 是否已被包含,然后才声明FILE* 相关函数。这样做的缺点是它会在我的客户代码中对#includes 施加特定的顺序。
  3. 将所有与 I/O 相关的声明移至单独的头文件,例如 foo_io.h

什么问题是最好的事情?

【问题讨论】:

  • I don't want to litter foo.h with too many other header files that users of foo.h might not have wanted to include 所以你想把你的客户的负担交给#include &lt;stdio.h&gt;,否则你的代码不能编译?您的代码应该在没有“用户干预”的情况下编译。
  • 问题是,没有头文件可以让你正确转发声明FILE(因此可以使用FILE *而不包括stdio.h),就像其他I/O一样(iosfwd)。所以你必须使用包含。
  • 写。编辑后的问题:正确答案是 1. 或 3. 如果您担心引入大量额外的标题和名称。无条件的#include &lt;stdio.h&gt; 应该永远对您的客户来说是个问题。这是 C:没有命名空间。如果您选择标准库中已经存在的名称这是您的错
  • @Matt: 对——如果是static,那么你的标题#include &lt;stdio.h&gt; 不会干扰它。
  • @Matt:抱歉,我之前的评论并不完全正确。如果getchar() 的标准声明和getchar 的静态声明很可能是编译时错误。 (没有链接时冲突,因为static 函数没有链接。)不过,我想说这不值得担心。如果需要使用FILE*,则需要包含&lt;stdio.h&gt;

标签: c++ c coding-style header-files declaration


【解决方案1】:

简短回答:您正在尝试解决一个不存在的问题。

如果使用FILE,则在 C++ 中包含stdio.hcstdio。就这么简单。

尝试“优化”#includes,除了明显未使用的情况外,不会给您带来任何好处并导致问题。

【讨论】:

  • 将购买旧编译器和/或没有预编译头文件的编译时间。
  • 尝试“优化”#include,除了明显未使用的情况外,不会给您带来任何好处并导致问题。我认为这取决于你的构建需要多长时间。我的意思是当你的构建需要 15 分钟以上时,你可能需要花一些时间来优化它。
  • 我从来没有说过优化。
【解决方案2】:

你应该努力让头文件在一个空的源模块中编译干净。例如:

#include "myheader.h"

就是这样。如果你把它放在一个 C++ 源文件中并在没有其他代码的情况下编译它,你应该得到 no 编译器错误。

如果您确实遇到了错误,那么您需要说明为什么会出现错误。我认为错误的唯一合理原因是标头在 your 库中是内部的,并且不应由您的库的用户独立使用。

如果您的客户应该使用标头,请不要通过获取上面的测试源文件并向源添加标头来“解决”问题。您可以通过在(在我的简单情况下)myheader.h

中包含正确的标题来修复它

【讨论】:

  • 这并不意味着包含是必要的。前向声明就足够了。
  • 正确 - 如果标头中的前向声明消除了编译器错误,那么这是替代方案。
  • 你能转发声明FILE 吗?我不这么认为。特别是,它可能被声明为typedef struct __FILE FILE;
  • @peppe FILE* 的前向声明是什么(如果您不知道使用的编译器)? (例如 gcc: typedef struct _IO_FILE FILE;)
  • @Matt 标准头文件(除非你有一个糟糕的编译器)有包含保护,所以用户是否包含头文件并没有区别。
【解决方案3】:

参考更新后的问题:

根据包含文件的顺序有条件地启用或禁用代码块(或功能)非常糟糕。这是通往地狱的直接道路。

如果您想启用或禁用您的功能,使您的代码更加模块化,您可以使用预处理器宏,可能需要用户明确选择编译模式。

#ifdef USE_STDIO
#include <stdio.h>
#endif

lotsa lotsa code

#ifdef USE_STDIO
int foo_print(const foo_t *foo);
int foo_fprint(FILE *f, const foo_t *foo);
#endif

此解决方案的缺点:代码变得更难遵循。

第二个选项是将这些方法提取到foo_io.h(可能还有foo_io.c)。此解决方案的缺点是您强制用户包含两个文件而不是一个。

【讨论】:

    【解决方案4】:

    您已经回答了自己的问题。 1) 和 3) 都是有效的解决方案。如果您在其中一个函数中使用FILE*,那么只有将您的函数与声明FILE 的标头结合使用才有意义。正如其他人所指出的,没有可以转发声明 FILE 的标头,因此您唯一的选择是包含 stdio.h

    如果你的客户端包含你的头文件,假设所有的函数都会被使用。不要使用条件编译来删除包含的内容。如果我包含您的头文件并看到它包含使用FILE* 的函数的声明,我希望也包含stdio.h

    【讨论】:

      【解决方案5】:

      如果您使用的是标准库功能,只需包含该标准头句号。不要过分认为有人可能有一个与标准库中的某个东西同名的函数:他们的代码已经很脆弱/代理了,你不应该担心这种情况。

      我无法确定您的代码是 C++ 还是 C(请注意,即使 C++ 可能起源于 C,但它们是不同的语言)。如果是 C++,您可以使用 cstdio 而不是 stdio.h 并使用来自 std 命名空间而不是全局命名空间的功能。否则,如果您的代码是 C,则必须使用 stdio.h

      【讨论】:

      • 井名冲突并不是不包含太多头文件的唯一原因。我的代码既不是 C 也不是 C++,因为它只是一个示例(尽管我似乎把它做得很像 C)。我将这两种语言用于各种目的,这是它们都提出的。
      猜你喜欢
      • 2021-12-09
      • 1970-01-01
      • 2020-07-11
      • 1970-01-01
      • 2020-12-06
      • 1970-01-01
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多