Chapter 7: Preprocessing Directives Outline We will only cover these topics in Chapter 7, the remain ones are optional. Introduction Symbolic Constants and Macros Source File Inclusion Conditional Compilation Predefined Macros
Introduction Preprocessing Affect program preprocessing and execution Capabilities Inclusion of additional C source files Definition of symbolic constants and macros Conditional preprocessing of a program Format of preprocessing directives A preprocessing directive consists of a sequence of preprocessing tokens that begins with a pound sign #, which must be the first non-space character on the line. Some preprocessing directives are listed below.
Directive #define #elif #else #endif Description Define a preprocessor macro. Alternatively include some text based on the value of another expression, if the previous #if, #ifdef, #ifndef, or #leif test failed. Alternatively include some text, if the previous #if, #ifdef, #ifndef, or #elif test failed. Terminate conditional text. #error Produce a compile-time error with a designated message. #if #ifdef #ifndef #include #line #pragma Conditionally include text, based on the value of an expression. Conditionally include text, based on whether a macro name is defined. Conditionally include text, based on if a name is not a defined macro. Insert text from another source file. Give a line number for message. Compiler/interpreter specific features, not in C standard # Null directive defined Preprocessing operator that yields 1 if a name is defined as a preprocessing macro and 0 otherwies; used in #if and #elif.
Symbolic Constants and Macros The #define Preprocessing Directive This preprocessing directive is used to create symbolic constants and macros. Form #define identifier replacement-list defines an object-like macro that causes each subsequent instance of the macro names to be replaced by the replacement-list of preprocessing tokens that constitute the remainder of the directive. The new-line is a character that terminates the #define preprocessing directive.
Symbolic constants The simple form of macro is particularly useful for introducing named constants into a program. It allows for easier modification of the constants later on.when programs are processed, all occurrences of symbolic constants indicated by identifier are replaced by the replacement-list. Example: #define BLOCK_SIZE 0x100 we can write int size = BLOCK_SIZE; instead of int size = 0x100; in the program. Note: Cannot redefine symbolic constants with different values by multiple #define statements
A preprocessing directive of the form #define identifier(identifier-list-opt) replacement-list new-line defines a function-like macro with arguments, similar syntactically to a function call. The parameters are specified by the optional list of identifiers. Example: if a macro mul with two arguments is defined by #define mul(x,y) ((x)*(y)) then the source program line result = mul(5, a+b); is replaced with result = ((5)*(a+b));
NOTE: Parentheses are important in macro definitions. Example: If macro mul is defined as #define mul(x,y) (x*y) The statement result = mul(5, a+b); in the source program becomes result = (5*a+b); The evaluation will be incorrect.
#undef Undefine a symbolic constant or macro, which can later be redefined. Example: #define mul(x,y) ((x)*(y)) /* */ #undef mul int mul; /* mul can be used after it is undefined */
Source File Inclusion The #include Preprocessing Directive Copy of a specified header file included in place of the directive. It has following two common forms. 1)#include <header.h> Searches standard library for header file and replaces the directive by the entire contents of the file. In Ch, the header is searched according to the paths specified by the the system variable _ipath. C compilers in Unix will typically search the header file in the directory /usr/include. In Visual C++, the header file is searched based on the paths in the environment variable INCLUDE. or cl I C:/home/assount/include program.c Used for standard library files 2) #include header.h" C compilers and interpreters will first search the header file in the same directory where the file is being processed, which typically is the current directory. Then search the header file in the paths as if it was included by #include <header.h>.
Applications Loading header files #include <stdio.h> Programs with multiple source files to be compiled together Includes user defined header files which have common declarations and definitions (classes, structures, function prototypes, and macros)
Conditional Compilation Conditional compilation Enables the user to control the compilation of the program, screen out portions of source code that are not to be compiled. Structure The structure is similar to if and else statement in C. Conditional preprocessing directives #if, #else, #elif, and #endif
Preprocessing directives of the forms #if expr1 /*... */ #elif expr2 /*... */ #else /*... */ #endif check whether the controlling expression evaluates to nonzero. Every #if ends with #endif Example: #if defined(_hpux_) printf( I am using HP-UX\n ); #elif defined(_win32_) printf( I am using Windows\n); #endif
Preprocessing directives of the forms # ifdef identifier # ifndef identifier check whether the identifier is or is not currently defined as a macro name. #ifdef identifier is the short form of #if defined(identifier) #ifndef identifier is the short form of #if!defined(identifier) Each directive s condition is checked in order. If it evaluates to false (zero), then the group that it controls is skipped: directives are processed only through the name that determines the directive in order to keep track of the level of nested conditionals. Only the first group whose control condition evaluates to true (nonzero) is processed. If none of the conditions evaluates to true, and there is a #else directive, then the group controlled by the #else is processed; if lacking a #else directive, then all the groups until the #endif are skipped.
Comment out a segment of code Comment out code segment which contains /*... */ Use following format to comment out the segment of code double d = some_func(); #ifdef JUNK /* This code segment will be commented out */ printf( d = %f\n, d); #endif The code segment will be commented out when JUNK is not defined, To uncomment the code segment, define JUNK or remove #ifdef JUNK and #endif.
To include a header file in a program only once, it is typically handled using the combination of the following preprocessing directives #ifndef, #define, and #endif. For example, a header file header.h may consist of the following code fragment. #ifndef HEADER_H #define HEADER_H /* code */ #endif
Macros defined in C Predefined Macros Macro Name LINE FILE DATE TIME STDC Description The line number of the current source program line which is expressed as a decimal integral constant The name of the current source file which is expressed as a string constant. The calendar date of the translation which is expressed as a string constant form Mmm dd yyy. Mmm is produced by asctime(). The current time which is expressed as a string constant of the form hh:mm:ss, as returned by asctime(). The decimal constant 1 for C comforming implementation. STDC_VERSION The decimal constant 199901L for C99 comforming implementation
Example: /* filename: predefined.c */ #include <stdio.h> int main() { printf(" FILE = %s\n", FILE ); printf(" LINE = %d\n", LINE ); printf(" DATE = %s\n", DATE ); printf(" TIME = %s\n", TIME ); #ifdef STDC printf(" STDC = %d\n", STDC ); #endif #ifdef STDC_VERSION printf(" STDC_VERSION = %d\n", STDC_VERSION ); #endif return 0; }
Output: FILE = predefined.c LINE = 6 DATE = Feb 24 2003 TIME = 00:09:42 STDC = 1 STDC_VERSION_ = 199901
/* File: printerror.c */ #include <stdio.h> #define printerror() printf("error in %s:%s():%d\n", \ FILE, func, LINE ); void funcname1() { printerror(); } void funcname2() { printerror(); } int main() { funcname1(); funcname2(); return 0; } Example Output Error in printerror.c:funcname1():7 Error in printerror.c:funcname2():10
Debugging code #define DEBUG /*... */ double x; x = some_func(); #ifdef DEBUG printf( The value of x = %f\n, x); #endif Defining DEBUG to print out the value of x. After debugging, remove #define statement. The debugging statements are ignored.