C #包括<&燃气轮机;不同文件中的文件

C #包括<&燃气轮机;不同文件中的文件,c,compiler-construction,C,Compiler Construction,如果我有几个头文件:比如说1.h,2.h,3.h。 假设所有三个头文件都有#include,其中一个包含文件。 当我必须在C文件main.C中使用所有3个头文件时, 在预处理器之后,它将有3份#include。 编译器如何处理这种冲突? 这是一个错误还是会产生任何开销 如果没有头球后卫,会发生什么 这通常通过预处理器语句解决: #ifndef __STDLIB_H #include <stdlib.h> #define __STDLIB_H #endif \ifndef\u

如果我有几个头文件:比如说
1.h
2.h
3.h
。 假设所有三个头文件都有
#include
,其中一个包含文件。 当我必须在C文件
main.C
中使用所有3个头文件时, 在预处理器之后,它将有3份
#include
。 编译器如何处理这种冲突? 这是一个错误还是会产生任何开销

如果没有头球后卫,会发生什么


这通常通过预处理器语句解决:

#ifndef __STDLIB_H
  #include <stdlib.h>
  #define __STDLIB_H
#endif
\ifndef\uuuu STDLIB\H
#包括
#定义标准库
#恩迪夫

虽然我从未在诸如stdlib.h之类的常见头文件中看到过它,但它可能只是您自己的头文件所必需的。

预处理器将包含所有三个副本,但将阻止解析除第一个副本以外的所有副本

头保护将告诉预处理器将该头文件的后续副本有效地转换为零

编辑响应:

标准库标题将具有标题防护装置。如果他们没有警卫,那将是非常不寻常和不正确的

同样,您有责任在自己的标题上使用标题保护


假设缺少标题保护,您将得到与重复定义相关的各种错误。

这是由两种流行技术之一完成的,这两种技术都由stdlib负责

一种方法是定义一个唯一常量并检查它,如果已经定义了,则#ifdef输出文件的所有内容

另一个是microsoft特有的
#pragma once
,它的优点是,如果硬盘已经包含了,甚至不必从硬盘读取(通过记住确切的路径)


必须在生成的所有头文件中执行相同的操作。或者,包含您的标题的标题将有问题。

大多数C标题的包装方式如下:

#ifndef FOO_H
#define FOO_H

/* Header contents here */

#endif
预处理器第一次扫描时,它将包括头的内容,因为
FOO_H
未定义;但是,它还定义了防止再次添加标题内容的
FOO_H

多次包含标头对性能的影响很小:预处理器每次都必须转到磁盘读取标头。这可以通过在C文件中添加保护来缓解,包括:

#ifndef FOO_H
#include <foo.h>
#endif
\ifndef FOO\H
#包括
#恩迪夫

这方面的内容在(一本优秀的书)中进行了详细讨论。

另一点:您可以将函数(或外部变量)重新声明数百万次,编译器将接受它:

int printf(const char*, ...);
int printf(const char*, ...);
完全合法,编译开销小,但没有运行时开销

当一个不受保护的包含文件被多次包含时,就会发生这种情况


请注意,对于include文件中的所有内容都不是这样。例如,您不能重新声明枚举。

据我所知,常规include只会抛出另一个文件的内容。标准库stdlib.h通常使用代码保护:,因此最终只包含一个副本。但是,如果你这样做,你可以打破它(一定要试试!):#包括一个,#取消一个#守卫,#再次包括一个

现在。。。为什么在另一个.h中包含.h?这可以是好的,至少在C++中是这样,但是最好避免。您可以使用转发声明:

只要代码不需要知道导入的结构在标题中的大小,就可以使用这些方法。您可能希望将一些函数参数按值转换为按引用/指针转换的参数,以解决此问题


另外,对于您自己的头文件,始终使用include-guard或#pragma一次

正如其他人所说,对于标准库头,系统必须确保头多次包含的效果与头一次包含的效果相同(它们必须是幂等的)。该规则的一个例外是
assert.h
,其效果可能会根据是否定义了
NDEBUG
而变化。引用C标准:

标准标题可以按任何顺序包含;每一个都可以包含一次以上 一个给定的范围,除了 包含
”的效果

但是多次包含任何其他头的效果不一定相同,然后由头编写器来确保没有冲突

例如,给定标题:

int a;
包含它两次将导致
a
的两个定义。这是一件坏事

避免此类冲突的最简单方法是使用上面定义的include guards:

#ifndef H_HEADER_NAME_
#define H_HEADER_NAME_
/* header contents */
#endif
这适用于所有编译器,并且不依赖特定于编译器的
#pragma
s(即使使用上述方法,在头文件中定义变量也不是一个好主意)

当然,在代码中,您应该确保include guard的宏名称满足以下条件:

  • 它不是以
    E
    开头,后跟大写字符
  • 它不是以
    PRI
    开头,后跟小写字符或
    X
  • 它不是以
    LC.
    开头,后跟大写字符
  • 它不是以
    SIG
    /
    SIG\uu
    开头,后跟大写字符
…等等(这就是为什么我更喜欢
H\u NAME\u

作为一个反常的例子,如果你想让你的用户猜测某些缓冲区大小,你可以有一个这样的头(警告:不要这样做,这应该是一个笑话)


是的,你是对的!但是如果不实现,会发生什么?编译器如何处理?它会产生任何错误吗?include卫士总是在.h文件中。如果没有它们,你会得到很多重复的定义错误如果其他人
#ifndef SZ
#define SZ 1024
#else
#if SZ == 1024
#undef SZ
#define SZ 128
#else
#error "You can include me no more than two times!"
#endif
#endif