B.2 ANSI C99
Bis heute unterstützt noch kein Compiler den C99-Standard komplett. Wollen Sie bspw. wissen, wie weit der GNU GCC Compiler diesen Standard bis jetzt unterstützt, können Sie sich unter der folgenden Webseite einen Überblick zum aktuellen Status verschaffen: http://gcc.gnu.org/c99status.html
Zwar sollte man als Autor stets bei den Fakten bleiben, aber die Frage sollte schon gestattet sein: Wem nützt der C99-Standard? Ich stelle die Frage daher, weil einzig unter Linux/UNIX die Bemühungen unternommen werden, den Standard umzusetzen. In der Welt von Microsoft (weder beim Borland- noch beim VC++-Compiler) finden sich Anzeichen der Umsetzung des Standards (und das nach fünf Jahren). Sinn und Zweck des ANSI C Standards ist es ja, Programme zu erstellen, die möglichst portabel sind! Und das ist de facto nicht der Fall – also ein Standard, der nicht so recht einer ist und es wahrscheinlich auch nie mehr werden wird.
Im Folgenden finden Sie einen groben Überblick zu den neuen Headerdateien des C99-Standards. Für eine detailliertere Auflistung (u. a.) aller C99-Funktionen sei bei Bedarf die Webseite http://www.dinkumware.com/refxc.html empfohlen.
B.2.1 <iso646.h> – Zur Verwendung von Zeichensätze im ISO646-Format (NA1)
Die Headerdatei <iso646.h> wurde mit Normative Amendment 1, eine Erweiterung des C-Standards, 1995 hinzugefügt und enthält lesbare Alternativen (symbolische Konstanten) zu einigen Operatoren.
Tabelle B.17
Alternative symbolische Konstanten für Operatoren
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)
|
B.2.2 <wchar.h> – (NA1)
Die Headerdatei <wchar.h> wurde ebenfalls mit Normative Amendment 1 1995 hinzugefügt. In dieser Headerdatei sind folgende Funktionen enthalten:
Umwandlung von Strings zu Zahlwerten für den erweiterten Zeichensatz
String- und Speicherbearbeitung für den erweiterten Zeichensatz
Ein- und Ausgabe für den erweiterten Zeichensatz
Eine genauere Erläuterung der einzelnen Funktionen würde an dieser Stelle zu weit gehen. Aber im Allgemeinen sind Ihnen diese Funktionen schon u. a. aus den <stdio.h>- und <string.h>-Headerdateien bekannt. Hier handelt es sich bei dem ...w... im Funktionsnamen um dieselbe Funktion wie Sie diese bereits aus den eben aufgeführten Headerdateien kennen, nur, dass sich diese auf den erweiterten Zeichensatz beziehen. Also: fgetwc() ist die Alternative für den erweiterten Zeichensatz der Funktion fgetc() zum Einlesen einzelner Zeichen aus einem FILE-Stream.
B.2.3 <wctype.h> (NA1)
Die Headerdatei <wctype.h> wurde ebenfalls wie <iso646.h> und <wchar.h> mit Normative Amendment 1 hinzugefügt. Diese Headerdatei enthält Zeichenklassifizierungen und Umwandlung für den erweiterten Zeichensatz (einfach, ctype.h für den erweiterten Zeichensatz ist wctype.h). Auch hier gilt dasselbe wie schon bei der Headerdatei <wchar.h>. Die Bedeutung der einzelnen Funktionen entspricht exakt der, die Sie aus der Headerdatei <ctype.h> bereits kennen – nur, dass sich diese Funktionen auf den erweiterten Zeichensatz beziehen und wieder den Buchstaben ...w... (für Wide-Char) im Funktionsnamen enthalten. Also, anstatt isalpha() lautet das Gegenstück für den erweiterten Zeichensatz iswalpha().
Jede Funktion erwartet als Argument einen Typ win_t, was ein Wert zwischen 0 und 32767 (oder WEOF) ist.
B.2.4 <complex.h> – Funktionen zur Manipulation komplexer Zahlen
In der Headerdatei <complex.h> finden Sie eine Reihe von Makros und Funktionen für die (ebenfalls) neu in C99 eingeführten arithmetischen Datentypen. Bei diesen Typen handelt es sich um weitere Gleitpunkttypen, welche zur Darstellung von komplexen und imaginären Zahlen eingeführt wurden. 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:
komplexe Gleitpunkttypen: float _Complex, double _Complex und long double _Complex
imaginäre Zahl: float _Imaginary, double _Imaginary und long double _Imaginary
B.2.5 <fenv.h> – Kontrolle der Gleitpunkzahlen-Umgebung
In der Headerdatei <fenv.h> (für Floating Environment = Gleitpunkt-Umgebung) wurden zwei neue Typen, mehrere Makros und einige Funktionen zum Testen auf Fehlersituationen beim Rechnen mit Gleitpunktzahlen implementiert. Diese Umgebung enthält die Systemvariable fexept_t, welche von Gleitpunkt-Exceptions-Funktionen gesetzt werden kann, und die Variable fenv_t, welche für den Kontroll-Modus (bspw. des Rundungsverhaltens) verwendet wird.
Gleitpunkt-Exceptions
Zum Behandeln von so genannten Gleitpunkt-Exceptions stehen Ihnen folgende Funktionen zur Verfügung:
#include <fenv.h>
int feclearexcept(int except);
int fegetexceptflag(fexcept_t *pflag, int except);
int feraiseexcept(int except);
int fesetexceptflag(const fexcept_t *pflag, int except);
int fetestexcept(int except);
Alle Funktionen enthalten als Argument except, womit Sie eine oder mehrere der folgenden Konstanten mithilfe des bitweisen ODER kombinieren können.
Tabelle B.18
Makros für Gleitpunkt-Exeptions
Makro
|
Bedeutung
|
FE_DIVBYZERO
|
Division durch 0
|
FE_INEXACT
|
Ergebniss der Operation nicht exakt
|
FE_INVALID
|
unzulässiger Wert(ebereich)
|
FE_ OVERFLOW
|
Überlauf
|
FE_UNDERFLOW
|
Unterlauf
|
FE_ALL_EXCEPT
|
alle Eben genannte Makros auf einmal verwenden
|
Mit der Funktion fetestexecept() prüfen Sie, welche der Gleitpunkt-Exceptions (siehe Tabelle B.18) gesetzt sind. Die Funktion feraiseexcept() löst die als Argument eingegebene Gleitpunkt-Exception aus. Mit feclearexcept() löschen Sie eine als Argument angegebene Gleitpunkt-Exception. Die Funktion fegetexceptflag() speichert den aktuellen Status, der angegebenen Exception (erstes Argument) in der Systemvariable pflag. Mit der Funktion fesetexceptflag() hingegen können Sie die Flags in pflag, welche Sie zuvor mit fegetexceptflag() gesichert haben, wieder herstellen.
Kontroll-Modus
Mit dem Kontroll-Modus können Sie bspw. angeben, wie eine Gleitpunktzahl gerundet werden soll. Die Rundungsart können Sie mit den folgenden beiden Funktionen setzen bzw. erfragen:
#include <fenv.h>
int fegetround(void);
int fesetround(int mode);
Welche Rundungsart Sie setzen bzw. ermitteln können, wird mit den folgenden symbolischen Konstanten festgelegt:
Tabelle B.19
Kontroll-Modus für das Rundungsverhalten
Makro
|
Bedeutung
|
FE_DOWNWARD
|
Abrunden zum nächstkleineren Wert
|
FE_TONEAREST
|
Runden zum nächsten Wert
|
FE_TOWARDZERO
|
Abschneiden
|
FE_UPWARD
|
Aufrunden zum nächstgrößeren Wert
|
Gleitpunkt-Umgebung
Wollen Sie die Gleitpunkt-Umgebung als Ganzes verwenden, stehen Ihnen mit dem Typ fenv_t folgende Funktionen zur Auswahl:
#include <fenv.h>
int fegetenv(fenv_t *penv);
int feholdexcept(fenv_t *penv);
int fesetenv(const fenv_t *penv);
int feupdateenv(const fenv_t *penv);
Mit fegetenv() können Sie die aktuelle Gleitpunkt-Umgebung in penv sichern und mit fsetenv() wiederherstellen bzw. setzen. Bei Verwendung von feholdexcept() wird trotzt einer Exception weitergerechnet. Diese Funktion sichert die aktuelle Gleitpunkt-Umgebung und setzt alle Status-Flags zurück. Bspw.:
...
fenv_t env;
/* Gleitpunkt-Umgebung speichern */
feholdexcept(&env);
...
/* Ein Exeception ist aufgetreten !!! bspw. Division durch 0 */
...
/* Exception löschen */
feclearexcept(FE_DIVIDBYZERO);
/* zuvor gesetzte Gleitpunkt –Umgebung wieder setzen */
feupdateenv(&env);
...
Wie im Code-Auschnitt eben gesehen, wird mit der Funktion feuptdateenv() die übergebene Gleitpunkt-Umgebung wieder gesetzt. Für die Funktionen feudateenv() und fesetenv() kann auch die symbolische Konstante FE_DFL_ENV verwendet werden, was ein Zeiger auf die Gleitpunkt-Umgebung darstellt, welcher beim Programmstart gesetzt wurde.
Um die Gleitpunkt-Umgebung überhaupt verwenden zu können, müssen Sie diese mit dem folgenden Pragma aktivieren:
#pragma STDC FENC_ACCESS ON
B.2.6 <inttypes.h> – Für genauere Integertypen
Diese Headerdatei beinhaltet weitere Typen, mehrere Funktionen und Makros, welche recht nützlich zur Verwendung von Integer sind. Das ANSI C-Komitee hat diese Headerdatei eingeführt, um endlich Integer-Typen zu unterstützen, die unabhängig von der Maschine und dem Betriebssystem sein sollen. Bspw. beträgt auf einem 16 Bit-System die Bitanzahl für char, short, int und long 8, 16, 16 und 32 Bits. Auf einem 32 Bit-System hingegeben 8, 16, 32 und 32 Bits. Hierbei wurden mit typdef Integertpyen verschiedenster Größe erstellt. Diese neue Definierung von Integertypen ist (wird) auf jeden Fall sinnvoll, gerade, da jetzt auch noch die Einführung der 64 Bit-Systeme auf dem Massenmarkt aktuell ist.
B.2.7 <stdbool.h> – Boolesche Datentypen in C
In der Headerdatei <stdbool.h> gibt es jetzt auch die booleschen Variablen als Makros implementiert für C.
Tabelle B.20
Boolescher Datentyp für C
Makro
|
Bedeutung
|
Bool
|
Boolescher Datentyp für Wahrheit
|
false
|
0/trifft nicht zu
|
true
|
1/trifft zu
|
B.2.8 <stdint.h> – Definiert verschieden Integertypen
In der Headerdatei <stdint.h> befinden sich weitere Ganzzahl-Datentypen, 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:
Tabelle B.21
Neue ganzzahlige Typen vorgegebener Breite
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
|
Zu all 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.
B.2.9 <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); /* float */
double sqrt(double zahl); /* double */
long double sqrtl(long double zahl); /* long double */
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.
Tabelle B.22
Neue mathematische Funktionen
Funktionen
|
Bedeutung
|
double round (double);
double trunc (double);
double rint (double)
|
Funktionen zum Runden von Zahlen
|
double fmax (double, double);
double fmin (double, double);
|
Maximum, Minimum
|
double log2 (double);
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
|
Sehr interessant dürften die Makros für den Vergleich von Gleitpunktzahlen sein, welche ebenfalls hinzugekommen sind:
Tabelle B.23
Makros zum Vergleichen von Gleitpunktzahlen
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
|
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>):
Tabelle B.24
Bestimmung der Gleitpunktzahl-Kategorie
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
|
Abfragen, in welche Kategorie eine bestimmte Gleitpunktzahl fällt, können Sie mit den folgenden Makros vornehmen:
Tabelle B.25
Makros zur Bestimmung der Gleitpunktzahl-Kategorie
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.
|
Intern werden all diese Makros jedoch zum Teil mit Hilfe des Makros fpclassify() ausgewertet:
fpclassify(x) == FP_INFINITE //isinf(x)
fpclassify(x) == FP_NORMAL //isnormal(x)
B.2.10 <tgmath.h> – Typengenerische Mathematik-Funktionen
In der Headerdatei <tgmath.h> befinden sich typengenerische Mathematik-Funktionen (bzw. Makros). Darunter versteht man, dass Sie verschieden mathematische Funktionen mit verschiedenen Argumenten wie reelle und/oder komplexe Gleitpunkttypen mit demselben Namen aufrufen können – es wird also immer die Funktion aufgerufen, welche dem Datentyp des Argumentes entspricht. Bspw.:
float _Complex fx;
long double lx;
/* gleich zu cos((double)1) */
cos(1) ;
/* gleich zu cosl(lx) */
cos(lx) ;
/* gleich zu
cpowl((long double _Complex)fx, (long double _Complex)lx) */
pow(fx, lx) ;
Ist eine Funktion hingegen nur für reelle und/oder komplexe Gleitpunkttypen definiert, dann entspricht der Namen des typengenerierten Makros dem der Funktion, welche ursprünglich für double definiert ist.
|