210 likes | 360 Views
14. THE PREPROCESSOR. Preprocessor Directives. • Preprocessing involves making changes to the text of the source program. • Preprocessing is (apparently) done before actual compilation begins. • The preprocessor doesn’t know (very much) C. • Major kinds of preprocessor directives:
E N D
Preprocessor Directives • Preprocessing involves making changes to the text of the source program. • Preprocessing is (apparently) done before actual compilation begins. • The preprocessor doesn’t know (very much) C. • Major kinds of preprocessor directives: Macro definition Conditional compilation File inclusion
Preprocessor Directives • Rules for using preprocessor directives: Must begin with a #. May contain extra spaces and tabs. End at the first new-line character, unless continued using \. Can appear anywhere in a program. Comments may appear on the same line.
Simple Macros • Form of a simple macro: #define identifier replacement-list The replacement list can be any sequence of C tokens, including identifiers, keywords, numbers, character constants, string literals, operators, and punctuation. • Uses of simple macros: Defining “manifest constants” Making minor changes to the syntax of C Renaming types As conditions to be tested later by the preprocessor
Simple Macros • Examples of simple macros: #define N 100 #define PI 3.14159 #define CR '\r' #define WARNING "Warning: nonstandard feature" #define BEGIN { #define END } #define BOOL int #define DEBUG • Warning: Don’t put any extraneous symbols in a macro definition; these will become part of the replacement list: #define N = 100 int a[N]; /* becomes int a[= 100]; */ #define N 100; int a[N]; /* becomes int a[100;]; */
Parameterized Macros • Form of a parameterized macro: #define identifier(x1, x2, …, xn) replacement-list There must be no space between the identifier and the left parenthesis. • Parameterized macros often serve as simple functions. • Examples of parameterized macros: #define MAX(x,y) ((x)>(y)?(x):(y)) #define EVEN(n) ((n) % 2 == 0) #define ODD(n) ((n) % 2 != 0) #define TOUPPER(c) ('a'<=(c)&&(c)<='z'?(c)-'a'+'A':(c)) #define getchar() getc(stdin)
Parameterized Macros • • Advantages of using a parameterized macro instead of a function: • The compiled code will • execute more rapidly. • Macros are “generic.” • • Disadvantages of using a parameterized macro instead of a function: • The compiled code will often be larger. • Arguments aren’t type-checked. • It’s not possible to have a pointer to a macro. • A macro may evaluate its arguments more than once, causing subtle errors. • • In C99, functions can be declared inline. A call of an inline function can be expanded by the compiler, yielding the speed advantage of a macro without the potential hazards.
The # Operator • Parameterized macros can be used to create templates for commonly used segments of code: #define PRINT_INT(x) printf("%d\n", x) PRINT_INT(i/j); /* becomes printf("%d\n", i/j); */
The # Operator • The # operator converts a macro argument into a string literal: #define PRINT_INT(x) printf(#x " = %d\n", x) PRINT_INT(i/j); The preprocessor will replace the call of PRINT_INT by the following code: printf("i/j" " = %d\n", i/j); which is equivalent to printf("i/j = %d\n", i/j); The output produced by this call of printf will have the following form: i/j = 5
General Properties of Macros • One macro may be defined in terms of another: #define PI 3.14159 #define TWO_PI (2*PI) When the preprocessor encounters the symbol TWO_PI later in the program, it replaces it by (2*PI). The preprocessor then rescans the replacement list to see if it contains invocations of other macros (PI in this case). The preprocessor will rescan the replacement list as many times as necessary to eliminate all macro names.
General Properties of Macros • The preprocessor replaces only entire symbols, not portions of symbols. It ignores macro names embedded in identifiers, character constants, and string literals. #define SIZE 256 char error_msg[] = "Error: SIZE exceeded"; /* not replaced */ ... if (BUFFER_SIZE > SIZE) /* only SIZE is replaced */ printf("%s\n", error_msg); • A macro definition normally remains in effect from the point at which it appears to the end of the file. However, a macro may be undefined by the following directive: #undef identifier
General Properties of Macros • Attempting to redefine a macro without undefining it first is illegal, unless the new definition is identical to the old one. • Attempting to undefine an undefined macro is legal.
Parentheses in Macro Definitions • When a macro’s replacement list is an expression, it should be enclosed in parentheses. Otherwise, the following may happen: #define TWO_PI 2*3.14159 ... conversion_factor = 360/TWO_PI; /* conversion_factor = 360/2*3.14159; */ • In a parameterized macro, each parameter should be enclosed in parentheses. If not, this may happen: #define SCALE(x) (x*10) ... j = SCALE(i+1); /* j = (i+1*10); */
Conditional Compilation • The #if directive tests an expression to determine whether or not a particular section of text should be included in a program. The #endif directive marks the end of the section: #if constant-expression … #endif • The operator defined can be used in an #if directive: #if defined(identifier) … #endif
Conditional Compilation • The #ifdef directive combines #if with defined: #ifdef identifier … #endif • The #ifndef directive is the opposite of #ifdef: #ifndef identifier … #endif
Conditional Compilation • #if, #ifdef, and #ifndef all allow #elif and #else clauses: if-header … #elif constant-expression … #else … #endif
Uses of Conditional Compilation • Writing code to run on different machines or under different operating systems: #if defined(WIN32) … #elif defined(MAC_OS) … #elif defined(LINUX) … #endif • Providing a default definition for a symbol: #ifndef BUFFER_SIZE #define BUFFER_SIZE 256 #endif
Uses of Conditional Compilation • Including debugging code: #ifdef DEBUG printf("Value of i: %d\n", i); printf("Value of j: %d\n", j); #endif • Temporarily disabling code that contains comments: #if 0 bkg_color = BLACK; /* set background color */ #endif • Protecting header files from being included more than once.
File Inclusion • The #include directive causes the entire contents of a file to be included in a program. • Files included into a program are called header files (or include files). • By convention, header files have the extension .h. • One form of #include is used for files that belong to the C library: #include <filename> Most compilers will search the directory (or directories) where system header files are kept.
File Inclusion • The other form of #include is used for files created by the programmer: #include "filename" Most compilers will search the current directory, then search the directory (or directories) where system header files are kept. • File names may include a drive specifier and/or a path: #include <sys\stat.h> #include "utils.h" #include "..\include\utils.h" #include "d:utils.h" #include "\cprogs\utils.h" #include "d:\cprogs\utils.h"