C header files and compilation/linking
I know that header files have forward declarations of various functions, structs, etc. that are used in the .c
file that 'calls' the #include
, right? As far as I understand, the "separation of powers" occurs like this:
Header file: func.h
-
contains forward declaration of function
int func(int i);
C source file: func.c
-
contains actual function definition
#include "func.h" int func(int i) { return ++i ; }
C source file source.c
(the "actual" program):
#include <stdio.h>
#include "func.h"
int main(void) {
int res = func(3);
printf("%i", res);
}
My question is: seeing that the #include
is simply a compiler directive that copies the contents of the .h
in the file that #include
is in, how does the .c
file know how to actually execute the function? All it's getting is the int func(int i);
, so how can it actually perform the function? How does it gain access to the actual definition of func
? Does the header include some sort of 'pointer' that says "that's my definition, over there!"?
How does it work?
Solution 1:
Uchia Itachi gave the answer. It's the linker.
Using GNU C compiler gcc
you would compile a one-file program like
gcc hello.c -o hello # generating the executable hello
But compiling the two (or more) file program as described in your example, you would have to do the following:
gcc -c func.c # generates the object file func.o
gcc -c main.c # generates the object file main.o
gcc func.o main.o -o main # generates the executable main
Each object file has external symbols (you may think of it as public members). Functions are by default external while (global) variables are by default internal. You could change this behavior by defining
static int func(int i) { # static linkage
return ++i ;
}
or
/* global variable accessible from other modules (object files) */
extern int global_variable = 10;
When encountering a call to a function, not defined in the main module, the linker searches all the object files (and libraries) provided as input for the module where the called function is defined. By default you probably have some libraries linked to your program, that's how you can use printf
, it's already compiled into a library.
If you are really interested, try some assembly programming. These names are the equivalent of labels in assembly code.
Solution 2:
It's the linker that handles all that. The compiler just emits a special sequence in the object file saying "I have this external symbol func
, please resolve it" for the linker. Then linker sees that, and searches all other object files and libraries for the symbol.
Solution 3:
A declaration of a symbol without a definition within the same compilation unit tells the compiler to compile with a placeholder for that symbol's address into an object file.
The linker will see that a definition for the symbol is required, and will look for external definitions of the symbol in libraries and other object files.
If the linker finds a definition, the placeholder in the original object file will be replaced with the found address in the final executable.