
Der Präprozessor ist ein unsichtbarer Helfer – er läuft bevor der Compiler deinen Code überhaupt sieht! Er ersetzt Text, bindet Dateien ein und kann Code-Teile ein- oder ausblenden. Das ist mächtiger als es klingt!
Was ist der Präprozessor?
Bevor der eigentliche Compiler dein C-Programm übersetzt, läuft der Präprozessor darüber. Er verarbeitet alle Zeilen die mit # beginnen – sogenannte Direktiven. Der Präprozessor macht nur reine Textersetzung – er kennt keine Typen, keine Variablen, keine Logik.
Die 3 Phasen der Kompilierung
| Phase | Was passiert |
|---|---|
| Präprozessor | #define und #include werden expandiert, Kommentare entfernt |
| Compiler | C-Quellcode wird in Maschinencode übersetzt (.o-Datei) |
| Linker | Einzelne Objektdateien werden zur ausführbaren .exe zusammengefügt |
#define – Konstanten
Mit #define kannst du benannte Konstanten definieren. Jedes Vorkommen des Namens im Code wird vor dem Compilieren durch den Wert ersetzt. Konvention: Namen immer in GROSSBUCHSTABEN.
#define PI 3.14159 #define MAX_GROESSE 100 #define BEGRUESSUNG "Hallo HTL!" #include <stdio.h> int main() { float radius = 5.0; float flaeche = PI * radius * radius; // Präprozessor macht daraus: // float flaeche = 3.14159 * radius * radius; printf("Flaeche: %.2f\n", flaeche); printf("Max: %d\n", MAX_GROESSE); return 0; }
✅ #define – Vorteile
- Kein Speicher verbraucht
- Kein Typ (universell einsetzbar)
- Reine Textersetzung – sehr schnell
- Kann nicht versehentlich verändert werden
const – sichere Alternative
const float PI = 3.14159f;- Hat einen Typ – der Compiler prüft!
- Nimmt Speicher (wie eine Variable)
- Besser für komplexe Typen
#define – Makros mit Parametern
Makros können auch Parameter haben – sie sehen aus wie Funktionen, sind aber reine Textersetzung. Immer extra Klammern um jeden Parameter und um den gesamten Ausdruck!
#define QUADRAT(x) ((x)*(x)) #define MAX(a,b) ((a)>(b) ? (a) : (b)) #define MIN(a,b) ((a)<(b) ? (a) : (b)) int main() { printf("%d\n", QUADRAT(5)); // → 25 printf("%d\n", QUADRAT(3+2)); // → 25 (korrekt!) printf("%d\n", MAX(7, 3)); // → 7 printf("%d\n", MIN(7, 3)); // → 3 return 0; }
⚠️ Die Klammer-Falle
Ohne Klammern um den Parameter passiert etwas Unerwartetes:
QUADRAT_FALSCH(3+2)
// wird zu: 3+2*3+2 = 11 ← FALSCH!
#define QUADRAT_RICHTIG(x) ((x)*(x))
QUADRAT_RICHTIG(3+2)
// wird zu: ((3+2)*(3+2)) = 25 ← Richtig!
#include – Bibliotheken und eigene Header
Mit #include fügt der Präprozessor den Inhalt einer anderen Datei wörtlich ein – als ob du den Code direkt dort geschrieben hättest.
#include <stdio.h> // Systemheader: <spitze Klammern> #include <stdlib.h> // Sucht in Systempfaden (z.B. /usr/include) #include <math.h> // Mathematische Funktionen #include "mein_header.h" // Eigene Datei: "Anführungszeichen" #include "rechner.h" // Sucht zuerst im aktuellen Verzeichnis
<spitze Klammern>
- Für Systembibliotheken (stdio, stdlib, math...)
- Sucht in System-Include-Pfaden
- Beispiel:
#include <stdio.h>
"Anführungszeichen"
- Für eigene Header-Dateien
- Sucht zuerst im aktuellen Verzeichnis
- Beispiel:
#include "rechner.h"
#ifndef – Include Guards
Problem: Wenn zwei Dateien denselben Header einbinden, wird er doppelt eingefügt – das führt zu Fehlern! Die Lösung sind Include Guards:
#ifndef MEIN_HEADER_H // Falls MEIN_HEADER_H NICHT definiert ist... #define MEIN_HEADER_H // ...dann definiere es jetzt /* Alle Deklarationen hier */ int addiere(int a, int b); float kreis_flaeche(float r); #endif // Ende des Guards
Moderne Alternative: #pragma once
#pragma once am Anfang der Header-Datei macht dasselbe wie Include Guards – kürzer und moderner. Wird von allen gängigen Compilern unterstützt, ist aber technisch kein C-Standard.
/* Alle Deklarationen hier – kein #endif nötig! */
Bedingte Kompilierung
Mit #ifdef / #ifndef / #endif kann man Code-Teile je nach Konfiguration ein- oder ausblenden – z.B. für Debug-Ausgaben oder plattformspezifischen Code.
#include <stdio.h> #define DEBUG // Kommentiere das aus um Debug zu deaktivieren! int main() { int x = 42; #ifdef DEBUG printf("[DEBUG] x = %d\n", x); #endif printf("Ergebnis: %d\n", x * 2); return 0; }
#ifdef _WIN32 printf("Laueft auf Windows!\n"); #else printf("Laueft auf Linux/Mac!\n"); #endif
Mit dem Compiler-Flag gcc -DDEBUG programm.c kann man DEBUG auch von der Kommandozeile aus definieren – ohne den Quellcode zu ändern!
Häufige Fehler
Fehler 1: Semikolon in #define
// x = PI; wird zu: x = 3.14;; ← doppeltes Semikolon!
Fehler 2: Fehlende Klammern in Makros
DOPPELT(3+4) // wird zu: 3+4*2 = 11 statt 14!
Fehler 3: Makros mit Seiteneffekten
// wird zu: ((i++)>(j++) ? (i++) : (j++))
// i++ oder j++ wird ZWEIMAL ausgewertet!
Merksatz: #define ist blindes Text-Copy-Paste
Der Präprozessor sieht keine Typen, prüft keine Syntax und versteht keine Logik. Er kopiert und fügt Text ein. Alles Weitere ist Compiler-Aufgabe.
⚡ Code-Simulator
Probiere den Präprozessor aus! Ändere die #define-Werte und beobachte wie sich die Ausgabe ändert:

Ändere den Wert von PI auf 3 und schau was passiert. Dann probier QUADRAT(3+2) mit und ohne Klammern – der Unterschied ist dramatisch!
🎯 Wissens-Quiz
#define MAX 100?#define QUADRAT(x) ((x)*(x)))#include <stdio.h> und #include "mein.h"?#pragma once?#define DOPPELT(x) x*2 bei DOPPELT(3+4) aus?📋 Spickzettel – Präprozessor
✅ Checkliste Unit 32
- Ich weiß was der Präprozessor macht und wann er läuft
- Ich kann #define-Konstanten mit GROSSBUCHSTABEN definieren
- Ich schreibe Makros mit korrekten Klammern um jeden Parameter
- Ich kenne den Unterschied zwischen <> und "" bei #include
- Ich kann einen Include Guard korrekt schreiben
- Ich nutze #ifdef für bedingte Kompilierung (Debug-Modus)