Jetzt folgen einige Standardfunktionen der Headerdatei worin Routinen für Zeit-und Datum definiert sind. Zuerst ein kurzer Überblick über den speziellen Datentypen dieser Headerdatei und deren Bedeutung. C C++ C/C++ Zeitroutinen Zeitfunkionen Zeit time.h Algorithmen Zeitberechnung Profiling Profiler Zeitroutinen - Zeitfunkionen Zeit time.h Algorithmen Zeitberechnung Profiling Profiler Kapitel 23: Weitere Headerdateien und deren Funktionen (ANSI C)

23.5. <setjmp.h>            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

In C sind Sprünge über Funktionsgrenzen hinweg nicht erlaubt. Das heißt genau, Funktionen werden immer an den direkten Ausrufer zurückgegeben. Wenn z.B. Funktion 1 die Funktion 2 aufruft, kehrt Funktion 2 immer zuerst zur Funktion 1 zurück; eben in der umgekehrten Reihenfolge, wie die einzelnen Funktionen auf (genauer: unter) dem Stack abgelegt wurden. Erst dann kann Funktion 1 zu ihrem Ausrufer zurückkehren. Ein Beispiel:

#include <stdio.h>

void func1(void);
void func2(void);
void func3(void);
void func4(void);

void func1(void)
{
   printf("Funktion 1 ist aufgerufen!\n");
   func2();
}

void func2(void)
{
   printf("Funktion 2 ist aufgerufen!\n");
   func3();
}

void func3(void)
{
   printf("Funktion 3 ist aufgerufen!\n");
   func4();
}

void func4(void)
{
   printf("Funktion 4 ist augerufen!\n");
}

int main()
{

   func1();
   return 0;
}

Abbildung 23.2: Rückkehr von Funktionen bei einem normalen Verlauf
Abbildung 23.2: Rückkehr von Funktionen bei einem normalen Verlauf

Das Programm ruft in der main()-Funktion zuerst func1() auf, func1() ruft anschließend func2() auf, func2() ruft danach func3() auf und func3() ruft am Ende func4() auf. Anschließend kehren die einzelnen Funktionen wieder in der Reihenfolge func3(), func2() und func1() zur main()-Funktion zurück. Was wäre jetzt, wenn in func2() eine Berechnung durchgeführt wird und der Wert dieser Berechnung nicht mehr dem entspricht, den der Nutzer sich versprochen hat? Trotzdem werden sinnloserweise noch func3() und func4() aufgerufen und ausgeführt. Die Frage lautet also, wie kann man z.B. von func2() zur main()-Funktion zurückspringen, Funktionen func3() und func4() auslassen und auch nicht mehr über func1() zur main()-Funktion zurückzukehren?

Dafür können Sie die Funktionen der Headerdatei <setjmp.h> verwenden. Hier die Syntax:

#include <setjmp.h>

jmp_buf env;    /* Primitiver Datentyp jmp_buf */

int setjmp(jmp_buf env)

void longjmp(jmp_buf env, int wert);

Der Datentyp jmp_buf env ist eine Art Puffer, der den mit setjmp(env) eingefrorenen Programmzustand enthält und den Sie mit der Funktion longjmp(env,1) wiederherstellen können. jmp_buf enthält zum Beispiel die CPU-Registerinhalte (CS, DS, SS und ES), den Stackpointer (SP), den Instruktionspointer (IP) usw. - alle Informationen eben, die erforderlich sind, um den gleichen Zustand wieder herzustellen, der vor dem Aufruf von setjmp() vorlag. Mit setjmp() werden, wie eben schon erwähnt, alle Informationen, die im Augenblick vorliegen, auf einen Stack gelegt. Der Aufruf von setjmp() lautet:

if(setjmp(env) == 0) 

Beim ersten Aufruf von setjmp() liefert die Funktion den Wert 0 zurück. Beim zweiten Aufruf durch longjmp(env) liefert die Funktion auf jeden Fall einen Wert ungleich 0 zurück.

Mit der Funktion longjmp() kehren Sie dann an diese Programmstelle zurück, die Sie mit setjmp(env) auf dem Stack abgelegt haben. Dies geschieht mit folgendem Aufruf:

longjmp(env,1); 

Nochmals alles zusammengefasst:

...
jmp_buf programmzustand;
...
if(setjmp(programmzustand)==0)
    printf("Programmzustand auf den Stack gelegt\n");
