Programming in C: Unit I (d): Preprocessor Directives

#define

with Example C Programs | Preprocessor Directives

To define preprocessor macros we use #define. The #define statement is also known as macro definition or simply a macro. There are two types of macros:object-like macro and function-like macro

#DEFINE

To define preprocessor macros we use #define. The #define statement is also known as macro definition or simply a macro. There are two types of macros:

• object-like macro and

• function-like macro.

Object-like Macro

An object-like macro is a simple identifier which will be replaced by a code fragment. They are usually used to give symbolic names to numeric constants. Object-like macros do not take any argument. It is the same what we have been using to declare constants using #define directive. The general syntax of defining a macro can be given as

#define identifier string

The preprocessor replaces every occurrence of the identifier in the source code by a string. The macro must start with the keyword #define and should be followed by an identifier and a string with at least one blank space between them. The string may be any text, a statement, or anything. However, the identifier must be a valid C name.

#define PI 3.14

The line defines a macro named PI as an abbreviation for the token 3.14. If somewhere after this #define directive there comes a C statement of the form

area = PI * radius * radius;

Then the C preprocessor will recognize and expand the macro PI. The C compiler will see the same tokens as it would if you had written

area = 3.14 * radius * radius;

A macro definition can also include an expression. However, when using expressions for replacement, make sure that the order of evaluation is correct. As a good programming habit, use parenthesis in the expression. For example, consider the following macro definitions:

Programming Tip: A semicolon (;) should not be placed at the end of a preprocessor directive.

#define ROWS 3

#define COLS 3

#define SIZE (ROWS * COLS)

Look at the following program which illustrates the use of #define for literal text substitution:

#include <stdio.h>

#include <conio.h>

#define INPUT printf("\n Enter a number: ");

scanf("%d", &num);

#define EQUALS = =

#define PRINT1 printf("\n_GREAT")

#define PRINT2 printf("\n TRY AGAIN")

#define START main() {

#define END getch();\

return 0; }

Programming Tip: In order to extend a preprocessor directive to multiple lines, place a backslash character (\) as the last character of the line.

START

int num;

INPUT

if (num EQUALS 100)

PRINT1;

else

END

PRINT2;

By convention, macro names are written in uppercase. This makes the program easy to read as anyone can tell at a glance which names are macros.

Note

An identifier is never replaced if it appears in a comment, within a string, or as part of a longer identifier.

Function-like Macros

Function-like macros are more complex than object-like macros. They are known as function-like macros because they are used to stimulate functions. When a function is stimulated using a macro, the macro definition replaces the function definition. The name of the macro serves as the header and the macro body serves as the function body. The name of the macro will then be used to replace the acro will then be used to function call.

The function-like macro includes a list of parameters. References to such macros look like function calls. We have studied that when a function is called, control passes from the calling function to the called function at run time. However, when a macro is referenced, source code is inserted into the program at compile time. The parameters are replaced by the corresponding arguments, and the text is inserted into the program stream. Therefore, macros are mo considered to be much more efficient than functions as they avoid the overhead involved in calling a function.

Programming Tip: Macro names can be in lowercase characters. But as a convention you must write macro names in uppercase characters.

A function-like macro defini- tion declares the names of formal parameters within parentheses, separated by commas. In case the function-like macro does not ac- cept any argument, then an empty formal parameter list can be provided.

The syntax of defining a function-like macro can be given as

# define identifier (arg1, arg2, ... argn) ... string

An identifier is followed by a parameter list in parentheses and the replacement string. Note that 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.

Invoking a Function-like Macro

A function-like macro is invoked by writing the identifier followed by a comma-separated list of arguments in parentheses. However, make sure that the number of arguments should match the number of parameters in the macro definition. One exception to this is if the parameter list in the definition ends with an ellipsis, the number of arguments in the invocation should exceed the number of parameters in the definition. The excess arguments are called trailing arguments.

When the preprocessor encounters a function-like macro invocation, argument substitution takes place. A parameter in the replacement code is replaced by the corresponding argument. If there are trailing arguments (as permitted by the macro definition), then they are merged with the intervening commas as if they were a single argument.

In case of nested macros (macro within another macro definition), i.e., if there are any macro invocations contained in the argument itself, they are completely replaced before the argument replaces its corresponding parameter in the replacement code.

The following line defines the macro MUL as having two parameters a and b and the replacement string (a * b):

#define MUL (a, b) (a*b)

Look how the preprocessor changes the following statement provided it appears after the macro definition.

a= 2, b = 3, C;

int a C = MUL (a, b) // c = a*b;

