cloopslinker

Include loop - linker or compiler error?


I am wondering if an infinite loop of including files causes a compiler problem or a linker problem. I tried this :

/* file : try.c */
#include "try1.c"
int main(void) {}

/* file : try1.c */
#include "try.c"
int a(void) { return 0; }

The command to compile is :

gcc -Wall try.c -o try

This obviously causes a very long output (starts like this) :

try.c:5:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try.c:1:0,
                 from try1.c:1,
                 from try.c:1:
try1.c:4:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try1.c:1:0,
                 from try.c:1:
try.c:5:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try.c:1:0:
try1.c:4:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
try.c:5:1: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token
In file included from try.c:2:0,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                 from try.c:1,
                 from try1.c:1,
                   .
                   .
                   etc...

Well, obviously there is an infinite loop here. But when does it occur ? At the compiling process or the linker one? I think you are going to tell me at the compiling process because it will define here more than one function with the same name (because of the loop), but isn't the part that unite files occur at the linker process (And then there are no compilation problem for only one file) ?

Thanks !


Solution

  • Actually, the expansion of #include- type statements is called a "preprocessing" step. I used to think that these steps were all handled as a separate step before any "compiling" happened, but @EricPostpischil pointed out in the comments (and gave an example to demonstrate it) that the two things - preprocessing and compiling - appear to happen concurrently (as the order of lines in the source file dictates). In other words, the expansion of # commands ("preprocessor directives") is done "as compilation happens". In that sense, the error is a "compile" error; but in my mind it is valid to say "the #include gets handled by the preprocessor". When "preprocessor steps" are handled by the compiler, the line is blurred. It is definitely not the linker that is causing the problem - the compiler will have given up long before you get to that step.

    As a general comment, it is not good practice to #include one .c file in another - this is what the linker should be used for. And in order to prevent "recursive includes", you will often see a structure like this in the .h files that come with your compiler:

    #ifndef __MYINCLUDEFILE
    #define __MYINCLUDEFILE
    ... put the body of the include file here
    #endif
    

    this ensures that an include file will only be included once, even if it's called from multiple places (the first time it's included, the __MYINCLUDEFILE variable will be defined; next time it is included, the entire body of the function is skipped). Once you do this with every include file, the kind of "recursive trap" that you fell into can no longer occur.

    As was pointed out by @wildplasser, the use of _NAME and __NAME is reserved for the language and the implementation - I am using it as an example because you will see constructs like this in the header files that ship with your compiler. When you create your own .h files you have to think of another convention that creates unique identifiers.