我应该在标头中使用 #include 吗?

发布于 2024-08-12 04:32:57 字数 296 浏览 2 评论 0原文

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

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

如果是,我是否还必须将其放在 #ifndef#define 之间或在 #define 之后?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

樱花落人离去 2024-08-19 04:32:57

NASA 戈达德太空飞行中心 (GSFC) C 中的标头规则指出必须可以在源文件中包含一个标头作为唯一的标头,然后使用该标头提供的功能的代码将进行编译。

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

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

此规则的好处是,如果有人需要使用标头,他们不必费力地找出还必须包含哪些其他标头 - 他们知道标头提供了所需的一切。

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

实现

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

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

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

因此,假设我们有一个魔法排序。

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...
}

请注意,标头必须包含一些定义 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"

...

这是我唯一一次知道该模块的私有header 不是实现文件中的第一个标头。然而,“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 语句;全部
#include 在正文文件中完成。单元头文件必须包含用于检查的 #ifdef 语句
所需的标头以正确的顺序包含在内。

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

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

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

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

GSFC 标准可通过互联网档案馆获取 2012-12-10

信息礼貌埃里克·S.布林顿:< /em>

可以通过 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

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

#endif /* HEADERGUARDMACRO */

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 */
就是爱搞怪 2024-08-19 04:32:57

一个好的做法是仅在包含文件需要 #includes 时才将其放入包含文件中。如果给定包含文件中的定义仅在 .c 文件中使用,则仅将其包含在 .c 文件中。

在您的情况下,我会将其包含在 #ifdef/#endif 之间的包含文件中。

这将最大限度地减少依赖性,以便在包含文件更改时不需要重新编译不需要给定包含的文件。

A good practice is to only put #includes in an include file if the include file needs them. If the definitions in a given include file are only used in the .c file then include it only in the .c file.

In your case, i would include it in the include file between the #ifdef/#endif.

This will minimize dependencies so that files that don't need a given include won't have to be recompiled if the include file changes.

执笔绘流年 2024-08-19 04:32:57

在编译期间,预处理器仅用指定的文件内容替换#include指令。
为了防止无限循环,应该使用“

#ifndef SOMEIDENTIFIER
#define SOMEIDENTIFIER
....header file body........
#endif

如果某个标头包含在文件中包含的另一个标头中”
不需要再次显式包含它,因为它将递归地包含到文件中

During compilation preprocessor just replaces #include directive by specified file content.
To prevent endless loop it should use

#ifndef SOMEIDENTIFIER
#define SOMEIDENTIFIER
....header file body........
#endif

If some header was included into another header which was included to your file
than it is not necessary to explicitly include it again, because it will be included into the file recursively

冰雪梦之恋 2024-08-19 04:32:57

通常,库开发​​人员会使用#ifndef /#define / #endif“技巧”来保护他们的包含免受多重包含的影响,因此您不必这样做。

当然,您应该检查...但无论如何编译器会在某个时候告诉您;-) 无论如何,检查多个包含项是一个很好的做法,因为它会减慢编译周期。

Usually, library developers protect their includes from multiple including with the #ifndef /#define / #endif "trick" so you don't have to do it.

Of course, you should check... but anyways the compiler will tell you at some point ;-) It is anyhow a good practice to check for multiple inclusions since it slows down the compilation cycle.

谢绝鈎搭 2024-08-19 04:32:57

是的,这是必要的,否则编译器在尝试编译它不“知道”的代码时会抱怨。 #include 是对编译器的提示/推动/肘,告诉它选择声明、结构等以便成功编译。 jldupont 指出的 #ifdef/#endif 标头技巧是为了加快代码编译速度。

它用于具有 C++ 编译器并编译纯 C 代码的实例,如下所示
下面是这个技巧的一个例子:

#ifndef __MY_HEADER_H__
#define __MY_HEADER_H__

#ifdef __cplusplus
extern "C" {
#endif


/* C code here such as structures, declarations etc. */

#ifdef __cplusplus
}
#endif

#endif /* __MY_HEADER_H__ */

现在,如果多次包含它,编译器只会包含它一次,因为符号 __MY_HEADER_H__ 被定义一次,这会加快编译时间。
请注意上面示例中的符号 cplusplus,如果您有 C 代码,那么这是处理 C++ 编译的正常标准方法。

我已经包含了上面的内容来展示这一点(尽管与海报的原始问题)。
希望这有帮助,
此致,
汤姆.

