12.3 Bedingte Kompilierung
 
Zu diesem Unterkapitel muss erwähnt werden, dass viele der beschriebenen Vorgehensweisen nicht dem ANSI C-Standard entsprechen. Da aber Programmierer oft ihre Programme auch gern auf andere Systeme portieren wollen, soll hier dennoch näher auf die Thematik eingegangen werden. Hierzu die Syntax zur bedingten Übersetzung:
#ifdef symbol
#ifdef ( symbol )
#elif symbol
#elif ( symbol )
#else
#endif
Diese Direktiven werden eingesetzt, um zu überprüfen, ob ein Symbol zuvor schon mit #define definiert wurde. Ist symbol definiert, liefern diese Direktiven 1 zurück, ansonsten 0. Abgeschlossen wird eine bedingte Übersetzung mit der Direktive #endif.
Sie haben im vorangegangenen Kapitel schon einen kurzen Einblick (bei der selbst geschriebenen Headerdatei) in die bedingte Kompilierung erhalten. Hierzu ein einfaches Beispiel:
/* clrscr.c */
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
#define clrscr() printf("\x1B[2J")
#elif __BORLANDC__ && __MSDOS__
#include <conio.h>
#elif __WIN32__ || _MSC_VER
#define clrscr() system("cls")
#else
#define clrscr() printf("clrscr() – Fehler!!\n")
#endif
int main(void) {
/* Universale Routine zum Löschen des Bildschirms */
clrscr();
return EXIT_SUCCESS;
}
Hier wird vor der Übersetzung festgelegt, welche Routine zum Löschen des Bildschirms benutzt werden soll. Mit
#ifdef __unix__
überprüft der Präprozessor, ob das Programm auf einem UNIX-artigen System läuft. Wenn das der Fall ist, dann wird mit
#define clrscr() printf("\x1B[2J")
die Routine zum Löschen des Bildschirms definiert, da diese eben nur unter UNIX/Linux funktioniert. Falls es sich nicht um ein UNIX-System handelt, wird mit
#elif __BORLANDC__ && __MSDOS__
überprüft, ob das Programm mit einem Borland-Compiler und unter MS-DOS übersetzt wird. Ist das der Fall, dann wird das Löschen des Bildschirms durch eine in der Headerdatei #include <conio.h> definierte Funktion mit demselben Namen vorgenommen. Anschließend wird überprüft, ob das Programm in einem Win32-Fenster läuft oder mit dem Visual C++-Compiler übersetzt wird.
#elif __WIN32__ || _MSC_VER
#define clrscr() system("cls")
Trifft keiner der geprüften Fälle zu, wird eine entsprechende Ausgabe erzeugt:
#else
#define clrscr() printf("clrscr()-Fehler!!\n")
Abgeschlossen wird diese bedingte Kompilierung mit
#endif
Durch die bedingte Kompilierung besteht die Möglichkeit, Programme einfacher auf andere Systeme zu portieren. Die bedingte Kompilierung lässt sich auch anders verwenden:
/* t_system.c */
#include <stdio.h>
#include <stdlib.h>
#ifdef __MSDOS__
int main(void) {
printf("Programm läuft unter MSDOS \n");
return EXIT_SUCCESS;
}
#elif __WIN32__ || _MSC_VER
int main(void) {
printf("Programm läuft unter Win32\n");
return EXIT_SUCCESS;
}
#elif __unix__ || __linux__
int main(void) {
printf("Programm läuft unter UNIX/LINUX\n");
return EXIT_SUCCESS;
}
#else
int main(void) {
printf("Unbekanntes Betriebssystem!!\n");
return EXIT_SUCCESS;
}
#endif
Hier wurden mehrere main()-Funktionen verwendet. Auf dem System, für das die bedingte Kompilierung gilt, wird die entsprechende main-Funktion auch ausgeführt.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 12.2
Bedingte Kompilierung – Programm läuft unter Win32
 Hier klicken, um das Bild zu Vergrößern
