Search code examples
cmacrosc-preprocessorifndef

Confusion with Preprocessor Directives


I have three files

File "grandparent.h"

#ifndef GRANDPARENT_H
#define GRANDPARENT_H

struct foo {
    int member;
};

#endif /* GRANDPARENT_H */

File "parent.h"

#include "grandparent.h"

File "child.c"

 #include "grandparent.h"
 #include "parent.h"

Wiki says

Here, the first inclusion of "grandparent.h" causes the macro GRANDPARENT_H to be defined. Then, when "child.c" includes "grandparent.h" the second time, the #ifndef test returns false, and the preprocessor skips down to the #endif, thus avoiding the second definition of struct foo. The program compiles correctly.

q1. "the first inclusion of "grandparent.h" causes the macro GRANDPARENT_H to be defined", So what i understand i its basically defining a macro named GRANDPARENT_H but what i dont understand is that how will the content of that macro (i.e GRANDPARENT_H) would be included in the child.c.

We are just defining the macro GRANDPARENT_H i.e

#define GRANDPARENT_H

struct foo {
    int member;
};

but how will its content i.e

struct foo {
    int member;
};

be included in the child.c


Solution

  • If you "expand" child.c manually until there are no #include left:

    //grandparent.h
    
    #ifndef GRANDPARENT_H    // <- not defined at this point
    #define GRANDPARENT_H    // <- now it's defined
    
    struct foo {
        int member;
    };
    
    #endif /* GRANDPARENT_H */
    //parent.h
    
    //#include "grandparent.h" resolves into
    //grandparent.h
    
    #ifndef GRANDPARENT_H   // <- already defined, skip until matching #endif
    #define GRANDPARENT_H   // <- not executed by preprocessor
    
    struct foo {            // <- not provided to the compiler
        int member;
    };
    
    #endif /* GRANDPARENT_H */
    

    now read it sequentially. The first line checks if macro GRANDPARENT_H is defined. Obviously it is not, as it's the first instruction of the code.

    The second line defines GRANDPARENT_H macro. It's empty but that's no important, what's important is that it is defined.

    Then, the code defines your struct...

    When the preprocessor encounters the second #ifdef GRANDPARENT_H, the macro is already defined, so it skips the whole contents of the file and you don't get any foo redefined error.

    Which is confirmed by using -E option to see preprocessed child.c file:

    $ gcc -E child.c
    # 1 "child.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "child.c"
    # 1 "grandparent.h" 1
    
    
    struct foo {
        int member;
    };
    # 2 "child.c" 2
    # 1 "parent.h" 1
    # 2 "child.c" 2
    

    as you can see, the structure is only defined once.

    Note that most compilers now support a simpler way to do this: just insert

    #pragma once
    

    at the start of your file. Like this:

    #pragma once
    
    struct foo {
        int member;
    };
    

    that's it!