else
    printf("Rücksprung mit longjmp erfolgt\n");
...
...
/* viele, viele Funktionen später */
longjmp(programmzustand,1);

Als Erstes legen Sie hier mit setjmp() den Programmzustand auf den Stack. Anschließend, viele Funktionen später, wird mit longjmp() dieser Zustand wiederhergestellt und springt zurück zu setjmp(). Dieses Mal ist der Rückgabewert von setjmp() aber nicht mehr 0, und daher fährt das Programm hinter der else-Anweisung fort.

Jetzt soll alles in einem Programm verwendet werden, ohne komplizierte Berechnungen oder Ähnliches. Es wird einfach abgefragt, wie viele Funktionen ausgeführt werden sollen, und das Programm springt nach der gewünschten Anzahl der Funktionen mit einem Aufruf von longjmp() zur main()-Funktion zurück:

#include <stdio.h>
#include <setjmp.h>

void func1(int);
void func2(int);
void func3(int);
void func4(void);
jmp_buf env;

static int zahl;

void func1(int zahl)
{
   printf("Funktion 1 ist aufgerufen!\n");
   if(zahl==1)
      longjmp(env,1);
   func2(zahl);
}

void func2(int zahl)
{
   printf("Funktion 2 ist aufgerufen!\n");
   if(zahl==2)
      longjmp(env,2);
   func3(zahl);
}

void func3(int zahl)
{
   printf("Funktion 3 ist aufgerufen!\n");
   if(zahl==3)
      longjmp(env,3);
   func4();
}

void func4()
{
   printf("Funktion 4 ist augerufen!\n");
}

int main()
{
   printf("Wie viele Funktionen sollen ausgeführt werden : ");
   scanf("%d",&zahl);
   if(setjmp(env) == 0)
      func1(zahl);
   else
      printf("Rücksprung durch longjmp von Funktion %d!\n",zahl);
   return 0;
}

Die Funktionen setjmp() und longjmp() haben übrigens nichts mit der Anweisung goto gemeinsam. Es hat sich als recht nützlich erwiesen, setjmp() und longjmp() bei Fehlerbehandlungen einzusetzen.

23.6. <signal.h>            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Signale sind nicht vorhersehbare Ereignisse, die zu einem nicht vorhersagbaren Zeitpunkt auftreten können, also asynchrone Ereignisse. Nach dem ANSI C-Standard gibt es folgende Signale, die vorkommen können:

Name Bedeutung
SIGABRT Dieses Signal signalisiert, dass sich das Programm abnormal beendet hat (abort()).
SIGFPE Dieses Signal wird z.B.angezeigt bei einer Division durch 0 oder einem Überlauf einer Zahl.
SIGILL Dieses Signal wird angezeigt, wenn ein illegaler Hardware-Befehl ausgeführt wird.
SIGINT Dieses Signal wird an alle Prozesse geschickt, wenn die Tastenkombination STRG + C gedrückt wurde.
SIGSEGV Wird dies angezeigt, wurde versucht, auf eine unerlaubte Speicherstelle zu schreiben oder zu lesen.
SIGTERM Beendigung eines Programms

Tabelle 23.3: Makros für Fehlersignale

Unter Linux gibt es deutlich mehr Signale (ca. 30). Mit dem Befehl

kill -l 

wird eine Liste der Signale unter Linux/UNIX ausgegeben.

Tritt ein Signal auf, haben Sie folgende Möglichkeiten, darauf zu reagieren:

Um auf die Signale zu reagieren, existiert ein so genanntes Signalkonzept. Dabei richtet ein Prozess einen so genannten Signalhandler ein. Dieser Signalhandler teilt - wenn das Signal auftritt - dem Systemkern mit, was er zu tun hat. Ein solcher Handler kann mit der Funktion signal() eingerichtet werden. Hier ihre Syntax:

#include <signal.h>

void(*signal(int signr, void(*sighandler)(int)))(int);

Einen solchen Prototypen zu lesen, ist fast unmöglich. Aus diesem Grund wurde die Funktion in der Headerdatei <signal.h> wie folgt vereinfacht:

typedef void (*__p_sig_fn_t)(int);
__p_sig_fn_t signal(int, __p_sig_fn_t);

Somit sieht der Prototyp folgendermaßen aus:

signalfunktion *signal(int signalnummer,
                       signalfunktion *sighandler);

