我应该在标题中使用 #include 吗? [英] Should I use #include in headers?

查看:42
本文介绍了我应该在标题中使用 #include 吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否需要#include某个文件,如果在头文件(*.h)中,使用了该文件中定义的类型?

例如,如果我使用 GLib 并希望在我的头文件中定义的结构中使用 gchar 基本类型,是否有必要执行 #include <glib.h>,知道我的 *.c 文件中已经有了它?

如果是,我还必须把它放在 #ifndef#define 之间还是在 #define 之后?

解决方案

NASA 的戈达德太空飞行中心 (GSFC) C 中的头文件规则规定,必须可以在源文件中包含一个头文件作为唯一的头文件,然后使用该头文件提供的工具的代码将被编译.>

这意味着标头必须是自包含的、幂等的和最小的:

  • 自包含 — 所有必要的类型都通过在需要时包含相关标头来定义.
  • 幂等 - 即使多次包含编译也不会中断.
  • 最小——它不定义任何使用标头访问标头定义的设施的代码不需要的东西.

这条规则的好处是,如果有人需要使用标头,他们不必费力找出还必须包含哪些其他标头——他们知道标头提供了所有必要的东西.

可能的缺点是某些标题可能会被多次包含;这就是为什么多重包含头保护是至关重要的(也是编译器尽可能避免重新包含头的原因).

实施

此规则意味着,如果标头使用一种类型——例如FILE *"或size_t"——那么它必须确保适当的其他标头( 例如)应该包括在内.一个经常被遗忘的推论是,头不应该包含包的用户为了使用包而不需要的任何其他头.换句话说,标题应该是最小的.

此外,GSFC 规则提供了一种简单的技术来确保发生这种情况:

  • 在定义功能的源文件中,标题必须是列出的第一个标题.

因此,假设我们有一个魔术排序.

magicsort.h

#ifndef MAGICSORT_H_INCLUDED#define MAGICSORT_H_INCLUDED#include typedef int (*Comparator)(const void *, const void *);extern void magicsort(void *array, size_t number, size_t size, Comparator cmp);#endif/* MAGICSORT_H_INCLUDED */

magicsort.c

#include void magicsort(void *array, size_t number, size_t size, Comparator cmp){……身体……}

注意头部必须包含一些定义size_t的标准头部;这样做的最小标准头文件是 ,尽管其他几个头文件也这样做().h>,可能还有其他一些).

另外,如前所述,如果实现文件需要一些其他的头文件,就这样吧,需要一些额外的头文件是完全正常的.但是实现文件('magicsort.c')应该包含它们本身,而不是依赖于它的头来包含它们.标题应该只包含软件用户需要的内容;不是实施者需要的.

配置标头

如果您的代码使用配置头文件(例如 GNU Autoconf 和生成的 'config.h'),您可能需要在 'magicsort.c' 中使用它:

#ifdef HAVE_CONFIG_H#include "config.h"#endif/* HAVE_CONFIG_H */#include "magicsort.h"...

这是我唯一一次知道模块的私有头不是实现文件中的第一个头.然而,'config.h' 的条件包含应该在 'magicsort.h' 本身中.

<小时>

更新 2011-05-01

上面链接的 URL 不再有效 (404).您可以在 EverySpec.com;C 标准 (582-2000-005) 似乎在行动中缺失.

C 标准的指导方针是:

<块引用>

§2.1 单位

(1) 代码应以单元或独立头文件的形式构成.

(2) 一个单元应由一个单一的头文件 (.h) 和一个或多个主体 (.c) 文件组成.头文件和正文文件统称为源文件.

(3) 单元头文件应包含客户单元所需的所有相关信息.一个单位的客户端只需访问头文件即可使用该单元.

(4) 单元头文件应包含单元头所需的所有其他头的#include 语句.这允许客户端通过包含单个头文件来使用单元.

(5) 单元主体文件应在所有其他#include 语句之前包含单元头的#include 语句.这让编译器验证所有必需的 #include 语句都在头文件.

(6) 主体文件应仅包含与一个单元相关的功能.一个正文文件可能不会为在不同头文件中声明的函数提供实现.

(7) 所有使用给定单元 U 任何部分的客户端单元都应包含单元 U 的头文件;这个确保只有一个地方定义了单元 U 中的实体.客户单位可只调用单元头中定义的函数;他们可能不会调用定义在正文但未在标题中声明.客户端单元不能访问主体中声明的变量但不在标题中.

一个组件包含一个或多个单元.例如,数学库是一个组件,它包含向量、矩阵、四元数等多个单位.

独立的头文件没有关联的正文;例如,一个常见的类型头做不声明函数,所以不需要主体.

一个单元有多个正文文件的一些原因:

  • 主体代码的一部分取决于硬件或操作系统,但其余部分是通用的.
  • 文件太大.
  • 该单元是一个通用的实用程序包,有些项目只会使用其中的几个职能.将每个函数放在一个单独的文件中允许链接器排除那些不从最终图像中使用.

