See this page as a slide show
CS157 Preprocessor
The C Preprocessor
- The C Preprocessor is a program that runs
before the compiler and performs simple transformations on a program.
- It might be a separate program, it might be integrated into
the compiler itself. It doesn’t matter—think of it as a separate pass
over the source program.
What the Preprocessor Provides
- The preprocessor provides several facilities to the programmer:
- Definition of constants
- Definition of parameterized macros
- Inclusion of external files
- Conditional compilation of code
- Compiler Directives
- Some of these features have been
superseded by changes in the language itself.
Constants and #define
- The
#define
directive allows for defining constants.
#define NAME value
- Every instance of
NAME
following the
#define
will be replaced with value.
- value can, in fact, be any string and is
terminated by the end of line.
An Example
#define SIZE 20
#define BAD 10**10
int main() {
int list[SIZE]; // this is ok
num = BAD; // not ok, strange error about *
SIZE = 10; // not ok, SIZE is replaced with 20, error
BIG_INT = 10000000; // ok, but bad practice
#define BIG_INT long int
BIG_INT big_num; // ok
list[0] = BIG_INT; // not ok, BIG_INT is replaced with long int
return 0;
}
More about #define
- Don’t use
#define
to declare constants like π;
that’s what const
is for.
- Don’t use
#define
to replace elements of the C language,
like the long int example; that’s what typedef
is for.
#define
can be useful for controlling
conditional compilation of code.
- It’s best to avoid it, if possible.
const
The const
keyword tells the compiler that the
following variable declaration/initialization
will remain constant throughout the program.
const double PI = 3.14159;
PI = 3.0; // This won't work: it's const!
I realize that the concept of a constant variable is a
bit of a contradiction. Oh, well!
Macros
#define
can also be used to define parameterized macros. For example,
#define SQR(x) ((x)*(x))
- You will see this often in older C code.
- Crazy people think that this is more efficient.
- Not with modern compilers, it’s not.
- It can lead to other problems.
- Just use a function.
Macros
- In its simplest form, the
#define
preprocessor directive is
used to equate constants and operators to symbolic names
#define SALES_TAX 0.05
- The substitutions are made by the C preprocessor just prior
to program compilation
- C places no restrictions on the equivalences that can be
established with the
#define
statement
- When the equivalence consists of more than a single value,
operator, or variable, the symbolic name is referred to as a macro.
- “macro” means “big”, as in “macroeconomics”.
Macros
- Macros are extremely useful when the calculations or expressions
they contain are relatively simple.
- A macro definition can be continued on the next line by using a \.
- The advantage of using a macro instead of a function is an increase
in execution speed.
- No execution time loss due to the call and return procedures required
by a function.
- However, a sufficiently smart compiler might do this for you.
- Disadvantage: the increase in required program memory space when a
macro is used repeatedly.
- Each time a macro is used, the complete macro text is reproduced
and stored as part of the program.
- A function is stored in memory only once.
- Easy to write a macro wrong and create havoc! Be very, very careful!
Macros
- Operation defined in
#define
- A macro without arguments is treated like a symbolic constant.
- A macro with arguments has its arguments substituted for replacement text,
when the macro is expanded.
- Performs a text substitution—no data type checking
- What will this macro do?
#define PI 3.14159
#define CIRCLE_AREA(x) (PI * (x) * (x))
area = CIRCLE_AREA(4);
Macros
Use parentheses. Without them, the macro
#define CIRCLE_AREA(x) PI * x * x
would cause
area = CIRCLE_AREA(c + 2);
to become
area = 3.14159 * c + 2 * c + 2;
Multiple arguments:
#define RECTANGLE_AREA(x, y) ((x) * (y))
rectArea = RECTANGLE_AREA(a+4, b+7);
becomes:
rectArea = ((a+4) * (b+7));
Conditional Compilation
Making decisions at compile-time.
Conditional Compilation
- The facilities of the preprocessor can be
used to include or exclude part of a program from compilation.
- This is often used to handle features of the
language that are not portable across machines or compilers.
- Compiler/machine specific flags can be set
and used to select which version of a piece of code to include.
More #define
#define
can be used to create flags
#define DEBUG
- Notice that we are not defining a replacement macro, but simply creating
the flag
DEBUG
. It’s defined, or it isn’t.
- Flags like this will be used to control the inclusion of code.
- Flags can also be defined on the command line with the
-D
option:
c11 -DDEBUG my_prog.c
More Directives
- Structure similar to an
if
statement:
#ifdef __HP__
#define compare stricmp
#else
#define compare strcasecmp
#endif
- Determines if the symbol
__HP__
has been defined
(it will, on an HP compiler).
- Now, we can use
compare(a,b)
as if it’s a real function.
- Every
#if
must end with #endif
#else
is optional
#ifdef
means #if defined(name)
,
#ifndef
means #if !defined(name)
Conditional Compilation
- Other statements
#else
— equivalent of else
in an if
structure
#elif
— equivalent of else if
in an if
structure
- "Comment out" code
- Can’t use
/*
… */
around /* */
- Use:
#if 0
the code that you don’t like
#endif
- To enable the code, change 0 to 1
Example
#include <stdio.h>
int main() {
float friction, number;
unsigned int zip_code;
#ifndef DEBUG
zip_code = 13285;
#else
zip_code = 00001;
#endif
friction = 0.04;
number = (zip_code * friction) - 3.2;
#ifdef DEBUG
printf("friction: %f number %f\n",friction,number);
#endif
printf("The final number was %f\n", number);
return 0;
}
#include
- We have seen the
#include
previously for getting
access to standard library functions.
#include <file>
#include "file"
- The
#include
directive tells the preprocessor to
copy a file into the current file at the location of the #include
.
- The two syntaxes determine where the preprocessor looks for the file.
<file>
looks in official directories, often /usr/include
.
"file"
looks in the current directory
#undef
- Use
#undef
to undefine a macro name
declared in another file you may have included:
#include "everything.h"
#undef PIE
#define PIE "I like apple."
- If
PIE
happens to be defined in
everything.h
, then we have undefined it.
- If it is not defined in
everything.h
,
then the #undef
directive has no effect.