10. Präprozessor-Befehle
Präprozessor-Befehle (auch Compilerdirektiven genannt) sind Anweisungen
an den Präprozessor des Compilers. Der Präprozessor ist dem Compiler vorgeschaltet,
d.h. bei jeder Compilierung wird erst der Präprozessor gestartet, der an ihn gerichtete
Befehle im Quellcode sucht und verarbeitet. Damit steuert der Präprozessor den
Compilierungsvorgang.
Präprozessor-Befehle beginnen stets mit # am Zeilenanfang. Im
Gegensatz zu Anweisungen in C/C++ enden sie aber nicht mit einem Semikolon! In der
folgenden Tabelle sind alle Präprozessor-Befehle aufgelistet:
Präprozessor-Befehl |
Bedeutung |
#include |
Fügt Text aus einer anderen Quelltextdatei ein. |
#define |
Definiert ein Makro. |
#undef |
Entfernt ein Makro. |
#if |
Bedingte Compilierung in Abhängigkeit der nachstehenden Bedingung. |
#ifdef |
Bedingte Compilierung in Abhängigkeit, ob ein Makroname definiert ist. |
#ifndef |
Bedingte Compilierung in Abhängigkeit, ob ein Makroname nicht definiert ist. |
#else |
Alternative Compilierung, wenn die Bedingung des vorstehenden #if, #elif, #ifdef oder #ifndef nicht erfüllt ist. |
#elif 2 |
Alternative Compilierung in Abhängigkeit der nachstehenden Bedingung, wenn die Bedingung des vorstehenden #if, #elif, #ifdef oder #ifndef nicht erfüllt ist (also eine Art Kombination aus #else und #if). |
#endif |
Beendet den Block mit der bedingten Compilierung. |
#line |
Setzt die Zeilennummer für Compiler-Meldungen. |
defined 1 |
Liefert eine 1, wenn der nachstehende Makroname definiert ist, sonst eine 0. Kann nur zusammen mit #if und #elif verwendet werden. |
#-Operator 1 |
Ersetzt innerhalb eines Makros einen Makroparameter durch eine konstante Zeichenkette, die den Wert des Parameters enthält. |
##-Operator 1 |
Erzeugt ein einzelnes Token aus zwei hintereinander stehenden Tokens. |
#pragma 1 |
Gibt Compiler- und System-abhängige Infomationen an den Compiler. |
#error 1 |
Erzeugt einen Fehler während der Compilierung mit der angegebenen Fehlermeldung. |
1 Neu in C99.
2 Gehört nicht zum Standard-C, obwohl es von vielen Compilern unterstützt wird.
Der Präprozessor entfernt üblicherweise alle Zeilen mit Präprozessor-Befehlen aus
dem Quelltext und führt die entsprechenden Befehle aus. Der daraus entstehende Quelltext
muss ein gültiges C-Programm ergeben.
Eine Zeile, in der (außer weißen Leerzeichen) nur ein # steht,
wird wie eine Leerzeile behandelt. Ältere C-Compiler können hier Fehlermeldungen
liefern.
Im Standard-C werden Leerzeichen, die vor dem # sowie zwischen
# und dem Präprozessor-Befehl stehen, ignoriert. Ältere C-Compiler
ignorieren diese Leerzeichen unter Umständen nicht. Daher sollte an diesen Stellen auf
Leerzeichen verzichtet werden.
Alle Präprozessor-Zeilen werden vor der Textersetzung der Makros erkannt. Daher kann ein
Makro nicht einen anderen Präprozessor-Befehl erzeugen. Das folgende Beispiel erzeugt eine
Fehlermeldung beim Compilieren.
Beispiel:
#define GETMATH #include <math.h>
GETMATH
Der Präprozessor ersetzt (erst nachdem er alle Präprozessor-Befehle erkannt hat) das
GETMATH durch
#include <math.h>.
Diese Zeile bleibt dann für die Compilierung so stehen und erzeugt während der
Compilierung einen Fehler.
10.1. #include
Bereits bekannt ist die #include-Anweisung. Mit ihr wird an der
aktuellen Stelle eine externe Datei eingelesen und in den Quellcode eingefügt. Im
allgemeinen werden mit diesem Befehl die Header-Dateien (Dateiendung
*.h) eingefügt, aber auch andere Quellcodedateien (Dateiendung
*.c oder *.cpp) sind möglich. Die
gewünschte Datei wird in spitze Klammern (<...>) gesetzt.
Die angegebene Datei wird im Standardverzeichnis der Header-Dateien gesucht. Dieses
Verzeichnis wird häufig auch Includeverzeichnis genannt. Um auch
eigene Header-Dateien einlesen zu können, die meistens bei den Quellcodedateien
gespeichert werden, wird der Dateiname in Anführungszeichen ("...")
anstatt in spitzen Klammern gesetzt. Dabei kann auch das Laufwerk und das Verzeichnis
angegeben werden, wenn es nicht bei den Quellcodedateien, sondern gesondert gespeichert
wird.
Beispiele:
#include <stdio.h> /* Includeverzeichnis */
#include "projekt.h" /* akt. Verzeichnis */
#define Makroname "my_header.h"
#include Makroname /* laedt Datei, die durch
den Makronamen
angegeben ist, aus dem
aktuellen Verzeichnis */
Bei Pfadangaben sollte aus Kompatibilitätsgründen ein Schrägstrich
(/) anstelle des sonst üblichen Backslashs (\)
verwendet werden. Natürlich kann auch ein Backslash verwendet werden, dann ist aber
das Programm nicht mehr unter UNIX compilierfähig! Ferner müssen bei der
Verwendung von Backslashs immer zwei hintereinander geschrieben werden, da der Backslash ein
Steuerzeichen (wie z.B. \n) einleitet.
Beispiele:
#include "C:\\Verz\\header.h" /* läuft nicht unter UNIX! */
#include "U:/projekt/include/projekt.h"
10.2. #define, #ifdef und #ifndef
Für die Fehlersuche werden meist Zeilen in den Quellcode eingefügt, die z.B. den
Inhalt von Variablen anzeigen. Ist die Fehlersuche abgeschlossen, müssen alle diese
Zeilen wieder gelöscht werden (und bei neuen Fehlern wieder geschrieben werden usw.).
Um dieses zu vermeiden, kann für den Präprozessor ein Label definiert werden.
Dazu wird der Befehl #define Label verwendet. Dieses Label kann
der Präprozessor abfragen, ob es definiert ist oder nicht. Je nachdem kann der
Präprozessor für das Compilieren Quellcodebereiche einblenden oder nicht. Diese
Abfrage wird mit dem Befehl #ifdef Label (Abkürzung
für #if defined) durchgeführt. Ist das Label definiert,
werden alle folgenden Anweisungen bis zum #else oder
#endif mit compiliert, ansonsten nicht. Um nun die Fehlersuche
abzuschließen, braucht nur die Definition des Labels gelöscht oder auskommentiert
zu werden. Wird es nur auskommentiert, kann es jederzeit mit minimalem Aufwand wieder
aktiviert werden.
Genauso wie in C/C++ kann auch ein else-Zweig eingerichtet werden. Dazu
wird der Befehl #else benutzt.
Beispiel:
kap10_01.c
01 #include <stdio.h>
02
03 #define TEST
04
05 int main()
06 {
07 int a = 3, b = 5, c;
08
09 c = a * b;
10 #ifdef TEST
11 printf("Variableninhalte:\n");
12 printf("a = %i\nb = %i\nc = %i\n", a, b, c);
13 #else
14 printf("Ergebnis von %i mal %i ist %i\n", a, b, c);
15 #endif
16
17 return 0;
18 }
In diesem Beispiel wird am Anfang des Programms das Label TEST (der
Name des Labels ist beliebig, unterliegt aber den Regeln eines Bezeichners; wird
standardmäßig komplett in Großbuchstaben geschrieben) definiert.
Im Verlauf des Programms wird dieses Label abgefragt, um den Inhalt der Variablen anzeigen
zu lassen. Ist dagegen das Label nicht definiert, wird das Ergebnis der Berechnung angezeigt.
Entsprechend umgekehrt wird mit dem Befehl #ifndef Label
(Abkürzung für #if not defined) geprüft, ob das Label
(noch) nicht definiert ist.
Ein anderer Anwendungsbereich dieser Präprozessor-Befehle ist der
Includewächter im Bereich der Header-Dateien. Wird in einem
großen Projekt dieselbe Header-Datei mehrfach eingelesen, werden die darin
enthaltenen Deklarationen bzw. Definitionen mehrfach ausgeführt. Dies führt zu
Fehlern bei der Compilierung. Um dies zu verhindern, wird in der Header-Datei zuerst
abgefragt, ob ein bestimmtes Label bereits definiert ist. Ist dies nicht der Fall
(nämlich beim ersten Durchlauf), wird dieses Label definiert. Dann folgen alle
Deklarationen und Definitionen der Header-Datei und am Ende das #endif.
Wird diese Header-Datei im gleichen Compiliervorgang ein weiteres Mal eingelesen, ist das
Label bereits definiert und der Rest der Header-Datei wird nicht wiederholt compiliert.
Das nächste Beispiel zeigt den typischen Aufbau von Header-Dateien. Der Name des
Labels, das definiert wird, ist meistens der Dateiname der Header-Datei, wobei der Punkt
durch einen Unterstrich ersetzt wird. Dadurch können in mehreren Header-Dateien keine
Labels mit dem gleichen Namen existieren. Da mit dem #define-Befehl
auch Textersetzung gemacht wird (siehe nächster Abschnitt), würde im folgenden
Beispiel jedes Vorkommen des Labels - z.B. als Variable - durch "nichts" ersetzt werden. Um
dies zu verhindern, wird der Name des Labels gleichzeitig durch sich selbst ersetzt.
Beispiel:
/* Header-Datei PROJEKT.H */
#ifndef PROJEKT_H
#define PROJEKT_H PROJEKT_H
/* Deklarationen und Definitionen */
#endif /* PROJEKT_H */
10.3. Makros mit #define
Ein weiterer Einsatzbereich des Befehls #define ist die Textersetzung
im Quellcode, wobei auch Parameter erlaubt sind. Diese Textersetzungen werden auch
Makros genannt. Der Präprozessor ersetzt vor dem Compilierungsvorgang im
Quellcode alle Texte, die mit dem angegebenen Text identisch sind, durch den angegebenen
Ersatztext. Im folgenden Beispiel wird angegeben, dass im Quellcode alle Vorkommen von PI
durch die Zahl 3.14159265 ersetzen sollen. Es hat sich eingebürgert, diese Makronamen
komplett in Großbuchstaben zu schreiben.
Beispiel:
kap10_02.c
01 #include <stdio.h>
02
03 #define PI 3.14159265
04
05 int main()
06 {
07 printf("PI = %f\n", PI);
08
09 return 0;
10 }
Nach der Definition kann PI wie eine konstante Variable verwendet
werden. Der Compiler bekommt diese aber gar nicht zu sehen, da vor dem Compilierungsvorgang
ja alle Stellen, an denen PI steht, durch die angegebene Zahl ersetzt.
Daher kann übrigens auch kein Zeiger auf PI gerichtet
werden.
Wie oben erwähnt, können auch Makros mit Parametern eingerichtet werden.
Beispiel:
#define QUADRAT(x) x * x
Steht nun im Quelltext die Zeile
z = QUADRAT(5);
wird diese durch den Präprozessor umgeschrieben in
z = 5 * 5;
Diese Makros können das Leben eines Programmierers manchmal sehr vereinfachen, aber
sie können auch gefährlich sein. Wie gefährlich das angegebene Makro sein
kann, ist sehr schnell gezeigt; es braucht nur eine Summe als Parameter übergeben
werden. Dann wird aus der Zeile
z = QUADRAT(3 + 6);
durch den Präprozessor folgende Zeile gemacht.
z = 3 + 6 * 3 + 6;
Das Ergebnis ist 27 statt 81! Was fehlt, sind Klammern. Aber auch wenn Klammern bei der
Definition des Makros gesetzt werden, kann es zu Problemen kommen.
#define QUADRAT(x) ((x) * (x))
Vorsichtshalber sind nicht nur um die beiden x Klammern, sondern auch um den gesamten
Ausdruck gesetzt worden. In der folgenden Zeile wird der Parameter vor der Übergabe um
1 erhöht.
z = QUADRAT(++x);
Falsch, denn nachdem der Präprozessor die Textersetzung durchgeführt hat, steht
folgende Zeile da.
z = ((++x) * (++x));
Hier wird deutlich, dass der Parameter vor der Multiplikation zweimal um 1 erhöht
wird.
Ferner wird mit Makros die Typkontrolle umgangen. Werden nun Parameter mit falschen
Datentypen übergeben, wird die Fehlersuche stark erschwert.
Aus z = QUADRAT("Test"); wird z = (("Test") * ("Test")); .
Fazit:
Die Textersetzung mit dem #define-Befehl sollte im allgemeinen
nicht verwendet werden, wenn es Alternativen gibt. Und die gibt es
fast immer!
10.4. Variable Argumentenliste in Markodefinitionen
Mit dem C99-Standard können Funktions-ähnliche Makros mit variabler
Parameteranzahl erzeugt werden. Dazu muss als letzter bzw. als einziger Parameter
eine Ellipse (...) angegeben werden. Diese Parameter können dann
im Makro mit dem Bezeichner __VA_ARGS__ (Kurzfassung von variable
arguments) verwendet werden.
Im folgenden Beispiel wird der Inhalt der Variablen x im Debug-Modus
auf dem Bildschirm angezeigt, ansonsten auf dem Fehlerkanal stderr
ausgegeben.
Beispiel:
kap10_03.c
01 #include <stdio.h>
02
03 /* in folgender Zeile "//" entfernen für Debug-Modus */
04 // #define DEBUG
05
06 #ifdef DEBUG
07 #define PRINTF(...) printf(__VA_ARGS__)
08 #else
09 #define PRINTF(...) fprintf(stderr, __VA_ARGS__)
10 #endif
11
12 int main()
13 {
14 int x = 0;
15
16 PRINTF("Variable x = %d\n", x);
17
18 return 0;
19 }
Im folgenden Beispiel wird ein Makro mit variabler Parameteranzahl definiert, das die
Parameter mit Hilfe des #-Operators (siehe unten im Abschnitt
#-Operator) in eine Zeichenkette umwandelt. Es werden also
die Namen der Parameter und nicht deren Inhalte ausgegeben.
Beispiel:
kap10_04.c
01 #include <stdio.h>
02
03 #define MAKE_STRING(...) #__VA_ARGS__
04
05 int main()
06 {
07 int a = 1, b = 2, c = 3, d = 4;
08
09 printf("Die Variablen %s haben ", MAKE_STRING(a, b, c, d));
10 printf("die Werte %i, %i, %i und %i.\n", a, b, c, d);
11
12 return 0;
13 }
10.5. Vordefinierte Makros
Im Standard-C müssen bereits einige Makros im Präprozessor vordefiniert sein. Die
Namen der vordefinierten Makros beginnen und enden jeweils mit zwei Unterstrichen. Die
wichtigsten vordefinierten Makros sind in der folgenden Tabelle aufgelistet.
Vordefiniertes Makro |
Bedeutung |
__LINE__ |
Zeilennummer innerhalb der aktuellen Quellcodedatei |
__FILE__ |
Name der aktuellen Quellcodedatei |
__DATE__ |
Datum, wann das Programm compiliert wurde (als Zeichenkette) |
__TIME__ |
Uhrzeit, wann das Programm compiliert wurde (als Zeichenkette) |
__STDC__ |
Liefert eine 1, wenn sich der Compiler nach dem Standard-C richtet. |
__STDC_VERSION__ |
Vor dem C95-Standard war dieses Makro nicht definiert; ab dem C95-Standard liefert dieses Makro das Veröffentlichungsdatum in Form von yyyymm als long-Zahl: |
Beispiel:
kap10_05.c
01 #include <stdio.h>
02
03 int main()
04 {
05 printf("Programm wurde compiliert am ");
06 printf("%s um %s.\n", __DATE__, __TIME__);
07
08 printf("Diese Programmzeile steht in Zeile ");
09 printf("%d in der Datei %s.\n", __LINE__, __FILE__);
10
11 #ifdef __STDC__
12 printf("Standard-C-Compiler!\n");
13 #else
14 printf("Kein Standard-C-Compiler!\n");
15 #endif
16
17 #if __STDC_VERSION__ > 201700L
18 printf("Standard-C17 oder neuer\n");
19 #elif __STDC_VERSION__ > 201100L
20 printf("Standard-C11\n");
21 #elif __STDC_VERSION__ > 199900L
22 printf("Standard-C99\n");
23 #elif __STDC_VERSION__ > 199400L
24 printf("Standard-C95\n");
25 #else
26 printf("Standard-C89 oder aelter\n");
27 #endif
28
29 return 0;
30 }
Dieses Beispielprogramm gibt folgendes aus (wenn es in der Quellcodedatei
kap10_05.c gespeichert ist und am 04.08.2022 um 15:47:45 Uhr mit einem
Standard-C11-Compiler compiliert wurde):
Programm wurde compiliert am Aug 4 2022 um 15:47:45.
Diese Programmzeile steht in Zeile 9 in der Datei kap10_05.c.
Standard-C-Compiler
Standard-C11
Auch werden für Compiler und Betriebssystem je eine Konstante vordefiniert. Die Konstanten der
gängigen Compiler und Betriebssystem werden in den folgenden beiden Tabellen aufgelistet
(die Liste ist schon etwas älter ...):
Vordefiniertes Makro |
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 |
Vordefiniertes Makro |
Betriebssystem |
__unix__ |
UNIX-System |
__unix |
UNIX-System |
__MS_DOS__ |
MS-DOS |
__WIN32__ |
MS Windows ab 95 |
__OS2__ |
OS2 |
_Windows |
Zielsystem MS Windows |
__NT__ |
MS Windows NT |
__linux__ |
Linux-System |
__FreeBSD__ |
FreeBSD |
__OpenBSD__ |
OpenBSD |
_SGI_SOURCE |
SGI-IRIX mit Extension *.sgi |
_MIPS_ISA |
SGI-IRIX |
__hpux |
HP-UX |
Vordefinierte Makros können mit #undef nicht entfernt werden.
10.6. #undef
Der Präprozessor-Befehl #undef kann dazu genutzt werden, um ein
Label bzw. ein Makro wieder zu entfernen. Dazu muss hinter dem Befehl der Name des Labels bzw.
des Makros gesetzt werden. Die Syntax lautet also wie folgt:
#undef Name
Es wird kein Fehler verursacht, wenn dieser Befehl auf einen Namen angewendet wird, der gar
nicht oder nicht mehr definiert ist.
Ist ein Label bzw. ein Makro erst einmal entfernt, kann anschließend ein neues Label
bzw. ein neues Makro mit diesem Namen definiert werden.
10.7. #if
Ähnlich wie bei den Befehlen #ifdef und #ifndef
kann mit dem Befehl #if eine bedingte Compilierung erreicht werden. Mit
dem Befehl #if werden aber nicht Labels abgefragt, sondern es können
beliebige Bedingungen verwendet werden. Die Bedingung muss einen konstanten arithmetischen
Wert ergeben, der als Wahrheitswert interpretiert werden kann. Dieser Wert muss bereits beim
Compilieren feststehen.
Der Bereich der bedingten Compilierung muss mit dem Befehl #endif
(in einer eigenen Zeile) abgeschlossen werden. Es ist auch möglich, mit
#else einen "Sonst"-Zweig in die bedingte Compilierung mit
einzubringen.
10.8. #line
Mit dem Präprozessor-Befehl #line können Zeilennummer und
Dateiname für die vordefinierten Makros __LINE__ und
__FILE__ verändert werden. Dies kann bei der Fehlersuche sinnvoll
sein, wenn die Quellcodedatei vor dem Compilieren von einem Tool bearbeitet wird, bei dem
Zeilen eingefügt oder mehrere Quellcodedateien zu einer zusammen gefügt werden.
Dabei gibt es zwei Formen der Anwendung: In der ersten Form werden hinter dem Befehl die
Zeilennummer und der Dateiname angegeben; der Dateiname muss in Anführungszeichen
gesetzt werden. Diese Angaben gelten für die Quellcodezeile, die diesem Befehl
folgt.
#line n "Dateiname"
In der zweiten Form wird der Dateiname weggelassen; dieser entspricht dann dem tatsächlichen
Dateinamen bzw. dem angegebenen Dateinamen des vorherigen
#line-Befehls.
#line n
Ältere Compiler erlauben auch eine verkürzte Schreibweise, die aber
vom Standard-C nicht akzeptiert wird. Dabei wird das Wort
line weggelassen:
# n "Dateiname"
Beispiel:
kap10_06.c
01 #include <stdio.h>
02
03 int main()
04 {
05 #line 12345 "Neuer_Dateiname.c"
06 printf("Diese Programmzeile steht in Zeile ");
07 printf("%d in der Datei %s.\n", __LINE__, __FILE__);
08
09 return 0;
10 }
Dieses Beispielprogramm gibt folgendes aus (egal in welcher Quellcodedatei es gespeichert
ist):
Diese Programmzeile steht in Zeile 12346 in der Datei
Neuer_Dateiname.c.
10.9. #-Operator
Der #-Operator ist ein unärer Präprozessor-Operator und wird
in Makros verwendet. Während das Makro umgesetzt wird, wird der
#-Operator mit dem dahinter stehenden Makro-Parameter ersetzt durch den
aktuellen Wert des Makro-Parameters eingeschlossen in Anführungszeichen. Dadurch wird
der Wert des Makro-Parameters, der hinter dem Operator steht, in eine Zeichenkette umgewandelt,
egal um was für einen Datentypen es sich handelt. Während die Zeichenkette erzeugt
wird, werden mehrere hintereinander stehende Weiße Leerzeichen ersetzt durch ein
Leerzeichen. Anführungszeichen und Backslashs, die im Makro-Parameter enthalten sind,
bekommen noch ein zusätzliches Backslash davor gesetzt, damit die ursprüngliche
Bedeutung erhalten bleibt.
Beispiel:
#define Makro(a) printf(#a)
Beim Durchlauf des Präprozessors werden die Makroaufrufe
Makro(7);
Makro("\n");
werden ersetzt durch
printf("7");
printf("\"\\n\"");
Achtung:
Eine Reihe von älteren C-Compilern fügen vor Anführungszeichen und Backslashs
keinen weiteren Backslash ein. Dies ist aber im Standard-C nicht zulässig.
10.10. ##-Operator
Mit dem ##-Operator lassen sich Tokens in einer Makrodefinition
zusammenfügen. D.h. das Token vor und das Token nach dem Operator werden zu einem Token
zusammengesetzt. Dabei wird für den zweiten Token der aktuelle Wert eingefügt,
sofern dieser ein Makro-Parameter ist.
Im folgenden Beispiel setzt der Präprozessor die Makros Makro(1) und
Makro(2) korrekt in die Variablen temp1 und
temp2 um. Da der Präprozessor die Makros vor dem Compilieren auflöst,
wird in der Schleife das Makro Makro(i) zur Variable tempi
umgesetzt.
Beispiel:
kap10_07.c
01 #include <stdio.h>
02
03 #define MAKRO(i) temp ## i
04
05 int main()
06 {
07 int temp1, temp2, tempi, i;
08
09 MAKRO(1) = 5; /* setzt die Variable temp1 auf 5 */
10 MAKRO(2) = 7; /* setzt die Variable temp2 auf 7 */
11
12 for (i = 1; i <= 2; i++)
13 MAKRO(i) = 0; /* setzt 2x die Variable tempi auf 0 */
14
15 printf("%i\n", MAKRO(1)); /* gibt temp1 (also 5) aus */
16 printf("%i\n", MAKRO(2)); /* gibt temp2 (also 7) aus */
17
18 return 0;
19 }
10.11. #pragma
Mit Hilfe des Präprozessor-Befehls #pragma können Informationen
oder Einstellungen an den Compiler übermittelt werden. Diese Informationen und
Einstellungen sind abhängig vom Compiler. Jeder Compiler sollte
#pragma-Befehle ignorieren, wenn die Informationen und Einstellungen nicht
zum Compiler passen. Dieser Befehl wurde mit dem Standard-C eingeführt.
Beispiel:
#pragma FENV_ACCESS ON
Dieser Befehl weist den Compiler an, beim Compilieren Überwachungen und Exceptions rund
um die Fließkomma-Arithmetik mit einzubauen.
Mit C99 wurden dann noch die Standard-Pragmas eingeführt. Während im obigen Beispiel
das Pragma Compiler-abhängig ist, wird im nächsten Beispiel das C99-Standard-Pragma
(gleichen Namens) angewendet.
Beispiel:
#pragma STDC FENV_ACCESS ON
Es gibt im C99 nur die drei folgenden Standard-Pragmas. Diese können auf
ON, OFF oder auf DEFAULT
(Vorgabewert des Compilers) gesetzt werden.
#pragma STDC FP_CONTRACT ON
#pragma STDC FENV_ACCESS ON
#pragma STDC CS_LIMITED_RANGE ON
Die #pragma-Befehle können nur an zwei verschiedenen Stellen im
Quelltext eingesetzt werden: Entweder in der obersten Ebene (Top Level) vor der ersten
Deklaration oder innerhalb eines Blockes - auch hier vor der ersten Deklaration.
Im ersten Fall ist der Befehl bis zum Ende des Quelltextes oder bis zu einem erneuten Befehl
mit dem gleichen Pragma gültig. Liegt der erneute Befehl innerhalb eines Blockes, so
überlagert dieser erneute Befehl den ersten nur innerhalb des Blockes; nach dem Block
ist wieder das erste Pragma gültig.
Im zweiten Fall ist der Befehl bis zum Ende des Blockes oder bis zu einem erneuten Befehl
mit dem gleichen Pragma gültig.
Mit C99 wurde zusätzlich auch ein _Pragma-Operator eingeführt.
Dieser wird während des Präprozessor-Laufes zusammen mit dem Parameter zu einem
#pragma-Befehl umgewandelt.
Beispiel:
Die Zeile
_Pragma("STDC FENV_ACCESS ON")
wird während des Präprozessor-Laufes umgewandelt in
#pragma STDC FENV_ACCESS ON
Im Gegensatz zum #pragma-Befehl kann der
_Pragma-Operator durch Makros erstellt werden.
10.12. #error
Der Präprozessor-Befehl #error wurde erst mit dem Standard-C
eingeführt. Der Befehl erzeugt eine Fehlermeldung während des Compilierens, d.h.
das Programm kann dadurch nicht compiliert werden. Die Fehlermeldung, die ausgegeben werden
soll, muss hinter dem Befehl angegeben werden.
Beispiel:
#ifndef Labelname
#error Labelname ist nicht definiert!
#endif
In diesem Beispiel wird während des Compilierens die Compiler-Fehlermeldung
"Labelname ist nicht definiert!" ausgegeben, sofern Labelname nicht
definiert ist. Damit kann der #error-Befehl verwendet werden, um
z.B. Wiedersprüche in der Programmierung zu finden.
Beispiel:
#define MAX 255
#if (MAX % 256) != 0
#error "MAX muss ein Vielfaches von 256 sein!"
#endif
In diesem Beispiel ist die Fehlermeldung in Anführungszeichen gesetzt worden, damit das
Token MAX nicht durch den #define-Befehl durch 255
ersetzt wird. Die Anführungszeichen werden in der Fehlermeldung mit ausgegeben.
Voriges Kapitel: 9. Funktionen
Nächstes Kapitel: 11. Datei-Ein- und Ausgabe in C