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:
= 199409L   für C95-Standard
= 199901L   für C99-Standard
= 201112L   für C11-Standard
= 201710L   für C17-Standard


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