包含文件包含两次

时间:2015-07-01 23:08:40

标签: c gcc

[编辑]似乎有些人在没有阅读我的帖子的情况下进行评论和投票。请在评论前阅读。例如:如果您认为我反对包含多次文件,那么您就错过了我的帖子。

当一些包含文件被包含两次时,我想通过输出错误来捕获草率编程。

我将#pragma once用于打算包含在其他包含文件中的包含文件。这很有效。

我知道我可以做一些"保护宏"实现这一目标。但有更好的方法吗?另一个#pragma或gcc编译选项?

这是一个守卫宏的样本:

#ifndef FILE_FOO_SEEN
#define FILE_FOO_SEEN

the entire file

#endif /* !FILE_FOO_SEEN */

这对我有用:

#ifdef FILE_FOO_SEEN

#error "FILE_FOO inappropriately included twice.

#endif /* FILE_FOO_SEEN */

#define FILE_FOO_SEEN
/* Active Code */

[edit]注意:我不是要问为什么有必要多次支持包含头文件。这是一个常见的案例,并且支持gcc编译指示。当我有一些我不想要包含的特定包含文件时,我要求的情况。如果有人不止一次地包含它们,那么它将是两个动作之一:1)将陷阱更改为#pragma一次,或2)更改代码以消除依赖性。

因为有一个特殊的pragma来支持多个包含,所以我认为有人可能有很好的技巧来避免这种情况。

[edit]添加我们遵循的一些与头文件使用相关的规则:

  1. 每个.c文件都有一个带有函数原型的伴随.h文件。您可以将此.h文件视为.c文件的接口。此.h文件还可能包含函数使用或返回的结构定义和枚举。此.h文件不需要包含在任何其他.h文件中,因为其他.h文件不使用函数。

  2. 我们不会为包含main()的.c文件创建伴随.h文件。

  3. 有时,结构和枚举的使用与返回或使用它们的函数无关。对于这些情况,枚举和结构将移动到独立的.h文件中。此.h文件将包含#pragma once,因为它可以多次包含。

  4. 首选不透明结构。

  5. 记录接口的Doxygen文档包含在.h文件中。这很有效,因为.h文件包含.c文件的完整接口。

  6. 我们根据需要对这些规则进行例外处理,但在大多数情况下,我们遵守这些规则。

  7. 遵循这些规则使得编写单元测试更容易,因为完整的界面易于识别和理解。

2 个答案:

答案 0 :(得分:2)

我认为你不应该这样做(提醒我不要使用你的标题,如果你决定继续这个计划,请。)

但是,如果您在两次包含标题时使用众所周知的防止损坏的机制开始:

<?php

 // $_POST['what_to_do'] is actually this one -  <input type="hidden" value="" id="what_to_do" name="what_to_do" />
if ($_POST['what_to_do'] == 'make_copy') {
    $action = "copy";
} elseif ($_POST['what_to_do'] == 'save_as') {
    $action = "edit";
}

然后您可以轻松地调整它以在文件 包含两次时生成错误(正如您在问题中所认识的那样):

#ifndef FILE_FOO_SEEN
#define FILE_FOO_SEEN

…the entire file…

#endif /* !FILE_FOO_SEEN */

由于#if defined(FILE_FOO_SEEN) #error File foo.h already included #else #define FILE_FOO_SEEN …the entire file… #endif /* FILE_FOO_SEEN */ 是标准C的一部分,因此它应该起作用 - 至少是为了从编译中获得诊断消息。它应该,但可能不会(过去的Solaris - 我正在看着你!),阻止编译成功。

根据您的编译器,您可能能够微调错误处理策略,但坦率地说,最好(到目前为止)只允许多次包含标题。

标题应该是独立的和幂等的。

  • 自包含意味着您不应该被要求明确包含任何其他内容以便能够使用标题(您只需编写#error并且它可以正常工作)。
  • 幂等意味着如果您通过任何机制将文件包含两次,则可以安全地工作。

C标准要求标准标题。严格来说,#include "header.h"并不是严格幂等的,但其他都是幂等的。它们都是自包含的,但<assert.h>允许包含<inttypes.h>

你应该遵循标准的主导 - 它提供了很好的使用技巧。

相关问题

我并未声明这是完整列表或无偏见的链接列表。

答案 1 :(得分:1)

tl; dr 您可能无法避免多个包含

更多解释为什么......

程序的不同部分包含相同的标题是完全正常的。 #include想要实现的目标是在使用之前已知所需的结构和功能。由于多个包含相同文件的速度减慢,因此编译和循环包含可能导致#pragma once#ifndef - #define - #endif构造被发明的无限循环。您可能会读到#pragma once已弃用且已过时但无论如何都会被使用...

类似地,有一个#import而不是#include仅包含该文件一次,但这是一个罕见的AFAIK,至少在C / C ++代码中。

现在让我们说我们在没有双重包含的情况下生成干净的代码,并为每个开发人员提供可能会执行此操作的警告。您现在遇到的问题是:如果不同文件中的多个类需要相同的数据结构,该怎么办?由于您只坚持使用单一包含,因此您需要知道文件的包含顺序。

实施例

A类需要来自B类的东西,但两者都需要带有结构的Header X.您将在A类之前包含B,并在A类文件中包含标题X.如果您现在更改类的依赖关系,则可能需要将标题X include移动到链顶部的另一个类。

现在想象一下这个包含许多文件的大项目,甚至更多的人独立工作。很明显,如果可能的话,没有人愿意解开依赖关系。所以你只需使用#pragma once,每个人都很开心,这基本上就是关于它的整个故事。

打印错误的可能方法

#warning并非完全与平台无关。

#error停止编译/预处理。

第三种方式应该得到GCC和MSVC的支持:

#pragma message("double include")
#pragma message "double include"