Home Resource Centre Compilation In C | Detail Explanation Using Diagrams & Examples

Compilation In C | Detail Explanation Using Diagrams & Examples

The programming language C is renowned for its efficiency, portability, and widespread usage in developing various software applications, from operating systems to embedded systems and everything in between. One of the key processes that make C such a versatile and powerful language is its compilation process. In this article, we'll delve into the process of compilation in C, exploring what it is, what it entails, why it's essential for writing C programs, and how it works, etc. So let's get started!

What Is Compilation In C?

Compilation refers to the process by which human-readable source code written in any programming language is transformed into machine-executable binary code. This transformation bridges the gap between the way we write code, which is in a form that's easy for humans to understand, and the way computers execute code, which is in binary form, composed of 0s and 1s.

In essence, compilation in C language is a means by which we take our high-level code, written in the C programming language, and translate it into low-level machine-readable code that a computer's central processing unit (CPU) can directly execute. This process involves several phases that ensure the correctness, efficiency, and portability of the code.

The Compilation Process

The compilation process in C can be divided into several phases, each with specific tasks and objectives. These phases ensure that the source code is translated into an efficient and executable form while also checking for errors and allowing for modularity. The phases of the process are-

  1. Preprocessing
  2. Compilation
  3. Assembling
  4. Linking

Why Do We Need A Compilation Process In C?

Compilation in C is necessary for several critical reasons, including the following-

  • Translation to Machine Code: Computers operate using binary machine language code, and source code written in C is in a human-readable format. Compilation is the process that translates C source code into machine code for execution.
  • Error Checking: Compilation checks for syntax errors, type mismatches, and other issues, which can save developers significant time in debugging and result in more robust programs.
  • Optimization: Compilers apply various optimization techniques to the generated code to improve program efficiency and performance.
  • Modularity: Compilation in C allows for the modularity of large programs by dividing them into separate source files. Linking combines these files into a unified program.
  • Portability: C code is typically written to be platform-independent. Compilation ensures that the code can run on different platforms without modification.
  • Efficient Execution: Compiled code executes faster than interpreted code because it is already in machine code form, requiring no parsing or interpretation during execution.
  • Independence from Source Code: Once compiled, a C program can be executed without the original source code, protecting intellectual property.
  • Safety and Security: Compilation in C can help identify security vulnerabilities at an early stage, thus reducing the risk of security breaches.

How To Compile And Execute C Program?

The first thing you need to compile and run a C program is the C compiler. The most common C compiler is GCC (GNU Compiler Collection). Given below are the steps you must follow to compile and run your own C program-

Step 0: Download the GCC C compiler from the official website.

The first step is to get the C compiler from the official website and install it to run when needed.

Step 1: Creating a C source file

The next step is to open any text editor, copy and paste the C program code (say, for example, the Hello World program), and save it with any name of your choice (for example, assume that you have saved the program with the name HelloWorld.c). Note that the extension in the name of the program must be (.c).

Step 2: Compiling using GCC compiler

Go to the directory where you saved the program file and open Command Prompt or Terminal. To compile the code, enter the command gcc .\helloWorld.c (here, HelloWorld.c is the name of your program). Assuming the code is fault-free, it will compile and produce a file with the standard name - a.exe.

Step 3: Executing the program

The final step is to run the program. For this, you must enter .\a.exe to start the program, and voila, your screen will display 'Hello World'.

Compilation In C: Step-by-Step Explanation

The source code is used as the input by the compiler, which produces object code as the output. The entire C language compilation process consists of four steps, as mentioned above, i.e., pre-processing, compilation, assembly, and linking. We will discuss what goes on in each step in detail ahead. 

Step 1: Preprocessor

The file extension (.c) is used to identify source code files, which are text files with code written in them. This source code is first given to the preprocessor, who expands it from there. After the code is extended, it is handed to the compiler for further processing. This phase includes the following-

Comments Removal:

The comments are the portion of code that the pre-processor removes during compilation because they are not very useful to the machine. They are used in C programs to provide a general understanding of a particular statement or section of code. When the pre-processing stage is over, the comments in the program below will be deleted.

Macros Expansion:

A macro in C programming is a section of code that has a given name and can be used repeatedly throughout a program. The #define preprocessor directive links a name to a value or a section of code and is used to define macros.

Before the program is compiled, the preprocessor substitutes the macro name with the appropriate value or code when it is used in the program. Note that using macros can make code simpler, easier to read, and require less typing.

File Inclusion:

When pre-processing a C program, file inclusion refers to adding a different file containing some pre-written code. The #include directive is utilized here.

  • File inclusion during pre-processing allows the #include<filename> directive to be replaced with the whole contents of the filename. Thus, the filename's content is added to the source code, producing a new intermediate file.
  • Example- We can integrate the <stdio.h> header file by inserting the #include directive at the beginning of our code. Contained within this header file are standard input/output functions, among which include the highly utilized scanf() and printf().

