0% found this document useful (0 votes)
207 views12 pages

Pre Processor Directives

The preprocessor is a program that processes code before compilation by handling directives starting with #. These directives can define macros, include other files, and conditionally compile code. Common preprocessor directives include #define for defining macros, #include for file inclusion, and #if/#endif for conditional compilation. Macro definitions can be object-like, replacing an identifier, or function-like, with parameters and replacement text.

Uploaded by

indu_singh_9
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
207 views12 pages

Pre Processor Directives

The preprocessor is a program that processes code before compilation by handling directives starting with #. These directives can define macros, include other files, and conditionally compile code. Common preprocessor directives include #define for defining macros, #include for file inclusion, and #if/#endif for conditional compilation. Macro definitions can be object-like, replacing an identifier, or function-like, with parameters and replacement text.

Uploaded by

indu_singh_9
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOC, PDF, TXT or read online on Scribd
You are on page 1/ 12

Preprocessor Directives

The preprocessor is a program that is invoked by the compiler to process code before
compilation. Commands for that program, known as directives, are lines of the source
file beginning with the character #, which distinguishes them from lines of source
program text. The effect of each preprocessor directive is a change to the text of the
source code, and the result is a new source code file, which does not contain the
directives. The preprocessed source code, an intermediate file, must be a valid C or
C++ program, because it becomes the input to the compiler.

The syntax of preprocessor directives is independent of, but similar to, the syntax of
the rest of the language, and the lexical conventions of the preprocessor differ from
those of the compiler. The preprocessor recognizes the normal C and C++ tokens, as
well as other characters that enable the preprocessor to recognize file names, the
presence and absence of white space, and the location of end-of-line markers.

Preprocessor directives and the related subject of macro expansion are discussed in
this section. After an overview of preprocessor directives, the topics covered include
textual macros, file inclusion, ISO standard and predefined macro names, conditional
compilation directives, and pragmas.

Preprocessor Overview

Preprocessing is a preliminary operation on C and C++ files before they are passed to
the compiler. It allows you to do the following:

• Replace tokens in the current file with specified replacement tokens


• Imbed files within the current file
• Conditionally compile sections of the current file
• Generate diagnostic messages
• Change the line number of the next line of source and change the file name of
the current file
• Apply machine-specific rules to specified sections of code

A token is a series of characters delimited by white space. The only white space
allowed on a preprocessor directive is the space, horizontal tab, vertical tab, form
feed, and comments. The new-line character can also separate preprocessor tokens.

The preprocessed source program file must be a valid C or C++ program.

The preprocessor is controlled by the following directives:

#define
Defines a macro.
#undef
Removes a preprocessor macro definition.
#error
Defines text for a compile-time error message.
#include
Inserts text from another source file.
#if
Conditionally suppresses portions of source code, depending on the result of a
constant expression.
#ifdef
Conditionally includes source text if a macro name is defined.
#ifndef
Conditionally includes source text if a macro name is not defined.
#else
Conditionally includes source text if the previous #if, #ifdef, #ifndef, or #elif
test fails.
#elif
Conditionally includes source text if the previous #if, #ifdef, #ifndef, or #elif
test fails, depending on the value of a constant expression.
#endif
Ends conditional text.
#line
Supplies a line number for compiler messages.
#pragma
Specifies implementation-defined instructions to the compiler.

Preprocessor directives begin with the # token followed by a preprocessor keyword.


The # token must appear as the first character that is not white space on a line. The #
is not part of the directive name and can be separated from the name with white
spaces.

A preprocessor directive ends at the new-line character unless the last character of the
line is the \ (backslash) character. If the \ character appears as the last character in the
preprocessor line, the preprocessor interprets the \ and the new-line character as a
continuation marker. The preprocessor deletes the \ (and the following new-line
character) and splices the physical source lines into continuous logical lines. White
space is allowed between backslash and the end of line character or the physical end
of record. However,this white space is usually not visible during editing.

Except for some #pragma directives, preprocessor directives can appear anywhere in
a program.