§2.1.1 标题包含基本原理

此标准要求单元的标题包含所有其他所需标题的 #include 语句通过单元标题.将单元头的 #include 放在单元主体的第一个位置允许编译器验证标头是否包含所有必需的 #include 语句.

本标准不允许的替代设计,不允许在标题中使用 #include 语句;全部#includes 在正文文件中完成.单元头文件必须包含 #ifdef 语句来检查以正确的顺序包含所需的标题.

替代设计的一个优点是正文文件中的 #include 列表正是生成文件中需要的依赖项列表,编译器会检查该列表.随着标准设计,必须使用工具来生成依赖列表.然而,所有的分支推荐的开发环境提供了这样的工具.

替代设计的一个主要缺点是,如果单元所需的标题列表发生变化,每个文件必须编辑使用该单元的语句以更新 #include 语句列表.此外,所需的标题列表对于不同的目标,编译器库单元可能会有所不同.

替代设计的另一个缺点是编译器库头​​文件,以及其他第三方文件,必须修改以添加所需的 #ifdef 语句.

一种不同的常见做法是在任何项目头文件之前包含所有系统头文件,在体文件.本标准不遵循这种做法,因为有些项目头文件可能依赖于系统头文件,要么是因为它们使用了系统头文件中的定义,要么因为他们想要覆盖系统定义.这样的项目头文件应该包含#include系统标题的语句;如果主体首先包含它们,则编译器不会对此进行检查.

GSFC 标准可通过 Internet Archive 2012-12-10 获得

信息礼貌埃里克·S·布林顿:

可以通过 Internet 存档访问和下载引用的 NASA C 编码标准:

http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard

测序

问题还问:

<块引用>

