Anfang   weiter

1. Der Präprozessor

Bevor der Compiler das C-Programm übersetzt, wird zuerst das Programm automatisch durch den sogenannte Präprozessor verarbeitet. Dieser Teil des C-Compilers bearbeitet die Anweisungen, die mit einem # beginnen und dient hauptsächlich der Texteinfügung (Einbindung der Headerdateien) und -ersetzung (Ersatz symbolischer Konstanten). Neben der Einbindung der Headerdateien und dem Ersetzen symbolischer Konstanten kann der Präprozessor aber noch mehr: Eine Präprozessoranweisung kann an jeder beliebigen Stelle im Quelltext vorkommen und ist ab dieser Stelle gültig.

Liste der Präprozessoranweisungen
Kommando Bedeutung
#include Bindet einen Dateiinhalt ein
#define definiert eine symbolische Konstante oder ein Makro
#undef Hebt eine Definition wieder auf
#if Steuerungsanweisungen für die bedingte Übersetzung von Programmtext
#elif  
#else oder Zweig
#endif Ende für Steuerungsanweisungen
#ifdef Abfrage, ob bestimmte Definitionen erfolgten
#ifndef Abfrage, ob bestimmte Definitionen nicht erfolgten
#line Verändert die Nummerierung der aktuellen Programmtextzeile
#pragma Herstellerspezifische Aktionen

Dateieinbindung
Die #include Anweisung ist eine der meist genutzten Möglichkeiten des Präprozessors. Fast alle Programme beginnen mit einer Zeile wie:
#include <stdio.h>
oder
#include "local.h"
Der Unterschied in der Schreibweise bedeutet, dass im ersten Fall, die Datei im Standardverzeichnis der Headerdateien des Compilers gesucht wird. Steht der Dateiname in doppelten Anführungszeichen ist der Pfad zur Datei explizit anzugeben. Bei manchen Compilern wird auch im aktuellen Verzeichnis und den Verzeichnissen, die in der Pfadvariablen angegeben sind, nachgesehen. Die Art der Dateieinbindung kann am leichtesten an Hand eines Beispieles erläutert werden:
/* text.c */
#include <stdio.h>
int main(void)
{
printf
#include "text2.c"
;
return 0;
}

/* text2.c */
("Das ist der Ausgabetext")
Bei der Übersetzung der Datei text.c wird sobald der Präprozessor auf die Anweisung #include text2.c trifft, der Inhalt der Datei text2.c an dieser Stelle eingefügt. Der Präprozessor erstellt eine temporäre Datei mit dem Inhalt:
int main(void)
{
printf
("Das ist der Ausgabetext")
;
return 0;
}
die anschließend vom Compiler übersetzt wird. Da C ein formatfreie Programmiersprache ist, kann die Formatierung des Quelltextes relativ frei gewählt werden, so dass die Zeilenschaltung zwischen printf und dem Klammerausdruck kein Problem darstellt. Dagegen ist die Zeilenschaltung nach #include "text2.c" unbedingt notwendig, da die Präprozessoranweisungen ohne Strichpunkt einzugeben sind.

In den Standardheaderdateien befindet sich normalerweise kein Programmcode. So enthält die include Datei stdio.h nur Definitionen und verändert daher den Programmdateiinhalt im obigen Beispiel nicht.

Symbolische Konstanten und Makros
Symbolische Konstanten

Mit der #define Anweisung können Zeichenketten im Programm durch eine andere ersetzt werden und ermöglichen den Einsatz symbolischer Konstanten und Makros. Der Einsatz symbolischer Konstanten entsteht aus dem Wunsch den Programmtext möglichst einfach lesbar, aber auch einfach wartbar zu halten. So kann in einem Programm das etwa Rechnungen (kaufmännischer Art, also inkl. MWSt.) erstellt, über die Definition

#define MWST 20
im ganzen Programm mit der symbolischen Konstanten MWST gearbeitet werden. Da die Definition dieser Konstanten nur an einer einzigen Stelle des Quelltextes auftaucht, braucht nur die #define Anweisung abgeändert zu werden, falls der Mehrwersteuersatz (etwa im Rahmen einer Steuerreform) verändert wird. Das übrige Programm bleibt ungeändert.

Makros
Neben dem Einsatz symbolischer Konstanten erlaubt die #define Direktive des Präprozessors auch die Definition sogenannter Makros. Dabei wird bei einem Makro ebenfalls der Textersetzungsmechanismus des Präprozessors verwendet. Mit den Definitionen

#define BEGIN {
#define END }
#define program 
#define UEB main()
#define WriteLn printf
lässt sich folgendes kurzes Programm auch mit dem C-Compiler kompilieren, obwohl es wie ein, leider nicht ganz syntaktisch richtiges, Pascal Programm aussieht:
program UEB
BEGIN
  WriteLn("Hallo, was jetzt?");
END
Wesentlich effektiver sind Makros, die mit Parametern ausgestattet werden. Mit
#define SQUARE(value) ((value)*(value))
wird ein Makro mit dem Namen SQUARE deklariert, das einen Parameter, value genannt, übernimmt. Innerhalb eines Programmes wird dann die Zeile
result = SQUARE(x);
durch
result = ((x)*(x));
ersetzt. Wichtig ist dabei die Klammerung der Argumente in der Definition, denn
result = SQUARE(x + y);
wird durch
result = ((x + y)*(x + y));
ersetzt und liefert damit auch ein richtiges Ergebnis. Ohne die Klammern um die Argumente würde im zweiten Beispiel die Ersetzung folgendermaßen fehlerhaft erfolgen:
result = (x + y*x + y);
Ein großer Vorteil solcher Funktions-Makros ist, daß die Argumente der Funktions-Makros nicht Typ-sensitiv sind, da durch die Textersetzung vor der Kompilation jeweils die Variablen des enstprechenden Typs in den Quelltext eingefügt werden.

Bedingte Übersetzung

Ein weiterer Einsatzbereich des Präprozessors ist die Steuerung des Übersetzungsprozesses. Zum Einsatz kommt diese Möglichkeit, wenn ein Programm z. B. für verschiedene Betriebssysteme gedacht ist, oder um bei einer Fehlersuche bestimmte Sequenzen des Programmes ablaufen zu lassen oder ausblenden zu können. Die Ablaufsteuerung erfolgt über Verzweigungsausdrücke ähnlich denen in C. Mit #if ... #endif wird ein Ausdruck auf seinen "Wahrheitsgehalt" getestet. Ist er wahr, wird der unmittelbar auf das #if folgende Teil ausgeführt, ansonst alles bis zum #endif ausgelassen, oder sollte ein #else Teil vorhanden sein, mit diesem weitergemacht.

#include <stdio.h>
#define DEBUG 1

int main(void)
{
#if DEBUG
 printf("Testversion");
#endif
return 0;
}
Im obigen Programm wird Testversion am Bildschirm ausgegeben, während das Programm bei
#define DEBUG 0
keine Ausgabe liefert. Auf diese Weise kann man sich zu Testzwecken kritische Variablen eines Programmes etc. ausgeben lassen, und in der Endversion dann diese Teile auslassen, ohne jene Stellen, wo die Ausgabeanweisungen stehen, extra löschen zu müssen.


weiter mit einigen Ein- Ausgabe Funktionen