Macro Definition and Expansion (#define)

A preprocessor define directive directs the preprocessor to replace all subsequent


occurrences of a macro with specified replacement tokens.

A preprocessor #define directive has the form:

>>-#--define--identifier--+--------------------------+---------->
| .-,--------------. |
| V | |
'-(----+------------+-+--)-'
'-identifier-'

.----------------.
V |
>----+------------+-+------------------------------------------><
+-identifier-+
'-character--'

The #define directive can contain an object-like definition or a function-like


definition.

#define versus const

• The #define directive can be used to create a name for a numerical, character,
or string constant, whereas a const object of any type can be declared.
• A const object is subject to the scoping rules for variables, whereas a constant
created using #define is not.
• Unlike a const object, the value of a macro does not appear in the
intermediate source code used by the compiler because they are expanded
inline. The inline expansion makes the macro value unavailable to the
debugger.
• A macro can be used in a constant expression, such as an array bound,
whereas a const object cannot.
• The compiler does not type-check a macro, including macro
arguments.

Object-Like Macros

An object-like macro definition replaces a single identifier with the specified


replacement tokens. The following object-like definition causes the preprocessor to
replace all subsequent instances of the identifier COUNT with the constant 1000 :

#define COUNT 1000

If the statement

int arry[COUNT];

appears after this definition and in the same file as the definition, the preprocessor
would change the statement to

int arry[1000];

in the output of the preprocessor.

Other definitions can make reference to the identifier COUNT:

#define MAX_COUNT COUNT + 100

The preprocessor replaces each subsequent occurrence of MAX_COUNT with


COUNT + 100, which the preprocessor then replaces with 1000 + 100.

If a number that is partially built by a macro expansion is produced, the preprocessor


does not consider the result to be a single value. For example, the following will not
result in the value 10.2 but in a syntax error.

#define a 10
a.2

Identifiers that are partially built from a macro expansion may not be produced.
Therefore, the following example contains two identifiers and results in a syntax
error:

#define d efg
abcd

Function-Like Macros

More complex than object-like macros, a function-like macro definition declares the
names of formal parameters within parentheses, separated by commas. An empty
formal parameter list is legal: such a macro can be used to simulate a function that
takes no arguments. C adds support for function-like macros with a variable number
of arguments.
C++ supports function-like macros with a variable number of arguments, as a
language extension for compatibility with C.

Function-like macro definition:


An identifier followed by a parameter list in parentheses and the replacement
tokens. The parameters are imbedded in the replacement code. White space
cannot separate the identifier (which is the name of the macro) and the left
parenthesis of the parameter list. A comma must separate each parameter.

For portability, you should not have more than 31 parameters for a macro. The
parameter list may end with an ellipsis (...). In this case, the identifier
__VA_ARGS__ may appear in the replacement list.

Function-like macro invocation:


An identifier followed by a comma-separated list of arguments in parentheses.
The number of arguments should match the number of parameters in the
macro definition, unless the parameter list in the definition ends with an
ellipsis. In this latter case, the number of arguments in the invocation should
exceed the number of parameters in the definition. The excess are called
trailing arguments. Once the preprocessor identifies a function-like macro
invocation, argument substitution takes place. A parameter in the replacement
code is replaced by the corresponding argument. If trailing arguments are
permitted by the macro definition, they are merged with the intervening
commas to replace the identifier __VA_ARGS__, as if they were a single
argument. Any macro invocations contained in the argument itself are
completely replaced before the argument replaces its corresponding parameter
in the replacement code.

A macro argument can be empty (consisting of zero preprocessing tokens).


For example,

#define SUM(a,b,c) a + b + c
SUM(1,,3) /* No error message.
1 is substituted for a, 3 is substituted for c.
*/
This language feature is an orthogonal extension of C++.

If the identifier list does not end with an ellipsis, the number of arguments in a macro
invocation must be the same as the number of parameters in the corresponding macro
definition. During parameter substitution, any arguments remaining after all specified
arguments have been substituted (including any separating commas) are combined
into one argument called the variable argument. The variable argument will replace
any occurrence of the identifier __VA_ARGS__ in the replacement list. The following
example illustrates this:

#define debug(...) fprintf(stderr, __VA_ARGS__)

debug("flag"); /* Becomes fprintf(stderr, "flag"); */

Commas in the macro invocation argument list do not act as argument separators
when they are:

• In character constants
• In string literals
• Surrounded by parentheses
The following line defines the macro SUM as having two parameters a and b and the
replacement tokens (a + b):

#define SUM(a,b) (a + b)

This definition would cause the preprocessor to change the following statements (if
the statements appear after the previous definition):

c = SUM(x,y);
c = d * SUM(x,y);

In the output of the preprocessor, these statements would appear as:

c = (x + y);
c = d * (x + y);

Use parentheses to ensure correct evaluation of replacement text. For example, the
definition:

#define SQR(c) ((c) * (c))

requires parentheses around each parameter c in the definition in order to correctly


evaluate an expression like:

y = SQR(a + b);

The preprocessor expands this statement to:

y = ((a + b) * (a + b));

Without parentheses in the definition, the correct order of evaluation is not preserved,
and the preprocessor output is:

y = (a + b * a + b);

Arguments of the # and ## operators are converted before replacement of parameters


in a function-like macro.

Once defined, a preprocessor identifier remains defined and in scope independent of


the scoping rules of the language. The scope of a macro definition begins at the
definition and does not end until a corresponding #undef directive is encountered. If
there is no corresponding #undef directive, the scope of the macro definition lasts
until the end of the translation unit.

A recursive macro is not fully expanded. For example, the definition

#define x(a,b) x(a+1,b+1) + 4

expands

x(20,10)

to

x(20+1,10+1) + 4
rather than trying to expand the macro x over and over within itself. After the macro x
is expanded, it is a call to function x().

A definition is not required to specify replacement tokens. The following definition


removes all instances of the token debug from subsequent lines in the current file:

#define debug

You can change the definition of a defined identifier or macro with a second
preprocessor #define directive only if the second preprocessor #define directive is
preceded by a preprocessor #undef directive. The #undef directive nullifies the first
definition so that the same identifier can be used in a redefinition.

Within the text of the program, the preprocessor does not scan character constants or
string constants for macro invocations.

Example of #define Directives

The following program contains two macro definitions and a macro invocation that
refers to both of the defined macros:

/**
** This example illustrates #define directives.
**/

#include <stdio.h>

#define SQR(s) ((s) * (s))


#define PRNT(a,b) \
printf("value 1 = %d\n", a); \
printf("value 2 = %d\n", b) ;

int main(void)
{
int x = 2;
int y = 3;

PRNT(SQR(x),y);

return(0);
}

After being interpreted by the preprocessor, this program is replaced by code


equivalent to the following:

#include <stdio.h>

int main(void)
{
int x = 2;
int y = 3;

printf("value 1 = %d\n", ( (x) * (x) ) );


printf("value 2 = %d\n", y);

return(0);
}

This program produces the following output:

value 1 = 4
value 2 = 3

Using Macros to Define Constants

The most common use of the macro replacement is in defining a constant. In C++
you can also declare constants using the keyword const.Rather than explicitly
putting constant values in a program, you can name the constants using macros,
then use the names in place of the constants. By changing the definition of the
macro, you can more easily change the program:

#define ARRAY_SIZE 1000


float x[ARRAY_SIZE];

In this example, the array x is dimensioned using the macro ARRAY_SIZE rather
than the constant 1000. Note that expressions that may use the array can also use
the macro instead of the actual constant:

for (i=0; i<<ARRAY_SIZE; ++i) f+=x[i];

Changing the dimension of x means only changing the macro for ARRAY_SIZE. The
dimension changes and so do all of the expressions that make use of the dimension.

Other Macros

Two other macros include:

#define FALSE 0
#define TRUE 1

The following macro is more complex. It has two parameters and produces an inline
expression which is equal to the maximum of its two parameters:

#define MAX(x,y) ((x) > (y) ? (x) : (y))


Concatenating Tokens with the ## Operator

Use the special ## operator to form other tokens by concatenating tokens used as
actual arguments. Each instance of the ## operator is deleted and the tokens
preceding and following the ## are concatenated into a single token. If either of these
names is a formal parameter of the macro, the corresponding argument at invocation
is used. This is useful in forming unique variable names within macros.

Example 1

The following illustrates the ## operator:

// define the macro; the ## operator


// concatenates arg1 with arg2
#define concat(arg1,arg2) arg1 ## arg2

main()
{
int concat(fire,fly);
concat(fire,fly) = 1;
printf("%d \n",concat(fire,fly));
}
Preprocessing the preceding program yields the following:

main()
{
int firefly ;
firefly = 1;
printf("%d \n",firefly );
}
Example 2

You can use the # and ## operators together:

#include <iostream.h>
#define show_me(arg) int var##arg=arg;\
cout << "var" << #arg << " is " << var##arg << "\n";
main()
{
show_me(1);
}

Preprocessing this example yields the following code for the main procedure:

main()
{
int var1=1; cout << "var" << "1" << " is " << var1 << "\n";
}

After compiling the code with CC and running the resulting executable file, you get the
following results:

var1 is 1

Spaces around the # and ## are optional.

NOTE: The # and ## operators are only valid when using the ANSI C mode
preprocessor, which is the default preprocessor. They are not supported
when using the compatibility mode preprocessor.

In both the # and ## operations, the arguments are substituted as is, without any
intermediate expansion. After these operations are completed, the entire
replacement text is re-scanned for further macro expansions.

Preprocessor directives
Preprocessor directives are lines included in the code of our programs that are not
program statements but directives for the preprocessor. These lines are always preceded
by a hash sign (#). The preprocessor is executed before the actual compilation of code
begins, therefore the preprocessor digests all these directives before any code is generated
by the statements.

These preprocessor directives extend only across a single line of code. As soon as a
newline character is found, the preprocessor directive is considered to end. No semicolon
(;) is expected at the end of a preprocessor directive. The only way a preprocessor
directive can extend through more than one line is by preceding the newline character at
the end of the line by a backslash (\).
macro definitions (#define, #undef)
To define preprocessor macros we can use #define. Its format is:

#define identifier replacement

When the preprocessor encounters this directive, it replaces any occurrence of


identifier in the rest of the code by replacement. This replacement can be an
expression, a statement, a block or simply anything. The preprocessor does not
understand C++, it simply replaces any occurrence of identifier by replacement.

1 #define TABLE_SIZE 100


2 int table1[TABLE_SIZE];
3 int table2[TABLE_SIZE];

After the preprocessor has replaced TABLE_SIZE, the code becomes equivalent to:

1 int table1[100];
2 int table2[100];

This use of #define as constant definer is already known by us from previous tutorials, but
#define can work also with parameters to define function macros:

#define getmax(a,b) a>b?a:b

This would replace any occurrence of getmax followed by two arguments by the
replacement expression, but also replacing each argument by its identifier, exactly as you
would expect if it was a function:

1 // function macro 5
2 #include <iostream> 7
3 using namespace std;
4
5 #define getmax(a,b) ((a)>(b)?(a):
6 (b))
7
8 int main()
9{
10 int x=5, y;
11 y= getmax(x,2);
12 cout << y << endl;
13 cout << getmax(7,x) << endl;
14 return 0;
}

Defined macros are not affected by block structure. A macro lasts until it is undefined with
the #undef preprocessor directive:

1 #define TABLE_SIZE 100


2 int table1[TABLE_SIZE];
3 #undef TABLE_SIZE
4 #define TABLE_SIZE 200
5 int table2[TABLE_SIZE];

This would generate the same code as:

1 int table1[100];
2 int table2[200];
Function macro definitions accept two special operators (# and ##) in the replacement
sequence:
If the operator # is used before a parameter is used in the replacement sequence, that
parameter is replaced by a string literal (as if it were enclosed between double quotes)

1 #define str(x) #x
2 cout << str(test);

This would be translated into:

cout << "test";

The operator ## concatenates two arguments leaving no blank spaces between them:

1 #define glue(a,b) a ## b
2 glue(c,out) << "test";

This would also be translated into:

cout << "test";

Because preprocessor replacements happen before any C++ syntax check, macro
definitions can be a tricky feature, but be careful: code that relies heavily on complicated
macros may seem obscure to other programmers, since the syntax they expect is on many
occasions different from the regular expressions programmers expect in C++.

Conditional inclusions (#ifdef, #ifndef, #if, #endif,


#else and #elif)

These directives allow to include or discard part of the code of a program if a certain
condition is met.

#ifdef allows a section of a program to be compiled only if the macro that is specified as
the parameter has been defined, no matter which its value is. For example:

1 #ifdef TABLE_SIZE
2 int table[TABLE_SIZE];
3 #endif

In this case, the line of code int table[TABLE_SIZE]; is only compiled if TABLE_SIZE
was previously defined with #define, independently of its value. If it was not defined,
that line will not be included in the program compilation.

#ifndef serves for the exact opposite: the code between #ifndef and #endif directives
is only compiled if the specified identifier has not been previously defined. For example:

1 #ifndef TABLE_SIZE
2 #define TABLE_SIZE 100
3 #endif
4 int table[TABLE_SIZE];

In this case, if when arriving at this piece of code, the TABLE_SIZE macro has not been
defined yet, it would be defined to a value of 100. If it already existed it would keep its
previous value since the #define directive would not be executed.

The #if, #else and #elif (i.e., "else if") directives serve to specify some condition to be
met in order for the portion of code they surround to be compiled. The condition that
follows #if or #elif can only evaluate constant expressions, including macro
expressions. For example:

1 #if TABLE_SIZE>200
2 #undef TABLE_SIZE
3 #define TABLE_SIZE 200
4
5 #elif TABLE_SIZE<50
6 #undef TABLE_SIZE
7 #define TABLE_SIZE 50
8
9 #else
10 #undef TABLE_SIZE
11 #define TABLE_SIZE 100
12 #endif
13
14 int table[TABLE_SIZE];

Notice how the whole structure of #if, #elif and #else chained directives ends with
#endif.

The behavior of #ifdef and #ifndef can also be achieved by using the special operators
defined and !defined respectively in any #if or #elif directive:

1 #if !defined TABLE_SIZE


2 #define TABLE_SIZE 100
3 #elif defined ARRAY_SIZE
4 #define TABLE_SIZE ARRAY_SIZE
5 int table[TABLE_SIZE];

Line control (#line)


When we compile a program and some error happens during the compiling process, the
compiler shows an error message with references to the name of the file where the error
happened and a line number, so it is easier to find the code generating the error.

The #line directive allows us to control both things, the line numbers within the code files
as well as the file name that we want that appears when an error takes place. Its format
is:

#line number "filename"

Where number is the new line number that will be assigned to the next code line. The line
numbers of successive lines will be increased one by one from this point on.

"filename" is an optional parameter that allows to redefine the file name that will be
shown. For example:

1 #line 20 "assigning variable"


2 int a?;

This code will generate an error that will be shown as error in file "assigning
variable", line 20.

Error directive (#error)


This directive aborts the compilation process when it is found, generating a compilation the
error that can be specified as its parameter:

1 #ifndef __cplusplus
2 #error A C++ compiler is required!
3 #endif

This example aborts the compilation process if the macro name __cplusplus is not
defined (this macro name is defined by default in all C++ compilers).

Source file inclusion (#include)


This directive has also been used assiduously in other sections of this tutorial. When the
preprocessor finds an #include directive it replaces it by the entire content of the
specified file. There are two ways to specify a file to be included:

1 #include "file"
2 #include <file>

The only difference between both expressions is the places (directories) where the
compiler is going to look for the file. In the first case where the file name is specified
between double-quotes, the file is searched first in the same directory that includes the file
containing the directive. In case that it is not there, the compiler searches the file in the
default directories where it is configured to look for the standard header files.
If the file name is enclosed between angle-brackets <> the file is searched directly where
the compiler is configured to look for the standard header files. Therefore, standard header
files are usually included in angle-brackets, while other specific header files are included
using quotes.

Pragma directive (#pragma)


This directive is used to specify diverse options to the compiler. These options are specific
for the platform and the compiler you use. Consult the manual or the reference of your
compiler for more information on the possible parameters that you can define with
#pragma.

If the compiler does not support a specific argument for #pragma, it is ignored - no error
is generated.

Predefined macro names


The following macro names are defined at any time:

macro value
Integer value representing the current line in the source code file being
__LINE__
compiled.
A string literal containing the presumed name of the source file being
__FILE__
compiled.
A string literal in the form "Mmm dd yyyy" containing the date in which the
__DATE__
compilation process began.
A string literal in the form "hh:mm:ss" containing the time at which the
__TIME__
compilation process began.
An integer value. All C++ compilers have this constant defined to some value.
__cplusplus If the compiler is fully compliant with the C++ standard its value is equal or
greater than 199711L depending on the version of the standard they comply.

For example:

1 // standard macro names


2 #include <iostream>
3 using namespace std;
4
5 int main()
6{
7 cout << "This is the line number " << __LINE__;
8 cout << " of file " << __FILE__ << ".\n";
9 cout << "Its compilation began " << __DATE__;
10 cout << " at " << __TIME__ << ".\n";
11 cout << "The compiler gives a __cplusplus value of " <<
12 __cplusplus;
13 return 0;
}

You might also like