NOTE: The source program/ code file, which originally had a (.c) extension, is given a (.i) extension during this phase.

Step 2: Compilation

The Compilation phase in C involves translating the preprocessed source code into assembly or machine code and generating object files. Here's a breakdown of the actions that occur during this phase:

Translation:

  • The compiler translates the preprocessed C source code into an intermediate representation, typically assembly language or an intermediate machine-independent code.
  • The translation process involves analyzing the code for syntax errors and generating corresponding machine-level instructions.

Code Optimization:

  • The compiler may perform various optimizations to improve the efficiency of the generated code.
  • Optimization techniques include constant folding, loop unrolling, and inlining functions to reduce execution time or memory usage.

Generation of Object Files:

  • The result of the compilation phase is one or more object files (with a .o extension on Unix-like systems or .obj on Windows).
  • Object files contain machine code specific to the target architecture and may also include additional information, such as symbols and relocation entries.

Step 3: Assembler

The compiler sends the assembly code (i.e., a simple English language used to write low-level instructions) to the assembler, which then turns it into object code. In other words, the assembler helps in converting the assembly file into an object file containing machine-level code. In this step, the file's extension changes to (.obj). 

Step 4: Linking

A linker is a tool used to connect every component of a program in the correct execution order. After the code has gone through this phase/ stage of compilation in C, it is in the executable machine code format. Note that if a program's code proves to be excessively lengthy, it may be necessary to break its contents into two distinct documents. These divided segments can then undergo separate construction processes before ultimately converging via an intermediary module known as the Linker.

The linker is a crucial component of the C language compilation process. Suppose your C program features a header file and integrates any function specified within this essential script. In that case, the linker shall proceed to bundle together both your object code and every pertinent library-based counterpart.

Example To Show Compilation In C

Let us write a simple Hello World program and discuss the compilation stages in C.

Code:

Output:

Hello, World!

Explanation:

The execution of the C program above is explained below. Note that we save the above program as 'hello.c'.

Preprocessing: The preprocessing stage of the compilation procedure involves dealing with preprocessor directives like #include and #define. In our illustration, the preprocessor is instructed to include the standard input/output library via the #include <stdio.h> command. The preprocessor will exchange the contents of the header file <stdio.h> with what is written on this line.

Compilation: Preprocessing is followed by compilation in C, which converts the code into assembly language. The particular processing structure of the machine is characterized by a distinctive form of coding that operates at a relatively basic level. This special language, which will be employed to execute programming instructions, maintains exclusivity to this architecture alone. The compiler will produce object code or machine-level code in a later-linkable format.

Assembler: The assembler transforms the assembly language code produced by the compiler into machine-level language code that the computer's processor can use at this stage. Additionally, the assembler produces object files containing the machine-level instructions. Note that an assembler contains the pre-written code, which converts assembly code to machine code.

Linking: This phase involves the linker resolving the addresses of external symbols using the relocatable object file. The linker takes over the task of generating an executable file that is capable of running on a computer. This particular type of digital entity, when constructed by the linking process, holds all machine instructions necessary for successful execution within its codebase.

We use the following command in the terminal to compile the C code mentioned above-

gcc hello.c -o hello

Within the context of compiling C source code, we identify 'hello.c' as a distinctive name for the aforementioned file. Similarly, the GCC differentiates itself from other compilers by holding this identity and (-o), allowing us to specify an output file with the name- hello. As expected, upon completion of compilation, our newly created executable will also called 'Hello'.

You can execute the program after compilation is finished by entering the following command:

./hello

This will execute the program and output the message- 'Hello, World!' to the console.

Check this out- Boosting Career Opportunities For Engineers Through E-School Competitions

What Is A Compiler?

Specialized software instruments, known as compilers, have been engineered to transform the origin files of code (input code) of a single programming language into machine-coded or bytecode constructs, alternatively converting it into another comprehensive programming syntax. Here is how it goes-

  • The programmer creates the source code in an integrated development environment (IDE) or a code editor, which is subsequently saved to one or more text files.
  • The files are read, the code is examined, and it is then translated into a format appropriate for the target platform by a compiler that supports the source programming language.

Generalized coding is translated into binary machine code by compilers, with a focus on particular computer structures and operating systems. Object code is another name for this output style. The output machine code, which may be read and executed by the target computers' processors, is solely composed of binary bits, or 1s and 0s.

How Do C Compilers Work?

