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.
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 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.
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.
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.
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
Programming in C
CS3251 2nd Semester CSE Dept 2021 | Regulation | 2nd Semester CSE Dept 2021 Regulation