Abbildung 12.3
Bedingte Kompilierung – Programm läuft unter Linux/UNIX
Sie können die bedingte Kompilierung mit if-else if–else-Abfragen vergleichen. Um Compiler-spezifische Abfragen zu tätigen, gibt es folgende Compiler-Konstanten:
Tabelle 12.3
Konstanten für bestimmte Compiler
Konstante
|
Compiler
|
MSC_VER
|
Microsoft C ab Version 6.0
|
QC
|
Microsoft Quick C ab Version 2.51
|
TURBOC
|
Borland Turbo C, Turbo C++ und BC++
|
BORLANDC
|
Borland C++
|
ZTC
|
Zortech C und C++
|
SC
|
Symantec C++
|
WATCOMC
|
WATCOM C
|
GNUC
|
Gnu C
|
EMX
|
Emx Gnu C
|
Es wird nochmals darauf hingewiesen, dass diese Konstanten nicht vom ANSI C-Gremium vorgeschrieben sind.
Für die bedingte Kompilierung mit Betriebssystemen finden sich folgende Konstanten:
Tabelle 12.4
Konstanten für bestimmte Betriebssysteme
Konstante
|
Betriebssystem
|
unix__ oder __unix
|
UNIX-System
|
MS_DOS
|
MS-DOS
|
WIN32
|
Windows ab 95
|
OS2
|
OS2
|
Windows
|
Zielsystem Windows
|
NT
|
Windows NT
|
linux
|
Linux
|
FreeBSD
|
FreeBSD
|
OpenBSD
|
OpenBSD
|
SGI_SOURCE
|
SGI-IRIX mit Extension *.sgi
|
MIPS_ISA
|
SGI-IRIX
|
hpux
|
HP-UX
|
Es gibt sicherlich noch weitere Konstanten. Die hier genannten zählen zu den gängigsten. Sehen Sie sich ein anderes Programmbeispiel dazu an:
/* sektor.c */
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__ || linux
#define SEKTORSIZE 4096
#elif __MSDOS__ || __WIN32__ || _MSC_VER
#define SEKTORSIZE 512
#else
#define SEKTORSIZE 0
#endif
void sect(long size) {
long kb,s=SEKTORSIZE;
if(s == 0)
printf("Unbekanntes System\n");
else if(s==4096)
printf("UNIXsystem : ");
else
printf("DOS/Win32 : ");
kb = size * s;
printf("%ld Sektoren = %ld B\n", size, kb);
}
int main(void) {
long sector;
printf("Wie viele Sektoren: ");
scanf("%ld",§or);
sect(sector);
return EXIT_SUCCESS;
}
Dies ist ein Beispiel zum Thema Portabilität. Auf MS-DOS/Win32 beträgt die Größe eines Sektors auf der Festplatte 512 KBytes. Auf UNIX-Systemen hingegen meist 4096 KBytes (unter BSD gewöhnlich 1024 KBytes) pro Sektor. Sie müssen nur am Anfang des Programms dem Präprozessor die Anweisung geben, für welches System er die Größe einer bestimmten Anzahl von Sektoren ausgeben soll.
Folgende Schreibweisen sind im Übrigen identisch:
#ifdef MAKRO
/* ist identisch mit */
#if defined MAKRO
Des Weiteren gibt es eine Direktive, die es ermöglicht, zu überprüfen, ob etwas nicht definiert wurde:
#ifndef __STDIO_H
#define __STDIO_H
#endif
Hier überprüft der Präprozessor, ob er die Headerdatei <stdio.h> noch nicht eingebunden hat.
Das ist zum Beispiel erforderlich, wenn mehrere Headerdateien und Module benutzt werden, die <stdio.h> benötigen. Somit würden alle Makros in der Headerdatei <stdio.h> mehrmals definiert werden, was im schlimmsten Fall sogar einen Fehler auslösen kann. Mit der eben geschriebenen Struktur wird dieses vermieden.
Auch zu dieser Direktive gibt es eine alternative Schreibweise:
#ifndef MAKRO
/* ist dasselbe wie */
#if !defined MAKRO
|