🌠

Preprocessor and Macros

TypeQuiz 4 Material

Notes

The C Preprocessor

The C preprocessor does:

Syntax:

Conventionally, we only include files that end in “.h” —> consist of declarations (including function prototypes) and macro definitions but no executable code

Declare functions before you call them

int foo(int);        —> function prototype(declaration)
int bar(char *);  —> function prototype(declaration)
int main() {
	foo(1);
	bar("Bye")
}

int foo(int i) {
	// stuff
}

int bar(char *s) {
	// stuff
}

Add function declaration above main() since main needs to know about the functions it will call

#Include

If you surround the file name with double quotes(""):

If you surround the file name with angle brackets(<>):

Macro Processing

Macro Processing is a text search-and-replace operation on your source code file

Since macros are literal text substitution, this could be dangerous or lead to unexpected behavior because when directly replacing a macro with code you could run into arithmetic precedence issues (PEMDAS issues)

If you invoke the compiler with gcc –E, it will show you the pre-processed input file with all the includes and macros expanded

How to macros as constant:

How to macros with argument:

Guards

In case of #include the same header file twice, use #ifndef

#ifndef <HEADER_FILE_NAME>_H
#define <HEADER_FILE_NAME>_H
//Contents of header file
#endif

Questions & Answers

  1. What is the preprocessor, and what is its role in C? At what stage does it run in the compilation pipeline?
  1. What does the #include directive do in the preprocessor?
    • Student Solution
      • Conventionally, we only include files that end in “.h”
      • These consist of declarations (including function prototypes)
    • What is a header file? List two reasons why we use header files in C
    • Student Solution
      • In C, you can't use a function/variable before you declare it
      • Header files contain function declarations and global variables
        • They have a .h extension
        • Like the "interface" of what a file exposes
      • Shouldn't include function implementations
    • What kind of code do we usually put in header files?
    • Student Solution
      • Header files typically contain
        • Macro definitions
        • Type Declarations
        • Function Prototypes
        • NOT Code!!!
    • Why shouldn't you #include a .c source file?
    • Student Solution
      • .c files include function implementations, not just declarations. While that may seem innocuous, if you included them, it can be extremely difficult to find if you accidentally included an implementation twice. Including a declaration twice is fine, as long as they're the same. But including an implementation twice is a compiler error that's hard to find.
      • All this is to say that to include .h files, rather than .c files, decreases the likelihood of errors, and makes debugging easier.
  1. What is a macro? What are some cases in which we might want to use a macro?
  1. C does not have particularly safe or powerful macros; its preprocessor macros essentially perform text substitution into your program! How could this be dangerous or lead to unexpected behavior?
  1. Write a macro AVG3 that computes the average of three numbers, evaluating to a double. For example, AVG3(4, 5, 6) should evaluate to 5.0.
    • Watch out for the danger from the point above! Write your macros safely so it doesn't lead to unexpected results
    • Watch out for integer division
  1. Challenge practice question (conceptual, just to test your understanding, harder than what you'll see on the quiz):

    Let's say I define the following macro and the following function:

#define SQUARE(x) ((x)*(x))
double square(double x) { return x * x; }

Can you think of at least two different kinds of scenarios in which using the macro instead of the function leads to different behavior at runtime (i.e. changing square to SQUARE could change the output of the program)?