Compilation of C programs

In a previous post we saw how to create our first program in C, the classic Hello World!. However, we saw a very simple way to compile and execute that program. Today we will see a little more in depth how to compile C language programs.
Compilation process
First of all, let's see a little about the process by which a program becomes a C executable.

It all starts with editing the program using an editor in which we save the file with the extension .c
. This file is the entry point for the compiler which converts it into an object file with extension .o
or .obj
in some compilers. Finally, the linker or linker editor transforms it into an executable file for the machine. Generally, if the extension is not specified, it will be an .out
file.
Additionally, this file can be taken by the debugger to debug when we obtain unexpected results or when we want to verify that everything is running well by running the program step by step.
The compilation process is usually a bit more complex than the one shown above. When the compiler is invoked, the preprocessor is responsible (before compiling) for processing the compiler directives. That is, all lines that start with the #
character. Once this is done, the file is passed to the compiler to perform the compilation process.

Compiler installation
Most Linux systems have C and C++ compilers. On GNU/Linux Debian-based systems, simply run the following command to install the GCC compiler.
sudo apt-get install gcc
If you are using Ubuntu you can follow this tutorial that gives you everything you need to get started with C.
- Getting started with C in Ubuntu 22.04 (Jammy Jellyfish)
Simple compilation
Remember that to compile a program, it is enough to execute the compilation command, in our case gcc, and specify the name of the file.
gcc program.c
This will create by default the output file called a.out. To specify the output file name, we can use the -o option.
gcc program.c -o program
Where program.c is the C code file and program is the compiled file that will be generated. Even when our program has references to other header files, we only need to compile our main file. Example:
main.c
#include <stdio.h>
#include "variables.c"
int main()
{
printf("PI is %.4f\n", pi);
return 0;
}
variables.c
float pi = 3.1516;
This program includes the file variables.c. What the preprocessor does in these cases is to include the content of the file in our main file to process it as if it were one. Understanding this is very important because if we have really large programs it can be a headache to compile the entire program this way.
Compilation of multiple files
Compiling multiple files occurs when we add header files that generally come from external libraries. Let's see the following example:
main.c
#include <stdio.h>
#include "mylib.h"
int main()
{
printf("PI is %.4f\n", pi);
return 0;
}
mylib.h
extern float pi;
mylib.c
float pi = 3.1516;
To compile this program we must run the following command referencing our two files with extension .c.
gcc main.c mylib.c
Note that extern has been used to reference variables created in another file. This modifier has the advantage of not creating a new space in memory and allowing us to modularize our program. This same example could be compiled per steps, something that would be very useful in large applications where if we make a change in a file we would have to compile the entire application again.
gcc -c mylib.c
gcc -c main.c
gcc main.o mylib.o
When executing gcc -c file.c an object file is generated. This is so because the -c parameter prevents the compiler from linking the files, so it generates the object files for us to do the linking ourselves.
Files with the extension .o
are object files that the compiler has processed and are passed to the linker to produce an executable program.
If we had a very large number of dependencies and made a change in dependency X, the next step to compile would be the same.
gcc -c X.c
gcc -c main.c
gcc main.o X.o A.o B.o C.o
In where A.o, B.o, C.o are other generated dependencies.
Execution
To run the program, just run it as follows:
./output