cmacrosc-preprocessorheader-filesinclude-guards

What exactly do C include guards do?


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?


Solution

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