C包括警卫究竟做了什么?

时间:2015-01-07 01:33:26

标签: c

我有一个关于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?

感谢任何帮助!

3 个答案:

答案 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

但是,由于该函数未声明为externstatic,并且实际上是在头文件中实现的,如果您尝试在另一个源文件中使用它,则会出现问题,因为函数定义不包括在内。

答案 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 }
  };

我希望这可以帮助您理解为什么在某些情况下可以避免包括护身符!