我有一个关于C中包含警卫的问题。我已经做了一些阅读,但我会感激一点澄清。
假设我有一个带有函数定义的头文件“header.h”。
#ifndef HEADER_FILE
#define HEADER_FILE
int two(void){
return 2;
}
#endif
此头文件包含一个include guard。但是,我对#define HEADER_FILE实际上在做什么感到困惑。假设我忘记了包含守卫,完全忽略添加'#define HEADER_FILE'对我来说是完全合法的。
所以我的问题:我们在定义HEADER_FILE时到底在做什么?我们定义了什么?为什么忘记包含警卫是可以的,在这种情况下我们也忘了添加#define HEADER_FILE?
感谢任何帮助!
答案 0 :(得分:13)
这是一个预处理器宏。
所有这些都是预处理器语法,基本上说,如果尚未定义此宏,则定义它并包含#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
,并且实际上是在头文件中实现的,如果您尝试在另一个源文件中使用它,则会出现问题,因为函数定义不包括在内。
答案 1 :(得分:5)
您可以阻止该文件被多次包含,此处
#ifndef HEADER_FILE
你测试是否未定义HEADER_FILE
,如果是,那么
#define HEADER_FILE
将定义它,现在如果你将文件包含在另一个文件中,第一次定义HEADER_FILE
,而第二次,它将被定义,因此文件的内容不再包含,因为#ifndef HEADER_FILE
将是假的。
请记住,这些是在实际编译完成之前由预处理器评估的,因此它们在编译时进行评估。
答案 2 :(得分:0)
首先,在现代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; }
在最后一种情况下,在每个包含此头的CPP源文件中定义了函数two()
;但是此功能是静态的,因此可以正确编译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
,并两次调用相同的内部包含。
第一次调用它来定义StructDefineMODULE结构的外部声明。
第二次被调用以初始化ModuleTRACE结构的数组。
由于此包含被调用了两次,因此必须避免使用#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 }
};
我希望这可以帮助您理解为什么在某些情况下可以避免包括护身符!