PS:很抱歉让任何人对此投反对票,因为我认为这对于 C/C++ 新手来说是有用的。欢迎留下评论/批评等。

Yes it is necessary or the compiler will complain when it tries to compile code that it is not "aware" of. Think of #include's are a hint/nudge/elbow to the compiler to tell it to pick up the declarations, structures etc in order for a successful compile. The #ifdef/#endif header trick as pointed out by jldupont, is to speed up compilation of code.

It is used in instances where you have a C++ compiler and compiling plain C code as shown here
Here is an example of the trick:

#ifndef __MY_HEADER_H__
#define __MY_HEADER_H__

#ifdef __cplusplus
extern "C" {
#endif


/* C code here such as structures, declarations etc. */

#ifdef __cplusplus
}
#endif

#endif /* __MY_HEADER_H__ */

Now, if this was included multiple times, the compiler will only include it once since the symbol __MY_HEADER_H__ is defined once, which speeds up compilation times.
Notice the symbol cplusplus in the above example, that is the normal standard way of coping with C++ compiling if you have a C code lying around.

I have included the above to show this (despite not really relevant to the poster's original question).
Hope this helps,
Best regards,
Tom.

PS: Sorry for letting anyone downvote this as I thought it would be useful tidbit for newcomers to C/C++. Leave a comment/criticisms etc as they are most welcome.

Spring初心 2024-08-19 04:32:57

您需要包含标头中的标头,并且无需将其包含在 .c 中。包含应该在 #define 之后,这样就不会不必要地多次包含它们。例如:

/* myHeader.h */
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <glib.h>

struct S
{
    gchar c;
};

#endif /* MY_HEADER_H */

/* myCode.c */
#include "myHeader.h"

void myFunction()
{
    struct S s;
    /* really exciting code goes here */
}

You need to include the header from your header, and there's no need to include it in the .c. Includes should go after the #define so they are not unnecessarily included multiple times. For example:

/* myHeader.h */
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <glib.h>

struct S
{
    gchar c;
};

#endif /* MY_HEADER_H */

and

/* myCode.c */
#include "myHeader.h"

void myFunction()
{
    struct S s;
    /* really exciting code goes here */
}
〃安静 2024-08-19 04:32:57

我使用以下构造来确保所需的包含文件在此包含之前包含。我仅将所有头文件包含在源文件中。

#ifndef INCLUDE_FILE_H
 #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h"
#endif

I use the following construct to be sure, that the needed include file is included before this include. I include all header files in source files only.

#ifndef INCLUDE_FILE_H
 #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h"
#endif
梦言归人 2024-08-19 04:32:57

我通常做的是创建一个包含文件,其中以正确的顺序包含所有必要的依赖项。所以我可能有:

#ifndef _PROJECT_H_
#define _PROJECT_H_
#include <someDependency.h>
#include "my_project_types.h"
#include "my_project_implementation_prototypes.h"
#endif

全部在project.h中。现在,project.h 可以包含在任何地方,没有顺序要求,但我仍然可以将依赖项、类型和 API 函数原型放在不同的标头中。

What I normally do is make a single include file that includes all necessary dependencies in the right order. So I might have:

#ifndef _PROJECT_H_
#define _PROJECT_H_
#include <someDependency.h>
#include "my_project_types.h"
#include "my_project_implementation_prototypes.h"
#endif

All in project.h. Now project.h can be included anywhere with no order requirements but I still have the luxury of having my dependencies, types, and API function prototypes in different headers.

翻身的咸鱼 2024-08-19 04:32:57

只需将所有外部头文件包含在项目中的一个通用头文件中,例如global.h,并将其包含在所有 c 文件中:

它可能如下所示:

#ifndef GLOBAL_GUARD
#define GLOBAL_GUARD

#include <glib.h>
/*...*/
typedef int  YOUR_INT_TYPE;
typedef char YOUR_CHAR_TYPE;
/*...*/
#endif

此文件使用include Guard 以避免多重包含、非法多重定义等。

Just include all external headers in one common header file in your project, e.g. global.h and include it in all your c files:

It can look like this:

#ifndef GLOBAL_GUARD
#define GLOBAL_GUARD

#include <glib.h>
/*...*/
typedef int  YOUR_INT_TYPE;
typedef char YOUR_CHAR_TYPE;
/*...*/
#endif

This file uses include guard to avoid multiple inclusions, illegal multiple definitions, etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文