Preprocessing

In this tutorial, our focus will be to understand the job of preprocessor and its contribution before the compilation begins. So, let’s get into it.


As the name suggests, preprocessor processes some part of the program prior to compilation process (pre means prior, and processor is something that does some processing). The job of preprocessor is to take the source code (with .c extension) and convert it to expanded source code (with .i extension). You will soon get to know what’s inside the expanded source code, but for now think of a preprocessor as the text editor program (like Microsoft Word) which edits some part of the source code.

When preprocessor receives the source code, it

  1. Remove comments from the source code
  2. Expand header files
  3. Expand macros

So, in the expanded source code, the header files are replaced with their actual content, macros are resolved and comments are removed.


What are comments and why preprocessor removes them?

Comments are added by the programmers to document their code for future reference for themselves or others.

For example, the following program has two comments (starting with //):

int main() {
     // starting point of the program
     int main() {
         // prints Hello World!
         printf(“Hello World!”);
         return 0;
     }
 }

Preprocessor removes comments from the source code because they are not contributing anything in the implementation of the program. Removing them will not affect the execution. They are just meant for programmers to document the code. A compiler can still compile the code without comments.

You might have heard that compilers ignore comments, but that’s a surface level understanding. In reality, compiler never sees comments as preprocessor removes them in the first place.


What’s the meaning of Preprocessor Expanding Header Files?

In the previous tutorial, I mentioned that we need to include stdio.h header file in our program to use the printf() function. It is the job of preprocessor to include the contents of the header file which eventually allows us to use the printf() function. This is the literal meaning of “preprocessor expanding header files”.

The #include statement is a preprocessor directive, which means that the statement following it is meant to be processed by the preprocessor. So, when we encounter a statement starting with #include, we know that it is meant to be processed by preprocessor.

But what is there inside stdio.h?

The stdio.h library contains declarations for many standard input and output functions, as well as all the necessary information for including them in your code. You may have heard the terms declaration and definition before, which are common in the world of programming languages.

Declaring a function means informing the compiler that the programmer has written the logic of the function somewhere, so have some faith and compile the code. It is like giving a guarantee to the compiler that the function already exists. On the other hand, defining a function means defining the actual logic of the function.

Built-in functions are the functions which are already defined by someone, so we just need to declare them and use them. stdio.h and other header files contain function declarations of the pre-defined functions. By including the header files in our code, we can use pre-defined functions whose declarations are available in these header files. Particularly, stdio.h contains declarations of the standard input/output functions which we can use in our programs for input and output operations.

What’s the problem in including the definitions in the header files?

Firstly, there is no need to compile the built-in functions, as they are pre-compiled and ready to use. Secondly, the standard input and output functions have hundreds of lines of code, including all of this code in the stdio.h header does not make sense because not all the functions maybe used by the program. Therefore, it is better to add just declarations of the functions in the header file. By doing this, we tell the compiler that we may use some or all of these functions later in our code, and to skip the definitions for now. The definitions of the specific functions used in the program will be provided later. This allows us to pass the compilation phase without any errors.


What is the meaning of “preprocessor expands macros”?

First, let’s understand the meaning of macro.

A macro is used to give a name to a constant, expression, or a piece of code so that it can be used in the program. It is defined using the #define directive, and preprocessor is responsible to replace the name with its actual definition.

The following example demonstrates use of macros.

#include <stdio.h> 
#define N 10
int main()
{
   printf("The value of N is %d", N);
   return 0;
}

Output:

The value of N is 10
Process returned 0 (0x0)   execution time : 1.883 s
Press any key to continue.

In the above program, N represents the name of the macro, and 10 is its value. The printf() function has two inputs. First is the string with %d which acts as the placeholder for value of N. Second, is N itself which is replaced by its value by the preprocessor before compilation. This is one of the reasons why the preprocessor is often referred to as the “editor.” The output of the preceding program is “The value of N is 10.”

Therefore, we can say that the job of preprocessor is to “expand macros” as it replaces the names with their actual definitions.

Note: This is the basic introduction to macros and we have covered its full potential yet. We will learn more about macros later.

The file obtained from the preprocessor has the extension .i, but with Code::Blocks we cannot see this file. We need to manually obtain it using the command prompt. Let’s learn the process of obtaining the expanded source code.


Getting the expanded source code

GCC (GNU Compiler Collection – a package containing everything needed to generate the final output) provides us with the ability to see the preprocessed expanded source, which is originally hidden from us. But before we get into the process, please write the following code in Code::Blocks (or the IDE of your choice) and save it in the C Programs folder as add.c.

#include <stdio.h>
#define N 10

// starting point of the program
int main()
{
    printf("The value of N is %d", N);
    return 0;
}

We will not build and run this code in the IDE. Instead, we will open the command prompt, navigate to the folder where the file add.c is stored (presumably the C Programs folder), and enter the following command:

gcc -E add.c -o add.i

In the above command

  1. gcc allows us to call the GNU Compiler Collection.
  2. -E is the flag that stops every other process except preprocessing.
  3. -o creates a new file and stores the result of preprocessing (expanded source code) in this file.
  4. add.i is the name of the file where the expanded source code will be stored.

After executing the above command, a new file named add.i will be created in our C programs folder. If you open this file in the IDE, you will see a lot of code added by the preprocessor. If you scroll down to the bottom, you will see the code we wrote, as follows:

int main()
{
    printf("The value of N is %d", 10);
    return 0;
}

It can be observed that all operations are performed by the preprocessor, including:

  1. Removing the comment “//starting point of the program“.
  2. Expanding the header file.
  3. Replacing the macro N with its value 10.

This verifies the preprocessing stage.



Leave a comment

Leave a comment

Your email address will not be published. Required fields are marked *

Thank you for choosing to leave a comment. Please be aware that all comments are moderated in accordance with our policy. For more information, please review our comments policy. Rest assured that your email address will not be shared with anyone. Kindly refrain from using keywords in your comment. Let’s maintain a respectful atmosphere and engage in meaningful conversations.