如果是,我是否还必须将它(#include 行)放在 #ifndef#define 之间或之后?#define.

答案显示了正确的机制——嵌套的包含等应该在 #define 之后(并且 #define 应该是第二个非注释行标题)——但它没有解释为什么这是正确的.

考虑如果将 #include 放在 #ifndef#define 之间会发生什么.假设另一个标头本身包含各种标头,甚至可能间接包含 #include "magicsort.h" .如果 magicsort.h 的第二次包含发生在 #define MAGICSORT_H_INCLUDED 之前,那么将在定义它定义的类型之前第二次包含该标头.因此,在 C89 和 C99 中,任何 typedef 类型名称都会被错误地重新定义(C2011 允许将它们重新定义为相同的类型),并且您将获得处理开销文件多次,首先破坏了头球后卫的目的.这也是为什么 #define 是第二行而不是写在 #endif 之前的原因.给出的公式是可靠的:

#ifndef HEADERGUARDMACRO#define HEADERGUARDMACRO...标头的原始内容 - 其他 #include 行等...#endif/* HEADERGUARDMACRO */

Is it necessary to #include some file, if inside a header (*.h), types defined in this file are used?

For instance, if I use GLib and wish to use the gchar basic type in a structure defined in my header, is it necessary to do a #include <glib.h>, knowing that I already have it in my *.c file?

If yes do I also have to put it between the #ifndef and #define or after the #define?

解决方案

NASA's Goddard Space Flight Center (GSFC) rules for headers in C state that it must be possible to include a header in a source file as the only header, and that code using the facilities provided by that header will then compile.

This means that the header must be self-contained, idempotent and minimal:

  • self-contained — all necessary types are defined by including relevant headers if need be.
  • idempotent — compilations don't break even if it is included multiple times.
  • minimal — it doesn't define anything that is not needed by code that uses the header to access the facilities defined by the header.

The benefit of this rule is that if someone needs to use the header, they do not have to struggle to work out which other headers must also be included — they know that the header provides everything necessary.

The possible downside is that some headers might be included many times; that is why the multiple inclusion header guards are crucial (and why compilers try to avoid reincluding headers whenever possible).

Implementation

This rule means that if the header uses a type — such as 'FILE *' or 'size_t' - then it must ensure that the appropriate other header (<stdio.h> or <stddef.h> for example) should be included. A corollary, often forgotten, is that the header should not include any other header that is not needed by the user of the package in order to use the package. The header should be minimal, in other words.

Further, the GSFC rules provide a simple technique to ensure that this is what happens:

  • In the source file that defines the functionality, the header must be the first header listed.

Hence, suppose we have a Magic Sort.

magicsort.h

#ifndef MAGICSORT_H_INCLUDED
#define MAGICSORT_H_INCLUDED

#include <stddef.h>

typedef int (*Comparator)(const void *, const void *);
extern void magicsort(void *array, size_t number, size_t size, Comparator cmp);

#endif /* MAGICSORT_H_INCLUDED */

magicsort.c

#include <magicsort.h>

void magicsort(void *array, size_t number, size_t size, Comparator cmp)
{
    ...body of sort...
}

Note that the header must include some standard header that defines size_t; the smallest standard header that does so is <stddef.h>, though several others also do so (<stdio.h>, <stdlib.h>, <string.h>, possibly a few others).

Also, as mentioned before, if the implementation file needs some other headers, so be it, and it is entirely normal for some extra headers to be necessary. But the implementation file ('magicsort.c') should include them itself, and not rely on its header to include them. The header should only include what users of the software need; not what the implementers need.

Configuration headers

If your code uses a configuration header (GNU Autoconf and the generated 'config.h', for example), you may need to use this in 'magicsort.c':

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include "magicsort.h"

...

This is the only time I know of that the module's private header is not the very first header in the implementation file. However, the conditional inclusion of 'config.h' should probably be in 'magicsort.h' itself.


Update 2011-05-01

The URL linked above is no longer functional (404). You can find the C++ standard (582-2003-004) at EverySpec.com; the C standard (582-2000-005) seems to be missing in action.

The guidelines from the C standard were:

§2.1 UNITS

(1) Code shall be structured as units, or as stand-alone header files.

(2) A unit shall consist of a single header file (.h) and one or more body (.c) files. Collectively the header and body files are referred to as the source files.

(3) A unit header file shall contain all pertinent information required by a client unit. A unit’s client needs to access only the header file in order to use the unit.

(4) The unit header file shall contain #include statements for all other headers required by the unit header. This lets clients use a unit by including a single header file.

(5) The unit body file shall contain an #include statement for the unit header, before all other #include statements. This lets the compiler verify that all required #include statements are in the header file.

(6) A body file shall contain only functions associated with one unit. One body file may not provide implementations for functions declared in different headers.

(7) All client units that use any part of a given unit U shall include the header file for unit U; this ensures that there is only one place where the entities in unit U are defined. Client units may call only the functions defined in the unit header; they may not call functions defined in the body but not declared in the header. Client units may not access variables declared in the body but not in the header.

A component contains one or more units. For example, a math library is a component that contains multiple units such as vector, matrix, and quaternion.

Stand-alone header files do not have associated bodies; for example, a common types header does not declare functions, so it needs no body.

Some reasons for having multiple body files for a unit:

  • Part of the body code is hardware or operating system dependent, but the rest is common.
  • The files are too large.
  • The unit is a common utility package, and some projects will only use a few of the functions. Putting each function in a separate file allows the linker to exclude the ones not used from the final image.

§2.1.1 Header include rationale

This standard requires a unit’s header to contain #include statements for all other headers required by the unit header. Placing #include for the unit header first in the unit body allows the compiler to verify that the header contains all required #include statements.

An alternate design, not permitted by this standard, allows no #include statements in headers; all #includes are done in the body files. Unit header files then must contain #ifdef statements that check that the required headers are included in the proper order.

One advantage of the alternate design is that the #include list in the body file is exactly the dependency list needed in a makefile, and this list is checked by the compiler. With the standard design, a tool must be used to generate the dependency list. However, all of the branch recommended development environments provide such a tool.

A major disadvantage of the alternate design is that if a unit’s required header list changes, each file that uses that unit must be edited to update the #include statement list. Also, the required header list for a compiler library unit may be different on different targets.

Another disadvantage of the alternate design is that compiler library header files, and other third party files, must be modified to add the required #ifdef statements.

A different common practice is to include all system header files before any project header files, in body files. This standard does not follow this practice, because some project header files may depend on system header files, either because they use the definitions in the system header, or because they want to override a system definition. Such project header files should contain #include statements for the system headers; if the body includes them first, the compiler does not check this.

GSFC Standard available via Internet Archive 2012-12-10

Information courtesy Eric S. Bullington:

The referenced NASA C coding standard can be accessed and downloaded via the Internet archive:

http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard

Sequencing

The question also asks:

If yes, do I also have to put it (the #include lines) between the #ifndef and #define or after the #define.

The answer shows the correct mechanism — the nested includes, etc, should be after the #define (and the #define should be the second non-comment line in the header) — but it doesn't explain why that's correct.

Consider what happens if you place the #include between the #ifndef and #define. Suppose the other header itself includes various headers, perhaps even #include "magicsort.h" indirectly. If the second inclusion of magicsort.h occurs before #define MAGICSORT_H_INCLUDED, then the header will be included a second time before the types it defines are defined. So, in C89 and C99, any typedef type name will be erroneously redefined (C2011 allows them to be redefined to the same type), and you will get the overhead of processing the file multiple times, defeating the purpose of the header guard in the first place. This is also why the #define is the second line and is not written just before the #endif. The formula given is reliable:

#ifndef HEADERGUARDMACRO
#define HEADERGUARDMACRO

...original content of header — other #include lines, etc...

#endif /* HEADERGUARDMACRO */

这篇关于我应该在标题中使用 #include 吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