Programming Tip: Use macros instead of functions as macros are much more efficient than functions since they avoid the overhead involved in calling a function.

In the output of the preprocessor, the above statement would appear as: c = a*b;

While using function-like mac- ros, you must use parentheses to ensure correct evaluation of replacement text. For example, if a macro SQUARE is defined as

 #define SQUARE (x) (x*x)

Then invoking the macro by writing

int a = 2, b = 3, c;

C = SQUARE (a); // c= 2*2;

The above statement is fine and would return the value 4. However, had you written the statements given below, you would have got incorrect results. For example, if had you written

int a = 2, b = 3, c;

C = SQUARE (a+b);

// c= 2+3*2+3; so c = 2+6+3 = 11

It is, therefore, very important that you put parentheses around each parameter in the definition to correctly evaluate an expression. So let us redefine our SQUARE macro by writing

#define SQUARE (x) ((x) * (x))

Now a statement like

c = SQUARE (a+b);

would be expanded as

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

Note

For portability, you should not have more than 31 parameters for a macro.

Nesting of Macros

We can use a macro in the definition of another macro. For example, consider the following macro definitions:

#define SQUARE (x) ((x) * (x))

#define CUBE (x) (SQUARE (x) * (x))

#define FOURTH POWER (x) (CUBE (x) * (x))

#define FIFTH POWER (x) (FOURTH_POWER (x) * (x))

In these definitions, the preprocessor will expand each macro until all the macros do not exhaust in the text. For example, the macro CUBE will be expanded as

CUBE (x) => SQUARE (x) * (x) => ((x) * (x)) * (x)

Generally, in C a macro can be nested to 31 levels.

Rules for Using Macros

Let us summarize some rules that must be used when specifying macro definitions and invoking them from an arbitrary place within the program.

Programming Tip: Although the preprocessor directive is usually placed before the main(), it can appear anywhere in the program code. However, if written in between, the directive will be applied only in the remainder of the source file.

• The macro name and the formal ni parameters are identifiers, so they must be specified in accordance with the rules for identifiers in the c language.

• Spaces, tabs, and comments are allowed to be used freely within a #define directive. All the spaces, tabs, and comments are replaced by a single space.

• White space in between the identifier and the left parenthesis that introduces the parameter list is not allowed.

• When referencing a macro, you may use comments and white-space characters freely. Comments are replaced with a single space. However, white-space characters (except leading and trailing white space) are preserved during the substitution.

• The number of arguments in the reference must match the number of parameters in the macro definition.

Operators Related to Macros

In this section, we will read about some operators that are directly or indirectly related to macros in C language.

# Operator to Convert to String Literals

The # preprocessor operator which can be used only in a function-like macro definition is used to convert the argument that follows it to a string literal. For example:

#include <stdio.h>

#define PRINT (num) printf(#num " =%d", num)

main()

{

PRINT (20);

}

The macro call expands to

printf("num" " = %d", num)

Programming Tip: Ensure that there is not more than one # operator in the replacement list of a macro definition because in such a case the order of evaluation of the operators is not defined.

Finally, the preprocessor will automatically concatenate two string literals into one string. So the above statement will become

printf("num = %d", num)

Hence, the unary # operator produces a string from its operand. Consider another macro MAC which is defined as

#define MAC (x) #x

MAC ("10") give a string literal equal to "10". Similarly, MAC ("HI") would give a string literal "HI".

Rules of using the # operator in a function-like macro definition

The # operator must be used in a function-like macro by following the rules mentioned below:

• A parameter following # operator in a function-like macro is converted into a string literal containing the argument passed to the macro.

• Leading and trailing white-space characters (those that appear before or after) in the argument passed to the macro are deleted.

• If the argument passed to the macro has multiple white- space characters, then they 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, then on expansion of the macro, a second \ character is inserted before the original \.

• If the argument passed to the macro contains a " (double quotation mark) character, then on expansion of the macro, a \ character is inserted before the ".

• The conversion of an argument into a string literal occurs before macro expansion on that argument.

• If there is more than one # operator 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.

Merge Operator (##)

At times you need macros to generate new tokens. Using the merge operator you can concatenate two tokens into a third valid token. For example,

#include <stdio.h>

#define JOIN (A, B) A##B

main( )

int i;

for (i = 1; i <= 5;i++)

printf("\n HI JOIN (USER, i): ");

}

The above program would print

HI USER1

HI USER2

HI USER3

HI USER4

HI USER5

Programming in C: Unit I (d): Preprocessor Directives : Tag: : with Example C Programs | Preprocessor Directives - #define