The C compilers are software applications that can translate source code written in the C programming language into executable instructions that a computer's processor can execute. The phases/ components of a C compiler are as follows:

  1. Lexical analysis: In this phase, the compiler identifies tokens like keywords, identifiers, literals, and operators as it reads the source code character by character.
  2. Syntax analysis: The compiler scrutinizes each sequence to validate if it adheres to proper syntactic structure as governed by the C programming language regulations to ensure that words and symbols are arranged correctly. Another name for this process/ phase is Parsing.
  3. Semantic analysis: The compiler conducts a series of checks to ensure that the code is semantically correct. These include confirming variable declarations before their usage and scrutinizing them for inconsistencies.
  4. Intermediate representation (IR) Code generation: The compiler creates an intermediate representation (IR) of the source code after the code has gone through all three analysis processes above. It is simpler to convert the source code into a new format thanks to the IR code. However, it must reproduce the original code completely and precisely, leaving out no functionality.
  5. Optimization: In this phase, the compiler uses two optimization strategies, i.e., reducing redundant operations and rearranging instructions to increase the performance of the generated code.
  6. Output code: Lastly, the compiler produces the final output code in the target language, which is frequently machine code or assembly code, after applying various optimization techniques to the intermediate representation (IR) code. The executable instructions in the output code can be carried out instantly by the target processor or operating system.

Conclusion

The process of compilation in C is a fundamental aspect of programming that allows developers to write efficient, portable, and reliable software. It is a bridge between high-level code and machine-executable binary code, ensuring that the software we develop can run on various platforms while maintaining correctness and efficiency. Understanding the compilation process is essential for any programmer seeking to harness the full power and potential of the C programming language.

Also read- 100+ Top C Interview Questions With Answers (2023)

Frequently Asked Questions

Q. What is the compilation process in C?

The compilation in C refers to a series of steps that transform human-readable C source code into machine-executable binary code. It typically consists of the following key phases, i.e., preprocessing, compilation, and, optionally, linking.

  • Preprocessing: Expand macros, include header files, and handle conditional compilation.
  • Compilation: Translate source code to assembly language or machine code, producing object files.
  • Assembly: Convert object files to machine code specific to the target architecture.
  • Linking: Combine object files and libraries, resolve references, and create the final executable.

The compilation process ensures code correctness, optimization, and portability, making the resulting program suitable for execution on the target system.

Q. Why is the compilation process in C necessary?

The compilation process in C is necessary for several important reasons. These include-

  • Translation to Machine Code: The compilation process translates the human-readable C source code into machine code that the computer's CPU can directly execute. This translation is essential for the computer to understand and run the program.
  • Error Checking: The compilation process performs extensive error checking to ensure that the source code is free of syntax errors, type mismatches, and other issues that could lead to runtime errors or unexpected program behavior. 
  • Optimization: Compilers often apply various optimization techniques to the generated machine code to improve the program's efficiency and performance. 
  • Modularity: In software development, large programs are often divided into multiple source files to improve code organization and maintainability. The compilation process allows developers to work on separate source files independently. The linker then combines these object files, creating a single executable program. This modularity simplifies code management and collaboration.
  • Portability: C code is typically written in a platform-independent manner. The compilation process ensures that the source code, once compiled, can run on different platforms without modification. This portability is achieved by generating platform-specific machine code during compilation.
  • Protection of Intellectual Property: Compiling the source code into machine code makes it more challenging for others to reverse-engineer and understand the original program's logic. This helps protect intellectual property and proprietary software.
  • Efficient Execution: Compiled code generally executes faster than interpreted code because it has already been translated into machine code. There is no need for an interpreter to parse and execute the source code line by line, as is the case with interpreted languages.
  • Independence from Source Code: Once a C program is compiled, it can be executed without the presence of the original source code. This allows for the distribution of closed-source or commercial software without exposing the underlying code to users.
  • Safety and Security: By checking for errors during compilation, the compiler helps improve the safety and security of software. Many security vulnerabilities and potential exploits can be detected at the compilation stage, reducing the risk of security breaches.

Q. What is linking in C programming?

Linking in C programming is the final phase in the compilation process, where the linker combines multiple object files and resolves external references to create a single executable program. The main purpose of linking is to take the object code generated from different source code files and libraries and merge them into a complete and executable program.

The linking process can be further categorized into two main types:

  • Static Linking: In static linking, the linker combines object files and libraries into a single executable file. The resulting program contains all the required code and data, making it independent of external libraries. This means that the entire program, including library code, is bundled into the executable file. The advantage is that the program is self-contained, but it may result in larger executable files.
  • Dynamic Linking: In dynamic linking, the linker includes only references to external libraries in the executable, not the actual library code. The operating system's dynamic linker/loader loads the necessary library code into memory at runtime. This approach allows for smaller executable files and makes it easy to share library code among multiple programs. Some common dynamic library file formats include DLL (Dynamic Link Library) on Windows and shared objects (.so) on Unix-like systems.

Q. What are some major C compilers? 

