Macros in C/C++ Computer Science and Engineering College of Engineering The Ohio State University Lecture 33
Macro Definition Directive: #define #define <name> <body> Example: #define BUFF_SIZE 1000 A newline ends the macro body Macros can only be 1 line long Work-around: For longer bodies, use the line continuation character, \ Naming convention: SCREAMING_SNAKE_CASE Common use: compile-time constant int Buffer[BUFF_SIZE]; Also: #undef
GNU gcc Toolchain Run only macro preprocessor: -E $ gcc E test.c > test.i Standard file extension for preprocessed C (C++) is.i (.ii) More than just expanding macros: Begins by stripping comments Concatenates lines marked with \ May collapse white space Adds markers for filename and line numbers to help connect later errors to original location in source file
Text Substitution Gcc preprocessor doesn't really care that input is a C program Theoretically can be used on any text file Demo: ulisse.c, ulisse.ii Caveat: The preprocessor does rely on proper C conventions for tokenizing Apostrophes will mess things up In practice: preprocessing something other than C/C++ files with gcc is a hack, it is brittle, and it is probably a bad idea
Algorithmic Highlights Single pass Forward references are not expanded Nested invocation is permitted Expanded body is rescanned for more invocations Nested definitions are not permitted No recursion or mutual recursion Self references are not further expanded Circularity (mutual recursion) handled similarly: First cyclic reference is not expanded
Nesting Forward References Input foo = X; #define X 4 bar = X; Input #define SIZE BUFF #define BUFF 1024 Mem[SIZE]; Output foo = X; bar = 4; Output Mem[1024];
Still Single Pass Input #define BUFF 1024 #define SIZE BUFF #undef BUFF #define BUFF 2048 Mem[SIZE]; Output?
Macro Arguments Argument list follows name, without spaces #define <name>(<arglist>) <body> Example #define INC(X) X++ #define SUM(X,Y) X+Y Danger: Arithmetic grouping Macro is doing text substitution, not programming language call Scope issues may cause surprising problems
Problem: Unprotected Body Recall ternary operator ( _? _ : _ ) Consider #define MAX(X,Y) X > Y? X : Y Do these work? a = MAX(b,c); a = MAX(b,c) + 1; Result of expansion: a = b > c? b : c; a = b > c? b : c + 1; Solution: protect the body #define MAX(X,Y) (X > Y? X : Y)
Problem: Unprotected Args Consider another use of MAX macro flag = MAX(b<0,c<0); A disjunction of the two conditions? Result of expansion: flag = (b<0 > c<0? b<0 : c<0); Solution: protect the arguments #define MAX(X,Y) ((X) > (Y)?(X):(Y)) flag = ((b<0) > (c<0)?(b<0):(c<0));
Conditional Expansion Common condition is this macro (not) defined? #ifndef BUFF_SIZE #define BUFF_SIZE 1024 #endif /*BUFF_SIZE*/ Application: debugging modes #define DEBUG_ON... #ifdef DEBUG_ON printf (... ); #endif /*DEBUG_ON*/ Advantage: No space/time overhead Disadvantage: Change requires recompiling
File Inclusion Syntax #include "<filename>" Effect: Contents of <filename> inserted at that point #include f
Danger: Repeated Inclusion Multiple inclusion of the same file can lead to conflicts, eg double declaration File f1 includes lib1 and lib2 File lib1 includes lib2 Result: f1.i contains two copies of lib2 In the extreme: recursive inclusion File f1 includes f2 File f2 includes f1
Solution: Conditional Protect every file that will be included by other file(s) Convention Wrap entire file in #ifndef #endif Inside, define a unique macro (1/file) Example, file cat.h #ifndef CAT_H_PAGS #define CAT_H_PAGS... #endif /*CAT_H_PAGS*/
Predefined Macros Macros provided by the preprocessor Some are part of ANSI language standard FILE : current file name LINE : this line number in current file DATE / TIME : current date/time Some are compiler-specific (eg gcc) VERSION BASE_FILE INCLUDE_LEVEL Example use: Error or debug messages printf("error in %s, line %d\n", FILE, LINE );
Using Arguments in Strings ANSI C: Parameter substitution is not performed within quoted strings Example #define DISP(EXP) \ printf("exp = %d\n", EXP) DISP(i*j+1) Result of expansion: printf("exp = %d\n", i*j+1) Solution: "Stringizing" operator, # #define DISP(EXP) \ printf(#exp " = %d\n", EXP) Result of expansion: printf("i*j+1" " = %d\n", i*j+1)
Pitfall: Side effects Macros look like function calls Text substitution means semantics are not the same as function calls Example: Recall MAX macro #define MAX(X,Y) ((X) > (Y)?(X):(Y)) a = MAX(b++, c++) If b = 2, and c = 5 beforehand, what is the result? // a =, b =, c =
Pitfall: Swallow the Semicolon Macro consists of compound statement #define INC(X,Y) {X++; Y++} Tempting to use macro with semicolon INC(a, b); However, consider: if ( ) INC(a, b); else Result: compile-time error Solution: (notice missing semicolon) #define INC(X, Y) \ do { X++; Y++ } while(0)
Summary Macro definition with #define Single pass No forward references No nested definitions Nested invocations expanded (no cycles) Arguments are permitted Conditional expansion #ifdef File inclusion #include Coding idioms to prevent some mistakes Protect the body, protect the arguments Avoid arguments with side effects