What exactly do C include guards do?
Asked Answered
C

3

28

Let's say I have a header file "header.h" with a function definition.

#ifndef HEADER_FILE
#define HEADER_FILE

int two(void){
return 2;
}

#endif

This header file has an include guard. However, I'm kind of confused as to what #define HEADER_FILE is actually doing. Let's say I were to forget the include guard, it would have been perfectly legal for me to completely ignore adding '#define HEADER_FILE'.

What exactly are we doing when we define HEADER_FILE? What are we defining? And why is it okay to forget the include guard in which case we can also forgot adding #define HEADER_FILE?

Cardiac answered 7/1, 2015 at 1:33 Comment(5)
You probably shouldn't include code in header files since the include guards only protect against multiple inclusion is a single translation unit. Including that header file in two separate source files is likely to cause a double-definition error when linking.Irade
Hmmm a non-static function definition inside a code guard. Looks like a problem.Phlogistic
That's true, and is worth noting. The code guards protect against multiple inclusion in a single transaction, but does not protect against multiple inclusion when two different object files are compiled separately and subsequently linked after.Proselytism
in C, this function: 'int two(void){ return 2; }' should NEVER be in the header file. rather just the prototype: 'int two(void);' the function should actually be in the .c file.Northman
en.wikipedia.org/wiki/Include_guardFolderol
P
27

It's a preprocessor macro.

All of it is preprocessor syntax, that basically says, if this macro has not already been defined, define it and include all code between the #ifndef and #endif

What it accomplishes is preventing the inclusion of file more than once, which can lead to problems in your code.

Your question:

And why is it okay to forget the include guard in which case we can also forgot adding #define HEADER_FILE?

It's OK to forget it because it's still legal C code without it. The preprocessor processes your file before it's compiled and includes the specified code in your final program if there's no logic specifying why it shouldn't. It's simply a common practice, but it's not required.

A simple example might help illustrate how this works:

Your header file, header_file.h we'll say, contains this:

#ifndef HEADER_FILE
#define HEADER_FILE

int two(void){
    return 2;
}

#endif

In another file (foo.c), you might have:

#include "header_file.h"

void foo() {
    int value = two();
    printf("foo value=%d\n", value);       
}

What this will translate to once it's "preprocessed" and ready for compilation is this:

int two(void){
    return 2;
}

void foo() {
    int value = two();
    printf("foo value=%d\n", value);       
}

All the include guard is accomplishing here is determining whether or not the header contents between the #ifndef ... and #endif should be pasted in place of the original #include.

However, since that function is not declared extern or static, and is actually implemented in a header file, you'd have a problem if you tried to use it in another source file, since the function definition would not be included.

Proselytism answered 7/1, 2015 at 1:36 Comment(4)
So correct me if I am wrong. HEADER_FILE is basically a 'macro folder' that contains all of macro definitions for everything between #ifndef and #endif ?Cardiac
@Teague No, it's just a symbol, like a variable, but it's just preprocessor syntax. Including a file using #include is really just taking the contents of the file and pasting it in place of the #include in another file. The preprocessor in this case will see you've previously defined a symbol called HEADER_FILE and determine whether or not the code should be included againProselytism
you are wrong - thr preprocessor sees that HEADER_FILE is already defined and so skips the rest of the file (its a big if statement)Phocomelia
Okay, just did some more background reading. This makes total sense. Thank you!Cardiac
G
9

You prevent the file from being included more than once, here

#ifndef HEADER_FILE

you test if HEADER_FILE is NOT defined, in case that's true then

#define HEADER_FILE

would define it, now if you include the file in another file, the first time it will define HEADER_FILE, while the second time, it will be already defined and hence the content of the file is not included again, since the #ifndef HEADER_FILE will be false.

Remember that these are evaluated by the preprocessor before actual compilation is done, so they are evaluated at compile time.

Gratify answered 7/1, 2015 at 1:35 Comment(0)
B
2

First of all, in modern C++ compile you can use #pragma once instead of include guards.

Then, your example is a little confuse, because you define an extern function in your header. Normally include files are used to define function's declarations and not function's definitions.

If you define functions in your header and if this header is used by more than one CPP source files, this function will be define more times with same name and you will have an error when program will be linked !

A better include would be

#ifndef HEADER_FILE
#define HEADER_FILE

int two(void);

#endif

or

#ifndef HEADER_FILE
#define HEADER_FILE

static int two(void) { return 2; }

#endif

or

#pragma once

static int two(void) { return 2; }

In the last case, function two() is defined in each CPP source files that include this header; but this function is static, so CPP sources are compiled correctly and CPP program is linked without problem.

In your question, you ask

in which case we can also forgot adding #define HEADER_FILE?

Personally, I use same header in very special tricky situation.

The following 2 includes are a "good" example:

/*******************************************************************
* 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 }
  };

where XTrace.Modules.h include is following

/*******************************************************************
* XTrace.Modules.h
********************************************************************
*/

MODULEx( BBDIXFILE )
MODULEx( CECHO )
MODULEx( INITDBFIELD )
MODULEx( IVIRLUX )

The first include contains #pragma once and call same internal include 2 times.

The first time it is called to define extern declaration of StructDefineMODULE structure.

The second time is is called to initialize an array of ModuleTRACE structures.

Since this include is called 2 times, #pragma once or #ifndef must be avoid.

In using an internal include I'm sure at 100% that all elements used to define StructDefineModule are also used to initialize tModuleTrace[] array.

The include internal result, would be

/*******************************************************************
* 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 }
  };

I hope that this can help you to understand why, in some situations, include guards can be avoid !

Basuto answered 17/4, 2019 at 9:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.