包含在头文件中

时间:2011-07-05 21:28:22

标签: c coding-style

头文件应该有#include个吗?

我一般认为这种等级包含是不好的。说你有这个:

foo.h中:

#include <stdio.h> // we use something from this library here
struct foo { ... } foo;

的main.c

#include "foo.h"
/* use foo for something */
printf(...)

当前main.c的实现发生了变化,您不再使用foo.h,编译将中断,您必须手动添加<stdio.h>

对此:

foo.h中

// Warning! we depend on stdio.h
struct foo {...

的main.c

#include <stdio.h> //required for foo.h, also for other stuff
#include "foo.h"

当你停止使用foo时,删除它不会破坏任何东西,但删除stdio.h会破坏foo.h。

#include是否应该禁止使用.h文件?

6 个答案:

答案 0 :(得分:4)

你已经概述了关于这个主题的两个主要哲学。

我自己的观点(我认为这就是人们真正拥有的所有观点)是标题应该尽可能自包含。我不想知道foo.h的所有依赖关系,只是为了能够使用该标头。我也鄙视必须按特定顺序包含标题。

但是,foo.h的开发人员也应该负责使其尽可能无依赖。例如,foo.h标题应该写成没有stdio.h的依赖,如果可能的话(使用前向声明可以帮助)。

请注意,C标准禁止标准标题包含另一个标准标题,但C ++标准不允许。因此,您可以看到从一个C ++编译器版本转移到另一个C ++编译器版本时所描述的问题例如,在MSVC中,包括用于引入<vector>的{​​{1}},但在MSVC 2010中不再出现,因此之前编译的代码可能不再是因为您可能需要专门包含{{1} }}

然而,尽管C标准似乎提倡第二种哲学,但请注意它也要求没有标题依赖于另一种标题,并且您可以按任何顺序包含标题。因此,您可以充分利用这两个领域,但代价是C库实施者的复杂性。他们必须跳过一些环节才能做到这一点(特别是为了支持可以通过几个标题中引入的定义,例如<iterator><iterator>)。我想那些起草C ++标准的人决定将这种复杂性添加到模仿者不再合理(我不知道C ++库实现者在多大程度上利用了'漏洞' - 看起来MS可能会收紧这个问题,即使技术上不需要它。)

答案 1 :(得分:3)

我的一般建议是:

  • 文件应该#include它需要什么。
  • 它不应该对#include所需要的东西产生别的影响。
  • 它不应该#include它不需要的东西,因为其他东西可能需要它。

真正的测试是这样的:您应该能够编译包含任何单个#include的源文件,并且除了“没有main()”之外不会得到任何错误或警告。如果你通过了这个测试,那么你可以期待任何其他任何东西能够#include你的文件没有问题。我写了一个名为“hcheck”的简短脚本,用来测试它:

#!/usr/bin/env bash
# hcheck: Check header file syntax (works on source files, too...)
if [ $# -eq 0 ]; then
    echo "Usage: $0 <filename>"
    exit 1
fi

for f in "$@" ; do
    case $f in
        *.c | *.cpp | *.cc | *.h | *.hh | *.hpp )
            echo "#include \"$f\"" > hcheck.cc
            printf "\n\033[4mChecking $f\033[0m\n"
            make -s $hcheck.o
            rm -f hcheck.o hcheck.cc
            ;;
    esac
done

我确信这个脚本可以做得更好,但它应该是一个很好的起点。

如果这太多了,如果你的头文件几乎总是有相应的源文件,那么另一种技术是要求相关的头是源文件中的第一个#include。例如:

foo.h中:

#ifndef Foo_h
#define Foo_h

/* #includes that Foo.h needs go here. */

/* Other header declarations here */

#endif

foo.c的:

#include "Foo.h"
/* other #includes that Foo.c needs go here. */

/* source code here */

这也显示了其他人提到的Foo.h中的“包含守卫”。

首先放置#include "Foo.h"Foo.h必须#include其依赖项,否则会出现编译错误。

答案 2 :(得分:2)

好吧,主要不应该首先依赖"foo.h"stdio。包括两次东西没有坏处。
此外,也许foo。 h 并不真正需要stdio。更有可能的是foo.c(实施)需要stdio

长话短说,我认为每个人都应该包括他们需要的东西,并依赖include guards

答案 3 :(得分:1)

一旦进入包含数百或数千个头文件的项目,这就变得站不住脚了。假设我有一个名为"MyCoolFunction.h"的头文件,其中包含MyCoolFunction()的原型,该函数将结构指针作为参数。我应该能够假设包含MyCoolFunction.h将包含所有必要的内容,并且允许我在不查看.h文件的情况下使用该函数来查看我需要包含的内容。

答案 4 :(得分:0)

如果头文件需要特定标头,请将其添加到头文件

#ifndef HEADER_GUARD_YOUR_STYLE
#define HEADER_GUARD_YOUR_STYLE

#include <stdio.h> /* FILE */
int foo(FILE *);

#endif /* HEADER GUARD */

如果代码文件不需要标题,请不要添加

/* #include <stdio.h> */ /* removed because unneeded */
#include <stddef.h> /* NULL */
#include "header.h"
int main(void) {
  foo(NULL);
  return 0;
}

答案 5 :(得分:0)

为什么不#include与标题对应的* .c文件中的内容?