Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
Therefore, the assignment statement following the macro definitions is expanded to the following statement:
result = (1 + 1) * (1 + (1 + 1));
When you are using the #define directive with a macro body that is an
expression, you need to enclose the macro body in parentheses. For example, if the macro definition is
#define SUM 12 + 8
then the following statement:
result = SUM * 10;
becomes this:
result = 12 + 8 * 10;
which assigns 92 to result.
However, if you enclose the macro body in parentheses like this:
#define SUM (12 + 8)
then the assignment statement becomes this:
result = (12 + 8) * 10;
and produces the result 200, which is likely what you want.
29 067231861x CH23 1/25/00 11:03 AM Page 397
Compiling Programs: The C Preprocessor
397
Compiling Your Code Under Conditions
You can select portions of your C program that you want to compile by using a set of preprocessor directives. This technique is called
conditional compilation
. This is useful, especially when you’re testing a piece of new code or debugging a portion of code.
The
#ifdef
and
#endif
Directives
The #ifdef and #endif directives control whether a given group of statements is to be
23
included as part of your program.
The general form to use the #ifdef and #endif directives is
#ifdef macro_name
statement1
statement2
. . .
statementN
#endif
Here macro_name is any character string that can be defined by a #define directive.
statement1, statement2, and statementN are statements that are included in the program only if macro_name has already been defined. If maccro_name has not been defined, statement1, statement2, and everything up to statementN skipped.
Unlike an if statement in C, the statements under the control of the #ifdef directive are not enclosed in braces; instead, the #endif directive must be used to mark the end of the
#ifdef block.
For instance, the #ifdef directive in the following code segment:
. . .
#ifdef DEBUG
printf(“The contents of the string pointed to by str: %s\n”, str);
#endif
. . .
indicates that if the macro name DEBUG is defined, the printf() function in the statement following the #ifdef directive is included in the program. The compiler will compile the statement so that the contents of a string pointed to by str will be printed by the statement. However if DEBUG has not been defined, the printf() call will be entirely left out of your compiled program.
The
#ifndef
Directive
The #ifndef directive enables you to define code that is to be executed when a particular macro name is not defined.
29 067231861x CH23 1/25/00 11:03 AM Page 398
398
Hour 23
The general format to use #ifndef is the same as for #ifdef:
#ifndef macro_name
statement1
statement2
. . .
statementN
#endif
Here macro_name, statement1, statement2, and statementN have the same meanings as those in the form of #ifdef introduced in the previous section. Again, the #endif directive is needed to mark the end of the #ifndef block.
Listing 23.2 contains a program that demonstrates how to use the #ifdef, #ifndef, and
#endif directives together.
TYPE
LISTING 23.2
Using the #ifdef, #ifndef, and #endif Directives
1: /* 23L02.c: Using #ifdef, #ifndef, and #endif */
2: #include
3:
4: #define UPPER_CASE 0
5: #define NO_ERROR 0
6:
7: main(void)
8: {
9: #ifdef UPPER_CASE
10: printf(“THIS LINE IS PRINTED OUT,\n”);
11: printf(“BECAUSE UPPER_CASE IS DEFINED.\n”);
12: #endif
13: #ifndef LOWER_CASE
14: printf(“\nThis line is printed out,\n”);
15: printf(“because LOWER_CASE is not defined.\n”);
16: #endif
17:
18: return NO_ERROR;
19: }
The following output is shown on the screen after the executable 23L02.exe is created and run on my computer:
THIS LINE IS PRINTED OUT,
OUTPUT
BECAUSE UPPER_CASE IS DEFINED.
This line is printed out,
because LOWER_CASE is not defined.
The purpose of the program in Listing 23.2 is to use #ifdef and #ifndef direc-ANALYSIS tives to control whether a message will be displayed.
29 067231861x CH23 1/25/00 11:03 AM Page 399
Compiling Programs: The C Preprocessor
399
Two macro names, UPPER_CASE and NO_ERROR, are defined in lines 4 and 5.
The #ifdef directive in line 9 checks whether the UPPER_CASE macro name has been defined. Because the macro name has been defined in line 4, the two statements in lines 10 and 11 (until the #endif directive in line 12 marks the end of the #ifdef block) are included in the compiled program.
In line 13, the #ifndef directive tells the preprocessor to include the two statements in lines 14 and 15 in the program if the LOWER_CASE macro name has not been defined. As
23
you can see, LOWER_CASE is not defined in the program at all. Therefore, the two statements in lines 14 and 15 are compiled as part of the program.
The output from running the program in Listing 23.2 shows that the printf() functions in lines 10, 11, 14, and 15 are compiled and executed accordingly, under the control of the #ifdef and #ifndef directives. You can try modifying the program by changing line 4 so that it defines LOWER_CASE rather than UPPER_CASE. The #ifdef and #ifndef directives will then remove all four printf() calls from the program.
The
#if
,
#elif
, and
#else
Directives
The #if directive specifies that certain statements are to be included only if the value represented by the conditional expression is nonzero. The conditional expression can be an arithmetic expression.
The general form to use the #if directive is
#if expression
statement1
statement2
. . .
statementN
#endif
Here expression is the conditional expression to be evaluated. statement1, statement2, and statementN represent the code to be included if expression is nonzero.
Note that the #endif directive is included at the end of the definition to mark the end of the #if block, as it does for an #ifdef or #ifndef block.
In addition, the #else directive provides an alternative to choose. The following general form uses the #else directive to put statement_1, statement_2, and statement_N into the program if expression is zero:
#if expression
statement1
statement2
. . .
29 067231861x CH23 1/25/00 11:03 AM Page 400
400
Hour 23
statementN
#else
statement_1
statement_2
. . .
statement_N
#endif
Again, the #endif directive is used to mark the end of the #if block.
Also, a macro definition can be used as part of the conditional expression evaluated by the #if directive. If the macro is defined, it has a nonzero value in the expression; otherwise, it has the value 0.
For example, look at the following portion of code:
#ifdef DEBUG
printf(“The value of the debug version: %d\n”, debug);
#else
printf(“The value of the release version: %d\n”, release);
#endif
If DEBUG has been defined by a #define directive, the value of the debug version is printed out by the printf() function in the following statement:
printf(“The value of the debug version: %d\n”, debug);
Otherwise, if DEBUG has not been defined, the following statement is executed: printf(“The value of the release version: %d\n”, release);
Now consider another example:
#if 1
printf(“The line is always printed out.\n”);
#endif
The printf() function is always executed because the expression 1 evaluated by the #if directive never returns 0.
In the following example:
#if MACRO_NAME1 || MACRO_NAME2
printf(“MACRO_NAME1 or MACRO_NAME2 is defined.\n”);
#else
printf(“MACRO_NAME1 and MACRO_NAME2 are not defined.\n”);
#endif
the logical operator || is used, along with MACRO_NAME1 and MACRO_NAME2 in the expression evaluated by the #if directive. If one of the macro names, MACRO_NAME1 or MACRO_NAME2, has been defined, the expression evaluates to a nonzero value; otherwise, 0
is produced.
29 067231861x CH23 1/25/00 11:03 AM Page 401
Compiling Programs: The C Preprocessor
401
The C preprocessor has another directive, #elif, which stands for “else if.” You can use
#if and #elif together to build an if-else-if chain for multiple conditional compilation.
The program shown in Listing 23.3 is an example of using the #if, #elif, and #else directives.
TYPE
LISTING 23.3
Using the #if, #elif, and #else Directives
23
1: /* 23L03.c: Using #if, #elif, and #else */
2: #include
3:
4: #define C_LANG ‘C’
5: #define B_LANG ‘B’
6: #define NO_ERROR 0
7:
8: main(void)
9: {
10: #if C_LANG == ‘C’ && B_LANG == ‘B’
11: #undef C_LANG
12: #define C_LANG “I know the C language.\n”
13: #undef B_LANG
14: #define B_LANG “I know BASIC.\n”
15: printf(“%s%s”, C_LANG, B_LANG);
16: #elif C_LANG == ‘C’
17: #undef C_LANG
18: #define C_LANG “I only know C language.\n”
19: printf(“%s”, C_LANG);
20: #elif B_LANG == ‘B’
21: #undef B_LANG
22: #define B_LANG “I only know BASIC.\n”
23: printf(“%s”, B_LANG);
24: #else
25: printf(“I don’t know C or BASIC.\n”);
26: #endif
27:
28: return NO_ERROR;
29: }
After the executable 23L03.exe is created and run, the following output is displayed on the screen of my computer:
I know C language.
OUTPUT
I know BASIC.
The purpose of the program in Listing 23.3 is to use the #if, #elif, and #else
ANALYSIS
directives to select portions of code that are going to be compiled.
29 067231861x CH23 1/25/00 11:03 AM Page 402
402
Hour 23
Inside the main() function, the #if directive in line 10 evaluates the conditional expression C_LANG == ‘C’ && B_LANG == ‘B’. If the expression evaluates to nonzero, statements in lines 11–15 are selected to be compiled.
In line 11 the #undef directive is used to remove the C_LANG macro name. Line 12 then redefines C_LANG with the string “I know the C language.\n”. Likewise, line 13
removes the B_LANG macro name and line 14 redefines B_LANG with another character string. The printf() call in line 15 prints the two newly assigned strings associated with C_LANG and B_LANG.
The #elif directive in line 16 starts to evaluate the expression C_LANG == ‘C’ if the expression in line 10 has evaluated to 0. If the C_LANG == ‘C’ expression evaluates to nonzero, the statements in lines 17–19 are compiled.
If, however, the expression in line 16 also fails to evaluate to a nonzero value, the B_LANG
== ‘B’ expression is evaluated by another #elif directive in line 20. The statements in lines 21–23 are skipped, and the statement in line 25 is compiled finally if the B_LANG ==
‘B’ expression evaluates to 0.
In line 26 the #endif directive marks the end of the #if block that started on line 10.
From the program in Listing 23.3 you can tell that C_LANG and B_LANG have been properly defined in lines 4 and 5. Therefore, the statements in lines 11–15 are selected as part of the program and compiled by the C compiler. The two character strings assigned to C_LANG and B_LANG during the redefinition are displayed after the program in Listing 23.3 is executed.
You can change the value of the macros C_LANG and B_LANG to experiment with other executions of the program.
Nested Conditional Compilation
According to the ANSI C standard, the #if and #elif directives can be nested at least eight levels.
For example, the #if directive is nested in the following code segment:
#if MACRO_NAME1
#if MACRO_NAME2
#if MACRO_NAME3
printf(“MACRO_NAME1, MACRO_NAME2, and MACRO_NAME3\n”);
#else
printf(“MACRO_NAME1 and MACRO_NAME2\n”);
#endif
#else
printf(“MACRO_NAME1\n”);
29 067231861x CH23 1/25/00 11:03 AM Page 403
Compiling Programs: The C Preprocessor
403
#endif
#else
printf(“No macro name defined.\n”);
#endif
Here the #if directive is nested to three levels. Note that each #else or #endif is associated with the nearest #if.
Now let’s have a look at another example in Listing 23.4, in which the #if directives are nested.
23
TYPE
LISTING 23.4
Nesting the #if Directive
1: /* 23L04.c: Nesting #if */
2: #include
3:
4: /* macro definitions */
5: #define ZERO 0
6: #define ONE 1
7: #define TWO (ONE + ONE)
8: #define THREE (ONE + TWO)
9: #define TEST_1 ONE
10: #define TEST_2 TWO
11: #define TEST_3 THREE
12: #define MAX_NUM THREE
13: #define NO_ERROR ZERO
14: /* function declaration */
15: void StrPrint(char **ptr_s, int max);
16: /* the main() function */