C Tutorial
C Preprocessor
#include, #define, and #ifdef run before the compiler. They edit the source text.
Text in, text out
Lines that start with # are preprocessor directives. They run before gcc compiles. The preprocessor does not know C types. It pastes files, replaces names with tokens, and keeps or drops chunks of text. The compiler then sees the edited result.
That is why a missing header is a preprocessor problem first, and why a #define can surprise you: it is find-and-replace, not a typed constant.
#include pastes a file
#include <stdio.h> asks the preprocessor to insert the contents of that header. After the paste, the compiler can see the declaration of printf. Angle brackets look in the system include path. Quotes look next to your source first: #include "point.h".
Example
#include <stdio.h>
int main(void) {
printf("stdio.h was pasted in\n");
return 0;
}Without that line, printf is an unknown name. The include does not run the program. It only grows the text the compiler reads.
#define replaces a name
#define MAX 3 tells the preprocessor: wherever you see the token MAX, write3 instead. After that pass, the compiler sees a literal 3. There is no MAX variable at run time.
Example
#include <stdio.h>
#define MAX 3
int main(void) {
int nums[MAX];
nums[0] = 10;
nums[1] = 20;
nums[2] = 30;
for (int i = 0; i < MAX; i++) {
printf("%d\n", nums[i]);
}
return 0;
}Prefer const int max = 3; when you want a typed object. Use #define for include guards and for flags the rest of the preprocessor must see, such as #ifdef.
#ifdef keeps or drops text
#ifdef DEBUG is true if DEBUG has been defined, even as an empty name.#endif ends the block. If the name is not defined, the lines in between never reach the compiler.
Example
#include <stdio.h>
#define DEBUG
int main(void) {
#ifdef DEBUG
printf("debug build\n");
#endif
printf("hello\n");
return 0;
}With #define DEBUG present, both lines print. Delete that define (or comment it out) and onlyhello remains. #ifndef is the inverse: keep the block when the name isnot defined. Header guards use that form.
It is not the compiler
| Directive | What the preprocessor does |
|---|---|
#include | Paste another file into this one |
#define | Replace a name with tokens |
#ifdef / #endif | Keep or drop a region of text |
Directives end at the newline. They do not use a semicolon. A mistake here shows up as a strange compile error later, because the compiler never saw the text you thought you wrote.
Run these at /c/try. Comment out #define DEBUG and compile again. That editor is gcc for C.
Next: header files, where #include and include guards earn their keep.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C editor at /c/try. Change one measurement and check whether the result still has the right unit.
Physics
g as a macro
#define G 9.81 pastes 9.81 into the source before gcc type-checks. ½ × 9.81 × 3² is 44.145 m from rest. A const double G is usually clearer. Macros still show up in older headers and in this chapter.
s = ½ G t²
Example
#include <stdio.h>
#define G 9.81
int main(void) {
double t = 3.0;
printf("s = %.2f m\n", 0.5 * G * t * t);
return 0;
}Chemistry
Avogadro’s number
One mole is 6.022×10²³ specified entities. Half a mole of anything is about 3.011×10²³ of those entities. The number is the same for electrons or molecules; what changes is what you are counting.
N = n × N_A
Example
#include <stdio.h>
#define NA 6.022e23
int main(void) {
double moles = 0.5;
printf("molecules ~ %.3e\n", moles * NA);
return 0;
}