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?
Linking − The linker is produces the final compilation output from the object files the compiler produced. This output can be a shared (or dynamic) library or an executable. It links the object files by replacing the undefined references with the correct addresses.
c' files call the pre-assembly of include files "compiling header files". However, it is an optimization technique that is not necessary for actual C development.
Compiling - The modified source code is compiled into binary object code. This code is not yet executable. Linking - The object code is combined with required supporting code to make an executable program. This step typically involves adding in any libraries that are required.
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.
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.
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.
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
.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With