Mit dem Parameter signalnummer legen Sie die Nummer des Signals fest, für die ein Signalhandler eingerichtet werden soll. Dies ist dann eines der Signale, welche Sie soeben in der Tabelle kennen gelernt haben (bzw. unter Linux diejenigen, die mit kill -l aufgelistet wurden).

Für den Parameter sighandler sind zwei Konstanten in der Headerdatei <signal.h> deklariert, SIG_DFL und SIG_IGN. Mit SIG_DFL wird die Default-Aktion ausgeführt, was meist die Beendigung des Prozesses bedeutet. Ein Beispiel:

signal(SIGINT,SIG_DFL);

Falls Sie die Tastenkombination STRG + C drücken, wird die Default-Einstellung des Signals SIGINT ausgeführt. Und die Default-Einstellung schreibt vor, dass das Programm beendet wird. Als zweite Möglichkeit können Sie Folgendes eingeben:

signal(SIGINT,SIG_IGN);

Drücken Sie jetzt die Tastenkombination STRG + C, passiert gar nichts. Das Signal SIGINT wird mit der Angabe von SIG_IGN ignoriert. Als dritte Möglichkeit können Sie das Signal SIGINT abfangen und die Adresse einer eigenen Funktion übergeben, welche ausgeführt werden soll, wenn die Tastenkombination STRG + C betätigt wurde:

signal(SIGINT,funktionsaufruf); 

Jetzt wird es Zeit, zu sehen, wie die Funktion signal() in der Praxis eingesetzt wird:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sigfunc(int sig)
{
   int c;
   if(sig != SIGINT)
      return;
   else
      {
         printf("\nWollen Sie das Programm beenden (j/n) : ");
         c=getchar();
         if(c == 'j')
            exit (0);
         else
            return;
      }
}

int main()
{
   int i;
   signal(SIGINT,sigfunc);
   while(1)
      {
         printf("Mit STRG+C beenden");
         for(i=0;i<=48;i++)
             printf("\b");
      }
   return 0;
}

Mit der Anweisung

signal(SIGINT,sigfunc); 

wird ein Signalhandler für das Signal SIGINT eingerichtet, der beim Auftreten dieses Signals die Funktion sigfunc aufrufen soll.

Ein einfaches Beispiel bietet auch das Erstellen einer eigenen kleinen Shell. Die einzelnen Shellbefehle werden in einer Endlosschleife abgearbeitet. Mit der Tastenkombination STRG + C lösen Sie dabei einen Neustart der Shell aus. Dieser Sprung (Neustart) wird mit den Funktionen der Headerdatei <setjmp.h> realisiert. Hier das Beispiel dazu:

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#define MAX 255
#define OK 0

jmp_buf restart;

void ctrlc(int sig)
{
   signal(sig,ctrlc);
   /* Zurück zur Kommandozeile */
   longjmp(restart,1);
   return;
}

int main()
{
   char *command;
   /* Install signal handler */
   signal(SIGINT,ctrlc);

   if(setjmp(restart) != 0)
      printf("\n\nShell neu gestartet...\n\n");
   else
      printf("\n\nShell gestartet...\n\n");

   for (;;)
      {/* Hier können Sie machen was Sie wollen */
         char puffer[MAX];
         printf("$~> ");
         fgets(puffer, MAX, stdin);
         command = strtok(puffer, "\n");

         if( strcmp(command, "test") == OK )
            printf("Ihr Befehl lautete \"test\"\n");
         else if( strcmp(command, "help") == OK )
            printf("Brauchen Sie Hilfe?\n");
         /* usw. eine Menge mehr Shellbefehle …*/
         else if( strcmp(command, "exit") == OK )
            exit(0);
         else
            {
               printf("\nUnbekannter Shellbefehl\n");
               printf("Bekannte Befehle: test, help, exit\n\n");
            }
      }
   return 0;
}

Dies ist eine einfache Schnittstelle einer eigenen Shell. Logischerweise müssen Sie statt der Ausgabe von Texten Ihre selbst geschriebenen Funktionen implementieren.

Ein weiteres Beispiel zu signal() mit dem Signal SIGABRT.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sigfunc(int sig)
{
   if(sig==SIGABRT)
      printf("Demonstration von SIGABRT\n");
}

int main()
{
   signal(SIGABRT,sigfunc);
   abort();
   return 0;
}