There are several C compilers available, each with its own features, optimizations, and target platforms. Some of the major C compilers commonly used include:

  • GNU Compiler Collection (GCC): GCC is one of the most widely used and highly respected C compilers. It is open-source and supports a wide range of target architectures and platforms. GCC includes compilers for C, C++, and other high-level programming languages. It is available on various Unix-like operating systems and can be used on Windows through MinGW or Cygwin.
  • Clang: Clang is another open-source C compiler that is part of the LLVM project. It is known for its fast compilation speed and modern features. Clang is often used as the default compiler on many Unix-like operating systems, and it provides compatibility with GCC.
  • Microsoft Visual C++ (MSVC): MSVC is Microsoft's C and C++ compiler for Windows. It is widely used for Windows application development and integrates with Microsoft Visual Studio, a popular integrated development environment (IDE).
  • Intel C/C++ Compiler (ICC): ICC is a highly optimized C and C++ compiler developed by Intel. It is known for its excellent performance on Intel processors and is often used for scientific and high-performance computing applications.
  • IBM XL C/C++ Compiler: IBM's XL C/C++ compiler is used for developing applications on IBM platforms, including AIX, IBM i, and Linux on IBM Power Systems.
  • ARM Compiler: The ARM compiler is used for developing software on ARM architecture-based systems, such as embedded systems, mobile devices, and IoT devices.
  • TCC (TinyCC): TCC is a lightweight and fast C compiler designed for quick compilation. It is often used in scenarios where rapid compilation is essential, such as in certain scripting languages or embedded systems.
  • Pelles C: Pelles C is a Windows compiler known for its ease of use and comprehensive development environment. It is suitable for Windows desktop application development.
  • Solaris Studio (formerly Sun Studio): Solaris Studio is used for software development on the Solaris operating system. It provides a range of compilers, including C and C++ compilers.

Q. What is the difference between a compiler and an interpreter? 

Aspect Compiler Interpreter
Translation Translates the entire source code into machine code or an intermediate form before execution. Translates source code line-by-line or statement-by-statement during execution.
Execution Executes the program after the entire source code is translated. Executes the program directly from the source code, line by line.
Output Generates machine code or an executable file as output. Does not generate a separate executable; it executes code directly.
Error handling Typically detects and reports all errors in the source code after the compilation phase. Stops at the first encountered error, and execution is affected only up to that point.
Performance Generally, it results in faster execution because of the pre-compiled code. It can be slower as it involves interpretation during execution, with potential optimization.
Portability Requires recompilation for each target platform or architecture. It is often more portable, as it can adapt to different platforms without recompilation.
Development and Debugging Longer edit-compile-run-debug cycle. Debugging can be challenging. Shorter edit-run-debug cycle. Easier to pinpoint errors during execution.
Examples C, C++, Java, and many compiled languages. Python, JavaScript, and many scripting languages.

 

Q. What is a cross-compiler?

A cross-compiler is a type of compiler that generates executable code or binary files for a platform or target architecture that is different from the one on which the compiler itself is running. In other words, it is a compiler that produces code for a cross-target, as opposed to a native compiler that generates code for the same platform it is running on.

Cross-compilers are commonly used in software development for embedded systems, micro-controller programs, and other specialized hardware platforms where the development environment on the target platform may be limited or nonexistent. Some common use cases for cross-compilers include-

  • Embedded Systems: In embedded systems development, the target hardware may have a different architecture or may not have the necessary resources to host a compiler. A cross-compiler allows developers to write code on a more powerful host system and generate binaries that can run on the embedded target.
  • Cross-Platform Development: A cross-compiler can be used to build platform-specific executables when developing software that needs to run on multiple platforms with different architectures.
  • Performance Optimization: Cross-compilers can be used to optimize code for a specific target architecture, taking advantage of hardware features and assembly instruction sets not available on the host system.
  • Porting Software: Cross-compilers can help in porting existing software to new target platforms without requiring significant changes to the source code.

We are sure that by now, you must know all about compilation in C and what it entails. Here are some other interesting topics that you must read:

  1. Tokens In C | A Complete Guide To 7 Token Types (With Examples)
  2. Swapping Of Two Numbers In C | 5 Ways Explained (With Examples)
  3. Format Specifiers In C | A Complete Guide With Detailed Examples
  4. Advantages And Disadvantages of C Programming Language
  5. Difference Between C And Embedded C Decoded!
Shivani Goyal
Manager, Content

An economics graduate with a passion for storytelling, I thrive on crafting content that blends creativity with technical insight. At Unstop, I create in-depth, SEO-driven content that simplifies complex tech topics and covers a wide array of subjects, all designed to inform, engage, and inspire our readers. My goal is to empower others to truly #BeUnstoppable through content that resonates. When I’m not writing, you’ll find me immersed in art, food, or lost in a good book—constantly drawing inspiration from the world around me.

TAGS
Engineering Computer Science
Updated On: 13 Mar'24, 04:49 PM IST