First | Next | Previous | Last | Glossary | About |
Whenever a C++ program is compiled it is processed by the preprocessor which looks for lines which begin with the # character. Note that the preprocessing takes place at compile time, there is no preprocessing at runtime. The C++ language provides several useful facilities which the preprocessor handles:
Include files Replace the line with the file. If filename is in ".." (double quotes) then the current directory is searched first then some standard directories. If filename is in <..> then only the standard directories are searched. Many of C++'s I/O, maths, system etc functions are defined in include files. These are generally header files (type h files) and need to be included with programs if the definitions are to be used. |
#include filename
e.g
#include "myfile.cc" #include <iostream> #include <ctype.h> We can leave the .h off standard include files since the practice of having two include files, one with .h and one without, seems to be developing. The iostream file itself includes iostream.h. Have a look in your include directories to confirm what is there. |
You might like to read the topic Header Files: How and why to use header files, in the gcc references. You will find lots of useful information here but it might be heavy going at the moment.
Constant definitions Anywhere that the identifier appears it will be replaced by its token. |
#define identifier token e.g. #define AGE 27 #define MODE 0644 |
Conditional compilation Direct the compiler to include or exclude parts of the program according to the state of certain conditions. |
#if constant_expression include this #else include this instead #endif |
Tests the value of the constant expression and acts accordingly |
#if X > 10 cout << X; #else count << (X/2); #endif |
If FRED is defined then |
#define FRED ifdef FRED .... #endif |
if FRED is not defined |
#ifndef FRED ... #endif |
An identifier can be removed from the list of defined identifiers |
#undef FRED |
If you want to know more you will find the gcc conditionals guide here Conditionals: How and why to use conditionals.
Macro expansions A macro can be defined and can have parameters Any where that the macro appears in the program it will be expanded to its string. |
#define ident(args) token_string #define MAX(A,B) ((A) > (B) ? (A) : (B)) #define SUM(A,B) (A + B) eg: x = MAX(12,92); will expand to: z = 12; y = 92; x = ((y) > (z) ? (y) : (z)); |
The statement x = ((y) > (z) ? (y) : (z)); might puzzle you. It is a shorthand notation for:
if (y > x) x = y; else x = z;
Macro substitutions (with arguments):
#define name(arg1, arg2, ...) string
Each occurence of name will be replaced by string. NB: no space between macro name and the parenthesis (. The macro name ends at the first whitespace.
You should understand that a macro is an expansion. It is not the same as a function. When the compiler encounters a macro as an executable statement or part of an executable statement it will expand it in place. A function of course is not expanded but is a single section of program that can be called repeatedly.
//ex20.cc #include <iostream> #define SQUARE(x) x * x #define LIMIT 5 int main() { int count = 1; char c; while (count <= LIMIT) { cout << count << "\t" << SQUARE(count) << endl; count++; } return 0; }
OUTPUT 1 1 2 4 3 9 4 16 5 25
This exercise shows a macro called SQUARE which obviously expands to give the square if its argument.
//ex21.cc #include <iostream> #define SQUARE(x) x * x #define LIMIT 5 int main() { int count = 2, j = 3; cout << count << "\t" << SQUARE(count + j) << endl; return 0; } which gives the output: 2 11
ex21 has a problem which is demonstrated here. When the macro expansion in ex21 takes place: SQUARE(2 + 3) it occurs as:
2 + 3 * 2 + 3
and since * has higher precedence than + the macro evaluates 2 + 6 + 3. Parentheses should be used to ensure that the evaluation takes place in the order required,eg SQUARE(x) (x) * (x)
However even this is not completely safe since a macro may be used in situations where other operators with higher precedence are used.
Macro's should be totally enclosed in parentheses:
SQUARE(x) ((x) * (x))
//ex22.cc #include <iostream> #define SQUARE(x) ((x) * (x)) #define LIMIT 5 int main() { int count = 2, j = 3; cout << count << "\t" << (count + j) << "\t" << SQUARE(count + j) << endl; return 0; }
ex21 shows the SQUARE macro as you should write. Use parentheses to force the evaluation you require.
//ex23.cc #include <iostream> int main() { int y; cout << "Type a 4 digit year , eg 1950: "; cin>> y; if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) cout << y << " is a leap year\n"; else cout << y << " is not a leap year\n"; return 0; }
The use of macros and defines can significantly assist the readability of a program. This example (ex23) doesn't use defines or macros and could be a little unclear. What on Earth does that long if condition do?
//ex24.cc #include <iostream> #define IS_LEAP_YEAR(year) (year % 4 == 0 && \ year % 100 != 0 || \ year % 400 == 0) #define then int main() { int y; cout << "Type a 4 digit year , eg 1950: "; cin >> y; if (IS_LEAP_YEAR(y)) then cout << y << " is a leap year\n"; else cout << y << " is not a leap year\n"; return 0; }
The next example (ex24) clarifies ex23 a little. It uses a macro and a trivial define. The macro has a meaningful name IS_LEAP_YEAR. The \ at the end of the lines in the macro is a line continuation character. This is necessary if the expansion is all on one line but does it help the readability of the macro if it is spread over several lines. The preprocessor requires its directives one per line, the continuation character is a way of writing "virtual" lines.
#include <iostream> #define MOD % #define EQUALS == #define AND && #define OR || #define NOTEQUALS != #define BECOMES = #define LESSTHAN < #define INTEGER int #define CHARACTER char #define LEAPYEAR " is a leap year\n" #define NOTLEAPYEAR " is not a leap year\n" #define then #define INCREMENT(x) (x++) #define SENDBACK return #define BEGIN { #define END } #define IS_LEAP_YEAR(year) ( year MOD 4 EQUALS 0\ AND \ year MOD 100 NOTEQUALS 0\ OR \ year MOD 400 EQUALS 0 ) #define THISYEAR 1993 CHARACTER main() BEGIN INTEGER y BECOMES THISYEAR - 101; CHARACTER c; while (y LESSTHAN THISYEAR) BEGIN if (IS_LEAP_YEAR(y)) then cout << y << LEAPYEAR; else cout << y << NOTLEAPYEAR; INCREMENT(y); END SENDBACK c; END
The last example (ex25) shows that you can use symbolic constants and macros to gain some interesting variations on the words used in C++.
You will find the gcc macro guide here Macros: How and why to use macros. It might be another helpful reference.
First | Next | Previous | Last | Glossary | About |
Copyright © 1999 - 2001
David Beech