Primary C Concepts: Preprocessor Directives

Last Updated : 10 July, 2022 · 2 min read

articles banner image

Preprocessor directives offer a lot of advantages and flexibility to a C programmer. Before diving into specific instances of preprocessor directives, we would benefit from understanding what a preprocessor is.

A preprocessor is a program that processes our written code before compilation. During this process, preprocessor will explicitly look for preprocessor directives and follow their instructions. At the time of compilation, the preprocessor will have modified the code in the program accordingly to the found directives.

Some of the most relevant preprocessor directives are:

1. Defining symbols: #define and #unset

Defining symbols, that is #define and #undef are instructions that are dealt with by the preprocessor. A good way to identify these or any other preprocessor directives, for that matter, is the # symbol at the beginning of the instruction and a lack of ; at the end.

The preprocessor directive #define, as the name suggests, allows us to define constant values and expressions.

Example of #define in C code:

# include <stdio.h>
# define CONSTANT 1

int main ( void )
{
    
if ( CONSTANT == 1 )
        
printf ("CONSTANT is defined" ); //program prints this line
    
else
        
printf ("CONSTANT is not defined" );
    
return 0 ;
}

In the C code example above, we are defining a constant. This constant, thus cannot be modified during the runtime of the program. However, it is accessible during runtime because the preprocessor replaced the constant name CONSTANT everywhere in our program with the assigned value of 1

The #define can define expressions, too. Expressions, in turn, can take varying programs. To better understand this consider the code example below for a moment:

# include <stdio.h>
# define PRODUCT(a,b) ((a) * (b))

int main ( void )
{
    
printf ( "%d", PRODUCT(10, 5) ); //prints 50
    
return 0 ;
}

The preprocessor directive #undef has opposing functionality to the discussed #define directive. Since #undef undefines or removes constant values and expressions that were defined by the #define.

#undef PRODUCT
#undef CONSTANT

2. Content inclusion: #include

The #include directive is used in most C programs because it marks the place in the program where this directive needs to be replaced with the contents of the specified filename. Let's say our program contains this line of code #include <stdio.h>. The preprocessor will run before compilation of the program, it will notice this line of code and replace it with all the data that is inside the <stdio.h> library. This allows the program to access all the functions in the runtime that this library contains.

Filename or library name can be specified inside angle brackets ( < > ) or inside double quotes. In the former case (#include <stdio.h>), the proprocessor expects and checks for this file inside the standard library. In the latter case (#include "our_header.h"), the preprocessor will firstly check for this file at user defined locations and will only check inside the standard library whenever user defined locations do not contain the specified file.

3. Conditional compilation: #ifndef, #ifdef and #endif

Both #ifdef and #ifndef directives are typically used for different purposes, yet both need to be closed with an #endif directive. The #ifndef directive is commonly used as a header guard. You can check this in the C code example below:

# ifndef HEADER
# define HEADER
int functional_definition( void );
# endif

Here we are checking if HEADER is not defined. On such an occassion #ifndef returns true. Which makes the lines of C code wrapped inside the #ifndef and #endif directives compilable.

Notice the #define HEADER (On line 2). This previously discussed directive will define HEADER constant. The next time this header file gets called, HEADER will be defined and the #ifndef HEADER will return false. Which will prevent the same information being compiled again. Our header is now guarded, all thanks to #ifndef and #endif preprocessor directives.

With the #ifdef the whole idea is rather similar to that of #ifndef. The difference is that the former returns true if the constant is, in fact, defined and false if it is not. This is useful for multi device applications because it allows to enable certain parts of the code depending on the device that runs the program.

4. File instruction: #pragma

The #pragma preprocessor directive is useful in large programs. This directive has the effect on the compiler's behaviour by specifying instructions to certain operating system. A widely supported instruction across many systems is once.

Including #pragma once at the top of your header file provides a guard from multiple compilations. You have already seen how to achieve this result in the Conditional compilation: #ifndef, #ifdef and the #endif section above. Note that you do not need to use both directive methods for the same purpose.

5. Quiz of reader's knowledge

Quiz : Preprocessor Directives in C

1For what purpose is #pragma once used?

2Which preprocessor directive returns true when the constant is not defined?

3If the value of #include is written in double quotes, as in #define "stdio.h",

quiz completed!

Congrats!

Feel free to share your achievement below!