The process of compiling C code transforms the provided source code into either object code or machine code. This compilation process consists of four main stages: Pre-processing, Compilation, Assembly, and Linking.
The preprocessor accepts the source code as an input and eliminates any comments present within the code. It processes the preprocessor directive and understands its meaning. For instance, when <stdio.h> directive is encountered in the program, the preprocessor interprets it and substitutes this directive with the contents of the 'stdio.h' file.
The following are the phases through which our program passes before being transformed into an executable form:
- Preprocessor
- Compiler
- Assembler
- Linker
Preprocessor
The code written in a text editor is known as the source code, with the file typically having a ".c" extension. This source code undergoes preprocessing before being expanded by the preprocessor. Following expansion, the code is then forwarded to the compiler for further processing.
Compiler
The code expanded by the preprocessor is then forwarded to the compiler for further processing. It is during this stage that the compiler translates the expanded code into assembly code. Another way to put it is that the C compiler transforms the pre-processed code into assembly code.
Assembler
The assembly code gets transformed into object code through the utilization of an assembler. The object file produced by the assembler retains the identical name as the source file. In DOS, the object file extension is '.obj,' while in UNIX, it is 'o'. For instance, if the source file is named 'hello.c,' the corresponding object file would be 'hello.obj'.
Linker
Primarily, most C programs rely on library functions, which are essentially pre-compiled with the object code stored in files with a '.lib' (or '.a') extension. The primary function of the linker is to merge this object code from library files with the object code of our program. In cases where our program makes reference to functions defined in other files, the linker's role becomes crucial. It integrates the object code from these external files into our program. In essence, the linker's task is to connect the object code of our program with that of the library files and other external files. The end result produced by the linker is the executable file, which shares the same name as the source file but with differing extensions. In the DOS environment, the executable file carries the '.exe' extension, while in UNIX, it may be named as 'a.out'. For instance, when utilizing the printf function in a program, the linker includes its corresponding code in the output file.
Let's understand through an example.
hello.c
#include <stdio.h>
int main()
{
printf("Hello javaLogic Practice");
return 0;
}
Next, we are going to generate a flowchart for the program described earlier:
In the above flow diagram, the following steps are taken to execute a program:
- Firstly, the input file, i.e., hello.c, is passed to the preprocessor, and the preprocessor converts the source code into expanded source code. The extension of the expanded source code would be hello.i.
- The expanded source code is passed to the compiler, and the compiler converts this expanded source code into assembly code. The extension of the assembly code would be hello.s.
- This assembly code is then sent to the assembler, which converts the assembly code into object code.
- After the creation of an object code, the linker creates the executable file. The loader will then load the executable file for the execution.