The C Preprocessor, often known as CPP is a macro processor that is used by the C compiler to transform your program before compilation. It is called a macro processor because it allows you to define macros.
Preprocessor directives begin with a # symbol. Preprocessor directives are used to define and replace tokens in the text and also used to insert the contents of other files into the source file. In C program, collection of all keywords, identifiers, operators, special symbols, constants, strings and data values are called as tokens.
#define Directive
This directive defines macros. There are two types of macros: those with parameters and those without parameters.
Example of macro without parameters
// Program to display the area of circle
#include <stdio.h>
#define PI 3.1415
int main() {
float radius, area;
printf("Enter the radius of circle: ");
scanf("%d", &radius);
area = PI*radius*radius;
printf("Area of circle = %.2f\n", area);
return 0;
}
In pre-processing stage, PI
will be replaced by 3.14
, so PI*radius*radius
will be 3.14*radius*radius
.
Example of macro with parameters
// Program to display the area of circle
#include <stdio.h>
#define PI 3.1415
#define circleArea(r) (PI*r*r)
int main() {
int radius;
float area;
printf("Enter the radius of circle: ");
scanf("%d", &radius);
area = circleArea(radius);
printf("Area of circle = %.2f\n", area);
return 0;
}
In pre-processing stage, circleArea(radius)
will be replaced by 3.14*r*r
.
Macros are often used to execute a sequence of multiple statements as a group. When multiple statements are used in a macro, they should be bound in a do-while
loop syntactically, so the macro can appear safely inside a statement block that doesn’t use braces (when a statement block uses braces, then multiple statements in a macro will expand correctly event without a do-while
loop).
In this noncompliant code example, the macro contains multiple unbound statements
#include <stdio.h>
#define SWAP(x, y) \
tmp = x; \
x = y; \
y = tmp
int main() {
int x, y, z, tmp;
if (z == 0)
SWAP(x, y);
return 0;
}
It will expand to the following, which is certainly not what the programmer intended
#include <stdio.h>
#define SWAP(x, y) \
tmp = x; \
x = y; \
y = tmp
int main() {
int x, y, z, tmp;
if (z == 0)
tmp = x;
x = y;
y = tmp;
return 0;
}
Next, in this noncompliant code example, the macro contains multiple statements bounded with curly brackets
#include <stdio.h>
#define SWAP(x, y) { tmp = (x); (x) = (y); (y) = tmp; }
int main() {
int x, y, z, tmp;
if (x > y)
SWAP(x, y); /* Branch 1 */
else
do_something(); /* Branch 2 */
return 0;
}
the macro will fail to expand and will be interpreted as an if statement with only one branch
#include <stdio.h>
#define SWAP(x, y) { tmp = (x); (x) = (y); (y) = tmp; }
int main() {
int x, y, z, tmp;
if (x > y) { /* Single-branch if-statement!!! */
tmp = x; /* The one and only branch consists */
x = y; /* of the block. */
y = tmp;
}
; /* Empty statement */
else /* ERROR!!! "parse error before else" */
do_something();
return 0;
}
So what is the compliant solution for this case? That is wrapping the macro inside a do-while
loop.
#include <stdio.h>
#define SWAP(x, y) \
do { \
tmp = (x); \
(x) = (y); \
(y) = tmp; } \
while (0)
int main() {
int x, y, z, tmp;
if (x > y)
SWAP(x, y);
else
do_something();
return 0;
}
The do-while
will always be executed exactly once.
#include Directive
The #include directive is used to include header files to a C program. #include directive has two variants:
#include <file>
and #include "file"
.
#include <file>
searches for a file named file in a defined places by compiler. GCC compiler looks in several different places for headers. You can see where it looks for header files by using this command
cpp -v
#include "file"
searches for a file named file first in the directory containing the current file, if it’s still not found then it continues in the defined places by compiler.
If you want to add additional directories to the search path, you can specify multiple -Idir
options.
If a header file happens to be included twice, the compiler will process its content twice. This is very likely to cause error. The standard wy to prevent this is to enclose the entire real contents of the file in a conditional, like this
/* File foo. */
#ifndef FILE_FOO_SEEN
#define FILE_FOO_SEEN
the entire file
#endif /* !FILE_FOO_SEEN */
#ifdef Directive
The #ifdef directive checks for the existence of macro defenitions.
The following example defines MAX_LEN
to be 75 if EXTENDED
is defined for the preprocessor. Otherwise, MAX_LEN
is defined to be 50.
#ifdef EXTENDED
# define MAX_LEN 75
#else
# define MAX_LEN 50
#endif
#ifndef Directive
The #ifndef directive checks whether a macro is not defined.
The following example defines MAX_LEN
to be 50 if EXTENDED
is not defined for the preprocessor. Otherwise, MAX_LEN
is defined to be 75.
#ifndef EXTENDED
# define MAX_LEN 50
#else
# define MAX_LEN 75
#endif
#if Directive
The #if directive allows you to test the value of an arithmetic expression, rather than the mere existence of one macro.
The expression of #if directive may contain
- integer constants
- character constants
- arithmetic operators for addition, subtraction, multiplication, division, bitwise operations, shifts, comparisons, and logical operations
- Uses of the
defined
operator, which lets you check whether macros are defined - Identifiers that are not macros is considered false (zero), function-like macros used without their function call parentheses are also treated as false (zero)
#if DLEVEL > 5
#define SIGNAL 1
#if STACKUSE == 1
#define STACK 200
#else
#define STACK 100
#endif
#else
#define SIGNAL 0
#if STACKUSE == 1
#define STACK 100
#else
#define STACK 50
#endif
#endif
#if DLEVEL == 0
#define STACK 0
#elif DLEVEL == 1
#define STACK 100
#elif DLEVEL > 5
display( debugptr );
#else
#define STACK 200
#endif
#undef Directive
If a macro ceases to be useful, it may be undefined with #undef directive.
#undef MAX_LEN
#line Directive
The #line directive supplies line numbers for compiler messages. It causes the compiler to view the line number of the next source line as the specified number.
You can use #line directives to make the compiler provide more meaningful error messages.
#include <stdio.h>
#define LINE200 200
int main(void)
{
func_1();
func_2();
}
#line 100
func_1()
{
printf("Func_1 - the current line number is %d\n",__LINE__);
}
#line LINE200
func_2()
{
printf("Func_2 - the current line number is %d\n",__LINE__);
}
#error Directive
The #error directive causes the preprocessor to generate an error message and causes the compilation to fail.
#define BUFFER_SIZE 255
#if BUFFER_SIZE < 256
#error "BUFFER_SIZE is too small."
#endif
#warning Directive
The #warning directive is like #error, but causes the preprocessor to issue a warning and continue preprocessing.
# Operator
The # operator converts a parameter of a function-like macro into a character string literal. For example, if macro ABC
is defined using the following directive:
#define ABC(x) #x
all subsequent invocations of the macro ABC
would be expanded into a character string literal containing the argument passed to ABC
. ABC(100)
would be expanded into a character string "100"
and ABC(Hello World)
would be expanded into "Hello World"
.
The # operator rules in a function-like macro
- white-space characters that appear after or before the argument passed to the macro are deleted
- multiple white-space characters embedded within the argument passed to the macro are replaced by a single space character
- if the argument passed to the macro contains a string literal and if a backslash \ character appears within the literal, a second \ character is inserted before the original \ when the macro is expanded
- if the argument passed to the macro contains a " (double quotation mark) character, a \ is inserted before the " when the macro is expanded
- the conversion of an argument into a string literal occurs before macro expansion on that argument
- if more than one # operator appears in the replacement list of a macro definition, the order of evaluation of the operators is not defined
- if the result of the macro expansion is not a valid character string literal, the behaviour is undefined
#define STR(x) #x
#define XSTR(x) STR(x)
#define ONE 1
Invocation | Result of Macro Expansion |
---|---|
STR(\n "\n" '\n') |
"\n \"\\n\" '\\n'" |
STR(ONE) |
"ONE" |
XSTR(ONE) |
"1" |
XSTR("hello") |
"\"hello\"" |
null directive (#)
The null directive performs no action. It consists of a single # on a line of its own.
In the following example, if MINVAL
is a defined macro name, no action is performed. If MINVAL
is not defined, it is defined 1.
#ifdef MINVAL
#
#else
#define MINVAL 1
#endif
## Operator
The ## (double number sign) operator concatenates two tokens in a macro invocation (text and/or arguments) given in a macro definition.
The ## (double number sign) operator rules
- concatenation take place before any macros in arguments are expanded
- if more than one ## operator appears in the replacement list of a macro definition, the order of evaluation of the operators is not defined
#define ArgArg(x, y) x##y
#define ArgText(x) x##TEXT
#define TextArg(x) TEXT##x
#define TextText TEXT##text
#define Jitter 1
#define bug 2
#define Jitterbug 3
Invocation | Result of Macro Expansion |
---|---|
ArgArg(lady, bug) |
ladybug |
ArgText(con) |
conTEXT |
TextArg(book) |
TEXTbook |
TextText |
TEXTtext |
ArgArg(Jitter, bug) |
3 |
Predefined Macros
There are some predefined macros in C programming.
Predefined Macro | Description |
---|---|
__DATE__ |
String containing the current date |
__FILE__ |
String containing the file name |
__LINE__ |
Integer representing the current line number |
__TIME__ |
String containing the current time |
__STDC__ |
The integer 1 indicates that the C compiler supports the ISO C standard |
#include <stdio.h>
int main()
{
printf("Current time: %s\n",__TIME__);
printf("Current time: %s\n",__DATE__);
printf("Current time: %s\n",__FILE__);
printf("Current time: %d\n",__LINE__);
printf("Current time: %d\n",__STDC__);
return 0;
}