[英]What exactly do C include guards do?
我有一个关于在C中包含警卫的问题。我已经做了一些阅读,但我会欣赏一点澄清。
假设我有一个带有函数定义的头文件“header.h”。
#ifndef HEADER_FILE
#define HEADER_FILE
int two(void){
return 2;
}
#endif
这个头文件有一个包含保护。 但是,我对#define HEADER_FILE实际上在做什么感到困惑。 假设我忘记了包含守卫,完全忽略添加'#define HEADER_FILE'对我来说是完全合法的。
所以我的问题是:当我们定义HEADER_FILE时,我们到底在做什么? 我们定义了什么? 为什么忘记包含警卫是可以的,在这种情况下我们也忘了添加#define HEADER_FILE?
任何帮助表示赞赏!
它是一个预处理器宏。
所有这些都是预处理器语法,基本上说,如果尚未定义此宏,则定义它并包含#ifndef
和#endif
之间的所有代码
它实现的目的是防止多次包含文件,这可能会导致代码出现问题。
你的问题:
为什么忘记包含警卫是可以的,在这种情况下我们也忘了添加#define HEADER_FILE?
忘记它是可以的,因为没有它它仍然是合法的C代码。 预处理器在编译之前处理您的文件,如果没有逻辑指定不应该的原因,则在最终程序中包含指定的代码。 这只是一种常见做法,但并不是必需的。
一个简单的例子可能有助于说明其工作原理:
您的头文件header_file.h
包含:
#ifndef HEADER_FILE
#define HEADER_FILE
int two(void){
return 2;
}
#endif
在另一个文件( foo.c
)中,您可能具有:
#include "header_file.h"
void foo() {
int value = two();
printf("foo value=%d\n", value);
}
一旦它被“预处理”并准备好编译,这将转化为:
int two(void){
return 2;
}
void foo() {
int value = two();
printf("foo value=%d\n", value);
}
所有包含守卫在这里完成的是确定是否应该粘贴#ifndef ...
和#endif
之间的标题内容来代替原始的#include
。
但是,由于该函数未声明为extern
或static
,并且实际上是在头文件中实现的,因此如果您尝试在另一个源文件中使用它,则会出现问题,因为不会包含函数定义。
您可以在此处阻止文件被多次包含
#ifndef HEADER_FILE
如果HEADER_FILE
则测试是否为真
#define HEADER_FILE
会定义它,现在如果你把文件包含在另一个文件中,第一次它将定义HEADER_FILE
,而第二次,它将被定义,因此文件的内容不会再次包含,因为#ifndef HEADER_FILE
将是假的。
请记住,这些是在实际编译完成之前由预处理器评估的,因此它们在编译时进行评估。
首先,在现代C ++编译中,您可以使用#pragma once
而不是包含保护。
然后,您的示例有点混乱,因为您在标题中定义了一个extern
函数。 通常include
文件用于定义函数的声明而不是函数的定义。
如果在标题中定义函数,并且此标题由多个CPP源文件使用,则此函数将使用相同的名称定义更多次,并且在链接程序时将出错!
更好的包括
#ifndef HEADER_FILE
#define HEADER_FILE
int two(void);
#endif
要么
#ifndef HEADER_FILE
#define HEADER_FILE
static int two(void) { return 2; }
#endif
要么
#pragma once
static int two(void) { return 2; }
在最后一种情况下,函数two()
在包含此标头的每个CPP源文件中定义; 但是这个函数是静态的,所以CPP源代码编译正确,CPP程序链接没有问题。
在你的问题中,你问
在这种情况下,我们还忘了添加#define HEADER_FILE?
就个人而言,我在非常特殊的棘手情况下使用相同的标题。
以下2个包含的是一个“好”的例子:
/*******************************************************************
* XTrace.Configuration.h
********************************************************************
*/
#pragma once
#define MODULEx(n) extern StructDefineMODULE MODULE_##n;
#include "XTrace.Modules.h"
#undef MODULEx
#define MODULEx(n) { #n, &MODULE_##n } ,
static struct ModuleTRACE tModuleTrace[]
= {
#include "XTrace.Modules.h"
{ 0, 0 }
};
以下是XTrace.Modules.h
包含的内容
/*******************************************************************
* XTrace.Modules.h
********************************************************************
*/
MODULEx( BBDIXFILE )
MODULEx( CECHO )
MODULEx( INITDBFIELD )
MODULEx( IVIRLUX )
第一个包含#pragma once
并调用相同的内部包含2次。
第一次调用它来定义StructDefineMODULE结构的extern声明。
第二次调用初始化ModuleTRACE结构数组。
由于此包含被称为2次,因此必须避免使用#pragma once
或#ifndef
。
在使用内部包含时,我确信在100%时,用于定义StructDefineModule的所有元素也用于初始化tModuleTrace []数组。
包含内部结果,将是
/*******************************************************************
* XTrace.Configuration.h
********************************************************************
*/
#pragma once
extern StructDefineMODULE MODULE_BBDIXFILE;
extern StructDefineMODULE MODULE_CECHO;
extern StructDefineMODULE MODULE_INITDBFIELD;
extern StructDefineMODULE MODULE_IVIRLUX;
static struct ModuleTRACE tModuleTrace[]
= { { "BBDIXFILE" , &MODULE_BBDIXFILE }
, { "CECHO" , &MODULE_CECHO }
, { "INITDBFIELD" , &MODULE_INITDBFIELD }
, { "IVIRLUX" , &MODULE_IVIRLUX }
, { 0, 0 }
};
我希望这可以帮助你理解为什么,在某些情况下,包括警卫可以避免!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.