繁体   English   中英

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

[英]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

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

您可以在此处阻止文件被多次包含

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM