07 Intro to C preprocessor - Department of Computer Science

Download Report

Transcript 07 Intro to C preprocessor - Department of Computer Science

CSc 352
An Introduction to the C Preprocessor
Saumya Debray
Dept. of Computer Science
The University of Arizona, Tucson
[email protected]
The C preprocessor and its role
text: Ch. 14
C compiler (e.g., gcc)
source
program
cpp
cc1
(C preprocessor)
(C compiler)
•
•
expanded
code
expand some kinds of characters
discard whitespace and comments
–
•
compiled
code
each comment is replaced with a single space
process directives:
–
–
–
file inclusion (#include)
macro expansion (#define)
conditional compilation (#if, #ifdef, …)
2
#include
• Specifies that the preprocessor should read in the
contents of the specified file
– usually used to read in type definitions, prototypes, etc.
– proceeds recursively
• #includes in the included file are read in as well
• Two forms:
– #include <filename>
• searches for filename from a predefined list of directories
• the list can be extended via “gcc –I dir”
– #include “filename”
• looks for filename specified as a relative or absolute path
3
#include : Example
where does it come from?
– man 3 printf :
a predefined include file that:
• comes with the system
• gives type declarations,
prototypes for library routines
(printf)
4
#include: cont’d
• We can also define our own header files:
– a header file has file-extension ‘.h’
– these header files typically contain “public” information
• type declarations
• macros and other definitions
• function prototypes
– often, the public information associated with a code file
foo.c will be placed in a header file foo.h
– these header files are included by files that need that
public information
#include “myheaderfile.h”
5
Macros
• A macro is a symbol that is recognized by the
preprocessor and replaced by the macro body
– Structure of simple macros:
#define identifier replacement_list
– Examples:
#define BUFFERSZ 1024
#define WORDLEN 64
6
Using simple macros
• We just use the macro name in place of the value,
e.g.:
#define BUFLEN 1024
#define Pi 3.1416
…
char buffer[BUFLEN];
…
area = Pi * r * r;

NOT:
#define BUFLEN = 1024
#define Pi 3.1416;
7
Example 1
8
Example 2
we can “macroize”
symbols selectively
9
Parameterized macros
• Macros can have parameters
– these resemble functions in some ways:
• macro definition ~ formal parameters
• macro use ~ actual arguments
– Form:
#define macroName(arg1, …, argn) replacement_list
– Example:
#define deref(ptr)
*ptr
no space here!
(else preprocessor will
#define MAX(x,y)
x>y?x:y
assume we’re defining
a simple macro
10
Example
11
Macros vs. functions
• Macros may be (slightly) faster
– don’t incur the overhead of function call/return
– however, the resulting code size is usually larger
• this can lead to loss of speed
• Macros are “generic”
– parameters don’t have any associated type
– arguments are not type-checked
• Macros may evaluate their arguments more than
once
– a function argument is only evaluated once per call
12
Macros vs. Functions: Argument Evaluation
• Macros and functions may behave differently if an
argument is referenced multiple times:
– a function argument is evaluated once, before the call
– a macro argument is evaluated each time it is encountered
in the macro body.
• Example:
int dbl(x) { return x + x;}
…
u = 10; v = dbl(u++);
printf(“u = %d, v = %d”, u, v);
prints: u = 11, v = 20
#define Dbl(x) x + x
…
u = 10; v = Dbl(u++);
printf(“u = %d, v = %d”, u, v);
Dbl(u++)
expands to:
u++ + u++
prints: u = 12, v = 21
13
Properties of macros
• Macros may be nested
– in definitions, e.g.:
#define Pi
3.1416
#define Twice_Pi 2*Pi
– in uses, e.g.:
#define double(x) x+x
#define Pi 3.1416
…
if ( x > double(Pi) ) …
• Nested macros are expanded recursively
14
Pitfalls of nested macros
Oops!
15
What happened?
textual
replacement!
16
Avoiding the problem
17
What happened
18
Header Files
• Have a file extension “.h”
• Contain shared definitions
– typedefs
– macros
– function prototypes
• referenced via “#include” directives
19
Header files: example
20
typedefs
• Allow us to define aliases for types
• Syntax:
typedef
old_type_name
new_type_name;
• new_type_name becomes an alias for old_type_name
• Example:
– typedef int BasePay;
– typedef struct node {
int value;
struct node *next;
} node;
21
Example
defines “wcnode” as an
alias for “struct wc”
we can use “wcnode” in
place of“struct wc”
but not here, since
“wcnode” has not yet
been defined
22
What if a file is #included multiple times?
foo.h
bar1.h
bar2.h
bar.c
23
Example of multiple inclusions
24
Problems with multiple inclusions
25
Solution to multiple inclusion problem
• Use conditional compilation to ensure that a header
file is “really included” at most once
– header file’s responsibility to protect itself from multipleinclusion problems
– uses a conditional-compilation directive #ifndef
– in effect sets a flag when a file is included so we don’t
include it again
– relies on convention
• we need to understand it so we don’t break it
26
Conditional Compilation: #ifdef
#ifdef identifier
line1
…
linen
line1 … linen will be included if
identifier has been defined as a
macro; otherwise nothing will
happen.
#endif
• macros can be defined by the compiler:
– gcc –D macroName
– gcc –D macroName=definition
• macros can be defined without giving them a specific
value, e.g.:
– #define macroName
27
Conditional Compilation: #ifndef
#ifndef identifier
line1
…
linen
#endif
line1 … linen will be
included if identifier
is NOT defined as a
macro; otherwise
nothing will happen.
28
Solution to multiple inclusion problem
The header file is written as follows:
#ifndef file_specific_flag
#define file_specific_flag
…contents of file…
#endif
indicates whether or
not this file has been
included already
• file_specific_flag usually constructed from the name
of the header file:
E.g.: file = foo.h  flag = _FOO_H_
– try to avoid macro names starting with ‘_’
29
Another use of #ifdefs
• They can be useful for controlling debugging output
– Example 1: guard debugging code with #ifdefs:
#ifdef DEBUG
…debug message…
#endif
straightforward, but needs
discipline to use consistently
– Example 2: use the debug macro to control what
debugging code appears in the program:
#ifdef DEBUG
#define DMSG(msg) printf(msg)
#else
#define DMSG(msg) {}
#endif
// debugging output
// empty statement
30
Example 1(a)
31
Example 1(b)
to address the “too many
arguments to macro” problem
“too many arguments” issue
resolved, but the macro
expansion isn’t working quite
the way we want
32
Example 1(c)
33
Generalizing #ifdef
#if constant-expression
line1
…
linen
#endif
Common uses:
• #if 1
or
• #if 0
 line1 … linen included if constant-expression
evaluates to a non-zero value
34