The Preprocessor#
(Chapter 8)
+-----------+ +---------+ +--------+
--------> | hello.c | --------> | hello.o | --------> | hello |
edit +-----------+ compile +---------+ Linking +--------+
Keywords Preprocessor
Identifiers Lexical analysis
Constants Syntax analysis
Operators Semantic analysis
Punctuation
Basic Commands#
#include <stdio.h>
#include “myfile.h”
---------------------------------
#define identifier string
#undef identifier
#define#
#define PI 3.14159
#define C2 99792.458 /*the speed of light*/
#define EOF (-1)
#define MAXINT 2147483647
#define EQ ==
#define do /* blank */
while (i EQ 1) do {
......
Macros#
#define SQ(x) ((x) * (x))
SQ(7 + w) /*expands to */ ((7 + w) * (7 + w))
SQ(SQ(*p)) /*expands to */ ((((*p) * (*p))) * (((*p) * (*p))))
#define SQ(x) x * x
SQ(a + b) /*expands to */ a + b * a + b
#define SQ(x) (x) * (x)
4 / SQ (2) /*expands to */ 4 / (2) * (2)
#define SQ (x) ((x) * (x))
SQ(7) /*expands to */ (x) ((x) * (x)) (7)
#define SQ(x) ((x) * (x)); /*Be careful!!!*/
/*because*/
if (x == 2)
x = SQ(y);
else
++x;
Nested Macros#
#define min(x, y) (((x) < (y)) ? (x) : (y))
m = min(u, v) /*expands to */ m = (((u) < (v)) ? (u) : (v))
#define min4(a, b, c, d) min(min(a,b), min(c,d))
You can stop the compiler after the preprocessing step and view the code:
gcc -E tmp.c
# @title
%%writefile tmp.c
#include <stdio.h>
#define SQUARE(x) ((x) * (x))
int main() {
printf("%d\n", SQUARE(5));
return 0;
}
Cell In[1], line 6
int main() {
^
SyntaxError: invalid syntax
# @title
!gcc tmp.c -o tmp
!./tmp
More about Macros#
Macros are not functions Even when written with all the parentheses
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
int a[4] = {0};
int biggest = 0;
biggest = x[0];
i = 1;
while (i < 4)
biggest = MAX(biggest, x[i++]);
This code would have worked fine if MAX was a function !
Expands to:
biggest = (((biggest) > (x[i++])) ? (biggest) : (x[i++]));
i is incremented twice when the condition (biggest > x[i++]) is false, and only once when it is true.
Conditional Compilation#
#if constant_integral_expression
#ifdef identifier
#ifndef identifier
#endif
Conditional Compilation (example 1)#
#if defined ( HP9000 ) || defined ( SUN4 ) && !defined( VAX )
/*Machine-dependent code*/
#endif
Conditional Compilation (example 2)#
#define DEBUG
#ifdef DEBUG
printf( “debug: a = %d\n”, a );
#endif
Conditional Compilation (example 3)#
#ifndef __HEADER_H
#define __HEADER_H
.
.
.
#endif
Conditional Compilation#
statement
#if 0
more statements
#endif
and still more statements
------------------------------------------
#elif constant_interal_expression
#else
#endif
Predefined Macros#
Four predefined macros (can’t be undefined) :
__DATE__ A string containing the current date
__FILE__ A string containing the file name
__LINE__ An integer representing the current line number
__TIME__ A string containing the current time
Header Files#
What are Header Files?#
Header files (.h files) contain declarations of functions, macros, constants, and structures. They allow for code reuse and modularity by separating the interface (declarations) from the implementation (definitions).
Key Points:#
Function Prototypes: Declare functions that will be defined in other source files.
Macros and Constants: Define reusable code snippets or constants.
Include Guard: Prevents multiple inclusions of the same header file.
Example: Header File (math_functions.h)#
#ifndef MATH_FUNCTIONS_H // Include guard to prevent multiple inclusions
#define MATH_FUNCTIONS_H
// Function prototypes
int add(int a, int b);
int subtract(int a, int b);
#endif // MATH_FUNCTIONS_H
Conditional Compilation in C#
What is Conditional Compilation?#
Conditional compilation allows for compiling code conditionally based on certain preprocessor directives like #define, #ifdef, #ifndef, and #endif. It is useful for including or excluding code depending on different conditions (e.g., platform-specific code, debugging).
Key Points:#
#define: Defines a constant or flag for conditional checks.#ifdef/#ifndef: Checks if a macro is defined or not.#endif: Ends a conditional block.
Example: Conditional Compilation with Debug Flag#
We will modify the program to conditionally include a debug message.
1. Header File (math_functions.h)#
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H
// Function prototypes
int add(int a, int b);
int subtract(int a, int b);
#endif // MATH_FUNCTIONS_H
// Conditionally include debugging
#define DEBUG // Comment this line to disable debug messages
2. Implementation File (math_functions.c)#
#include "math_functions.h"
#include <stdio.h>
// Function definitions
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
3. Main File (main.c)#
#include <stdio.h>
#include "math_functions.h"
int main() {
int sum = add(5, 3);
int difference = subtract(5, 3);
// Conditional Debug Output
#ifdef DEBUG
printf("Debug: Sum is %d\n", sum);
#endif
printf("Sum: %d\n", sum);
printf("Difference: %d\n", difference);
return 0;
}
Explanation:#
#define DEBUG: This flag enables the debug message.#ifdef DEBUG: IfDEBUGis defined, the debug message will be included in the compilation.No
DEBUGflag: If you comment out#define DEBUG, the debug message will be excluded from the compilation.
Compilation Steps#
To compile and link the program:
Compile and link everything in one command:
gcc main.c math_functions.c -o program
Run the program:
./program
Assert#
#include <stdio.h>
#include <stdlib.h>
#if defined(NDEBUG)
#define assert(ignore) ((void)0) /* ignore it */
#else
#define assert(expr) \
do \
{ \
if (!(expr)) \
{ \
printf("\n%s %s\n%s %s\n%s %d\n\n", \
"Assertion failed:", #expr, \
"In file:", __FILE__, \
"At line:", __LINE__); \
abort(); \
} \
} while (0)
#endif