Um zu testen, ob der Aufruf der Funktion signal() überhaupt erfolgreich war, befindet sich in der Headerdatei <signal.h> der Fehlercode SIG_ERR, der mit dem Wert -1 definiert ist. Wollen Sie also die Funktion signal() auf Fehler überprüfen, sollte dies so aussehen:

if( signal(SIGINT,sigfunc) == SIG_ERR)
   {  /* Fehler beim Aufruf von signal */

Es ist auch möglich, ein Signal an ein ausführendes Programm mit der Funktion raise() zu senden. Die Syntax der Funktion:

int raise(int signr); 

Damit können Sie ein Signal mit der signr an das Programm senden. Ein kurzes Beispiel:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sigfunc(int sig)
{
   if(sig==SIGINT)
      printf("SIGINT wurde ausgelöst\n");
}

int main()
{
   signal(SIGINT,sigfunc);
   /* SIGINT auslösen */
   raise(SIGINT);
   return 0;
}

Für Signale unter Linux/UNIX ist dies hier allerdings nur die Grundlage.

23.7. <string.h> - Die mem…-Funktionen zur Speichermanipulation            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Die meisten Stringfunktionen wurden bereits an früherer Stelle behandelt. Aber einige Funktionen habe ich Ihnen bislang noch unterschlagen. Mit den mem...-Funktionen in der Headerdatei <string.h> können Sie ganze Speicherblöcke kopieren, vergleichen, initialisieren und durchsuchen.

23.7.1 memchr() - Suche nach einzelnen Zeichen
Die Syntax:

void *memchr(const void *buffer, int c, size_t n);  

Diese Funktion sucht in den ersten n Bytes in buffer nach dem Zeichen c. Sollten Sie den ganzen String durchsuchen wollen, können Sie die Funktion strchr() verwenden. Tritt dabei ein Fehler auf oder wird das Zeichen nicht gefunden, gibt diese NULL zurück. Ein Beispiel:

#include <stdio.h>
#include <string.h>

int main()
{
   char str[] = "Have a lot of fun";
   char *p;
   char ch = 'l';

   p = (char *) memchr( str, ch, 10);
   if(NULL == p)
      printf("%c kommt nicht in den ersten 10 Bytes vor\n",ch);
   else
      printf("%c gefunden an Pos. %d\n",ch, p-str);
   return 0;
}

23.7.2 memcmp() - Bestimmte Anzahl von Bytes vergleichen
Die Syntax:

int memcmp( const void *s1, const void *s2, size_t n); 

Mit memcmp() werden die ersten n Bytes im Puffer s1 mit dem Puffer s2 lexografisch verglichen. Der Rückgabewert ist derselbe wie schon bei strcmp(). Ist s1 größer als s2, ist der Rückgabewert kleiner als 0. Ist s2 größer als s1, ist die Rückgabe größer als 0, und bei Gleichheit beider Speicherbereiche wird 0 zurückgegeben.

#include <stdio.h>
#include <string.h>

int main()
{
   char str1[] = "Have a lot of fun";
   char str2[] = "Have more than a lot of fun";
   int check, i;

   for(i = 4; i <= 10; i+=6)
      {
         check = memcmp( str1, str2, i);
         if(check == 0)
            printf("Vergleich %d Bytes: "
                   "Beide Strings sind gleich\n",i);
         else
            printf("Die ersten %d Bytes sind "
                   "unterschiedlich\n",i);
      }
   return 0;
}

23.7.3 memcpy() - Bestimmte Anzahl von Bytes kopieren
Die Syntax:

void *memcpy(void *dest, const void *src, size_t n);     

Mit der Funktion memcpy() können Sie n Bytes aus dem Puffer src in den Puffer dest kopieren. Die Funktion gibt die Anfangsadresse von dest zurück.

#include <stdio.h>
#include <string.h>

int main()
{
   char str[] = "Ein Wort was hier nicht hingehört: Mist!";
   char replace[] = "M***";
   char *ptr;

   ptr = strstr(str, "Mist");
   memcpy(ptr, replace, strlen(replace));

   printf("%s\n",str);
   return 0;
}

23.7.4 memmove() - Bestimmte Anzahl von Bytes kopieren
Die Syntax:

void *memmove(void *dest, const void* src, size_t n); 

Die Funktion erfüllt denselben Zweck wie die Funktion memcp(), mit einem einzigen, aber gravierenden Unterschied. memmove() stellt sicher, dass im Fall einer Überlappung der Speicherbereiche der Überlappungsbereich zuerst gelesen und dann überschrieben wird. Auch die Rückgabewerte sind bei memmove() dieselben wie bei memcpy().

#include <stdio.h>
#include <string.h>

int main()
{
   char str[21] = "1234567890";
   /* Den kompletten String nehmen und
      10 Bytes weiter als Kopie ablegen */
   memmove(str+10, str, sizeof(str)-1);
   printf("%s\n",str);
   return 0;
}

23.7.5 memset() - Speicherbereich mit bestimmten Zeichen auffüllen
Die Syntax:

void *memset(void *dest, int ch, unsigned int n);

Mit dieser Funktion füllen Sie die ersten n Bytes der Adresse dest mit den Zeichen ch auf.

#include <stdio.h>
#include <string.h>

int main()
{
   char credit_card[21] = "123456-aiex";
   char *ptr = strchr(credit_card, '-');
   *ptr++;
   /* Die letzten vier Zeichen der Kreditkartennummer
      nicht angeben */
   memset(ptr, '*', 4);
   printf("%s\n",credit_card);
   return 0;
}

23.8. Erweiterter ANSI C-Standard (ANSI C99)            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

Ursprünglich war für den erweiterten ANSI-C-Standard ein extra Kapitel vorgesehen. Aber um den Umfang des Buchs nicht zu sprengen und Ihre Geduld nicht allzu sehr zu strapazieren, werden diese Erweiterungen hier in einer kurzen Zusammenfassung erwähnt. Sie stellt keine Referenz der Erweiterungen dar, sondern fasst lediglich das Wichtigste in Kürze zusammen.

Hinweis
 

Es kann sein, dass dieser erweiterte ANSI C99-Standard bei vielen Compilern noch gar nicht implementiert ist und deshalb auch nicht funktioniert.

23.8.1 Neue elementare Datentypen
Zur Darstellung der boolschen Werte 1 und 0 wurde der Typ _Bool eingeführt. Der Wertebereich von _Bool beträgt 0 und 1. Wenn Sie die Headerdatei <stdbool.h> inkludieren, können Sie auch bool statt _Bool schreiben und die Bezeichner true für 1 und false für 0 verwenden.

Weitere zwei Ganzzahltypen, die eingeführt wurden, sind long long und unsigned long long, welche beide 8 Bytes Speicherplatz belegen. Hier ein Überblick der neuen Ganzzahltypen des C99-Standards:

Typ Speicherplatz Wertebereich
_Bool 1 Byte 0 und 1
long long 8 Byte -9223372036854775808
bis 9223372036854775807
unsigned long long 8 Byte 0 bis 18446744073709551615

Tabelle 23.4: Neue ganzzahlige Typen

23.8.2 <stdint.h> - Ganzzahlige Typen mit vorgegebener Breite
In der Headerdatei <stdint.h> befinden sich weitere Ganzzahldatentypen, die mit vorgegebener Breite verwendet werden. Mit vorgegebener Breite ist die Anzahl der Bits zur Darstellung des Werts gemeint, welche dieser Typ verwenden darf. Hier die Typen im Überblick:

Typ Bedeutung
intN_t Ein int-Wert mit einer Breite von exakt N Bits(erlaubte Werte für N: 8, 16, 32, 64)
int_leastN_t Ein int-Wert mit einer Breite von mindestens N Bits(erlaubte Werte für N: 8, 16, 32, 64)
int_fastN_t Der schnellste int-Typ mit mind. einer Breite von N Bits(erlaubte Werte für N: 8, 16, 32, 64)
intmax_t Größtmöglicher ganzzahliger Typ (Wert ist in der Konstante INT64_MAX bzw. UINT64_MAX deklariert)
intptr_t Max. Breite, um den Wert eines Zeigers zu speichern

Tabelle 23.5: Neue ganzzahlige Typen vorgegebener Breite

Zu allen diesen Ganzzahltypen gibt es jeweils einen unsigned-Bruder, der nur ein u vorangestellt hat (z.B. uintptr_t). Die maximalen und minimalen Limits dieser Ganzzahltypen sind ebenfalls in der Headerdatei <stdint.h> deklariert.

23.8.3 Komplexe Gleitpunkttypen
Zur Darstellung von komplexen und imaginären Zahlen wurden mit ANSI C99 weitere Gleitpunkttypen eingeführt. Die komplexe Zahl wird dabei mit dem Real- und Imaginärteil dargestellt, und zwar als float, double oder long double-Wert. Die Schreibweise dieser Gleitpunkttypen sieht wie folgt aus:

23.8.4 <iso646.h> - Symbolische Konstanten für Operatoren
In der Headerdatei <iso646.h> befinden sich einige symbolische Konstanten, welche Sie als Alternativen zu einigen Operatoren nutzen können. Hierzu eine Tabelle mit der jeweiligen symbolischen Konstante:

Konstante in <iso646.h> Operator
and && (logisches UND)
or || (logisches ODER)
not ! (logisches NICHT)
bitand & (bitweises UND)
bitor | (bitweises ODER)
xor ^ (bitweises Exklusiv-ODER)
compl ~ (bitweises NICHT)
and_eq &= (bitweises UND mit Zuweisung)
or_eq |= (bitweises ODER mit Zuweisung)
xor_eq ^= (bitweises Exklusiv-ODER mit Zuweisung)
not_eq != (logisches NICHT mit Zuweisung)

Tabelle 23.6: Alternative symbolische Konstanten für Operatoren

23.8.5 Deklaration von Bezeichnern
Neu beim ANSI C99-Standard ist, dass die Deklaration im Anweisungsblock frei platziert werden kann und nicht mehr am Anfang des Blocks erfolgen muss.

23.8.6 inline-Funktionen
inline-Funktionen sind dem C++-Programmierer ja bereits wohlbekannt und stehen mit dem C99-Standard auch dem C-Programmierer zur Verfügung. Um eine Funktion als inline-Funktion anzugeben, muss dieser nur das Schlüsselwort inline vorangestellt werden:

#include <stdio.h>

inline void xchange(int *z1, int *z2)
{
   int tmp;
   tmp = *z2;
   *z2  = *z1;
   *z1  = tmp;
}

inline void print(int *z1, int *z2)
{
   printf("%d :: %d\n", *z1, *z2);
}

int main()
{
   int zahl1 = 123, zahl2 = 321;

   print(&zahl1, &zahl2);
   xchange(&zahl1, &zahl2);
   print(&zahl1, &zahl2);
   return 0;
}

inline-Funktionen stellen eine sinnvolle Alternative zu parametrisierten define-Makros dar. Der Code der inline-Funktion wird vom Compiler direkt an der stelle eingefügt, an der der Aufruf stattfindet. Damit entfällt der Sprung in ein Unterprogramm, das heißt, der Aufwand mit dem Stack entfällt.

Die inline-Funktionen sollten aber möglichst klein gehalten werden. Werden zu viele Anweisungen verwendet, kann der Compiler das Schlüsselwort inline auch ignorieren und es als eine normale Funktion behandeln.

23.8.7 Vordefinierte Makros
Außer den bisher bekannten vordefinierten Makros, wie __LINE__, __FILE__, __DATE__, __TIME__ und __STDC__, sind weitere drei Makros hinzugekommen.

Makro Bedeutung
__func__ Gibt den Namen der Funktion aus, in der dieses Makro verwendet wird.
__STD_HOSTED__ Wenn es sich um eine Hosted-Implementierung der Standardbibliothek handelt, ist diese Konstante 1, ansonsten 0.
__STD_VERSION__ Wenn der ANSI C99 Standard unterstützt wird, ist diese Konstante 199901L (1999 Januar).

Tabelle 23.7: Neue vordefinierte Standardmakros

Ein Beispiel mit dem Makro __func__, welches sich prima zum Debuggen von Programmen eignet.

#include <stdio.h>

void eine_funktion()
{
   printf("Name der Funktion: %s\n",__func__);
}

int main()
{
   eine_funktion();
   return 0;
}

23.8.8 <math.h> - Neue Funktionen
Enorm erweitert wurde die Headerdatei <math.h>. Darin befinden sich jetzt noch mehr Funktionen und Makros als zuvor. Zu allen Funktionen, die Sie bereits kennen und denen, welche Sie in diesem Abschnitt noch kennen lernen, wurden Versionen herausgebracht, die jetzt auch für die Datentypen float und long double anwendbar sind. Bisher waren die Funktionen dieser Headerdatei ja nur mit double angegeben. Um die für den Datentyp passende Funktion zu verwenden, müssen Sie nur ein entsprechendes Suffix notieren. f steht für float, l für long double und keine Angabe steht - wie gehabt - für double-Gleitpunktzahlen. Als Beispiel die Funktion sqrt():

float sqrtf(float zahl);
double sqrt(double zahl);
long double sqrtl(long double zahl);

In der folgenden Tabelle finden Sie einige Funktionen, die neu in die Headerdatei <math.h> hinzugekommen sind. Zu all diesen Funktionen gibt es auch schon verschiedene Versionen. Es hängt davon ab, welches Suffix Sie verwenden.

Funktionen Bedeutung
double round (double);
double trunc (double);
double rint (double x)
Funktionen zum Runden von Zahlen
double fmax (double, double);
double fmin (double, double);
Maximum, Minimum
double log2 (double _x);
double logb (double);
Logarithmus
double copysign (double, double); Vorzeichen kopieren
double scalb (double, long);
extern double fma (double,double, double);
Laufzeitoptimierte Berechnungen
double hypot (double, double); Wurzel

Tabelle 23.8: Neue mathematische Funktionen

Sehr interessant dürften die Makros für den Vergleich von Gleitpunktzahlen sein, welche ebenfalls hinzugekommen sind:

Makro Bedeutung
isgreater(x, y) x größer als y
isgreaterequal(x, y) x größer als oder gleich y
isless(x, y) x kleiner als y
islessequal(x, y) x kleiner als oder gleich y
islessgreater(x, y) x kleiner als y ODER x größer als y
isunordered(x, y) Sind x und y nicht miteinander vergleichbar, gibt dieses Makro 1 zurück, ansonsten 0

Tabelle 23.9: Makros zum Vergleichen von Gleitpunktzahlen

Ein weiteres interessantes Feature sind Makros zu Bestimmung der Kategorie von Gleitpunktzahlen. In ANSI C werden die Gleitpunktzahlen in folgende fünf Kategorieren unterteilt (Konstanten aus der Headerdatei <math.h>):

Konstante Kategorie
FP_NAN NAN steht für Not a Number und bedeutet, dass es sich bei dem Wert um keine gültige Gleitpunktdarstellung handelt.
FP_NORMAL Eine Gleitpunktzahl in normaler Darstellung
FP_INFINITE Die Gleitpunktzahl wird als unendlicher Wert dargestellt.
FP_ZERO Eine Gleitpunktzahl mit dem Wert 0
FP_SUBNORMAL Eine Gleitpunktzahl, mit der besonders kleine Zahlen dargestellt werden können

Tabelle 23.10: Bestimmung der Gleitpunktzahl-Kategorie

Abfragen, in welche Kategorie eine bestimmte Gleitpunktzahl fällt, können Sie mit den folgenden Makros vornehmen:

Makro Bedeutung
isnan(x) Ist die Zahl gleich FP_NAN, wird 1 zurückgegeben, ansonsten 0.
isnormal(x) Ist die Zahl gleich FP_NORMAL, wird 1 zurückgegeben, ansonsten 0.
isfinite(x) Ist die Zahl eine Unendliche, wird 1 zurückgegeben, ansonsten 0.
isinf(x) Ist die Zahl gleich FP_INFINITE, wird 1 zurückgegeben, ansonsten 0.

Tabelle 23.11: Makros zur Bestimmung der Gleitpunktzahl-Kategorie

Intern werden alle diese Makros jedoch zum Teil mit Hilfe des Makros fpclassify() ausgewertet:

fpclassify(x) == FP_INFINITE //isinf(x)
fpclassify(x) == FP_NORMAL   //isnormal(x)

23.8.9 Zusammenfassung
Dies soll es nun gewesen sein bezüglich der Erweiterungen des neuen ANSI C99-Standards. Es gibt noch einige mehr, die in diesem Abschnitt aber nicht angesprochen wurden.

Einige Leser werden sich wohl fragen: Wo bleibt denn der neue ANSI C99-Standard? Immerhin sind jetzt schon einige Jahre vergangen, und es gibt immer noch keinen Compiler, der dem ANSI C99-Standard vollständig entspricht. Der gcc-Compiler wird diesen Standard wohl in ferner Zukunft erfüllen, aber bei den Compilern für Microsoft-Systeme sind in dieser Hinsicht keine Ansätze zu sehen. Es hat also den Anschein, dass sich der C99-Standard nicht mehr - wie noch der C89-Standard - auf den verschiedensten Systemen und Compilern flächendeckend verbreiten wird.

Weiter mit Kapitel 24: Dynamische Datenstrukturen)            zum Inhaltsverzeichnis