I know that header files have forward declarations of various functions, structs, etc. that are used in the .c
file that \'calls\' the #include
, ri
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.
Generally when you compile a file like this:
gcc -o program program.c
You really are calling a driver program, which does the following:
cpp
.cc1
as
(gas, the GNU Assembler).collect2
, which also uses ld
(the GNU linker).Typically, during the first 3 stages, you create a simple object file (.o
extension), which gets created by compiling a compilation unit (that is a .c file, with the #include and other directives replaced by the preprocessor).
The 4th stage is the one that creates the final executable. After compilation of a unit, the compiler marks several pieces of code as references that need to be resolved by the linker. The linker's job is to search among many compilation units and resolve references to external compilation units.
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.
The header provides access not only to other .c
files in the same program, but likewise to libraries that may be distributed in binary form. The relationship of one .c
file to another is exactly the same as a library that depends on another.
Since a programming interface needs to be in text form no matter the format of the implementation, header files make sense as a separation of concerns.
As others have mentioned, the program that resolves function calls and accesses between libraries and sources (translation units) is called the linker.
The linker does not work with headers. It just makes a big table of all the names that are defined in all the translation units and libraries, then links those names to the lines of code that access them. Archaic usage of C even allows for calling a function without any implementation declaration; it was just assumed that every undefined type was an int
.
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.