Das ATOS-Magazin
 
  zurück zum News-Archiv
Anfang zurück vorwärts Ende 

ANSI C - Ein Programmierkurs - Teil XIII

assert.h

Die Includedatei assert.h definiert das Makro assert, mit dem sich Testpunkte in einem Programm einfügen lassen. Als Parameter bekommt das Makro einen Ausdruck. Hat der Ausdruck den Wert 0, dann wird das Programm angehalten und eine Fehlermeldung ausgegeben, die folgendem Formatstring entspricht:

 "\nAssertion failed: %s, file %s, line %d\n"

Der erste Platzhalter gibt den Ausdruck wieder, den assert prüfen soll. Der Dateiname und die Zeilennummer werden durch den die Makros ./c-kurs.wml und 4757 des Preprozessors gesetzt.

Ist der Makroname NDEBUG (no debugging) definiert, wird das assert-Makro ignoriert. Damit können durch assert Überprüfungen während der Entwicklung gemacht werden. Bei der Programm-Version für die Auslieferung wird durch Definition von NDEBUG die Überprüfung ausgeschaltet, ohne dass das Makro assert aus dem Quelltext entfernt werden muss.

Beim Folgenden Beispiel wird mittels assert überprüft, ob der stream auch wirklich ungleich NULL ist. Ist er NULL, dann ist er mit Sicherheit nicht gültig, unser Ausdruck liefert einen Wert von 0 (Falsch) und das Programm wird angehalten. Schreiben Sie ein kleines Programm, das diese Funktion enthält und übergeben Sie einmal einen gültigen stream und einmal NULL. Dann versuchen Sie beides nochmals, nachdem Sie vor der Includeanweisung für assert.h die Zeile

#define NDEBUG

eingefügt haben. Für TC/PC gibt es in den Optionen für den Compiler das Editfeld define Macro, das auch dazu benutzt werden kann, NDEBUG für die Releaseversion zu definieren.

int WriteInFile(FILE *stream)
{
   assert(stream!=NULL);
   fputs("Hallo Welt",stream);
}

stdarg.h

In der Datei stdarg.h sind Funktionen für die Behandlung von variablen Parameterlisten definiert, wie sie z.B. die printf-Funktion benutzt.

void va_start(va_list ap, parmN)
Mit dem Makro va_start initialisiert man die Bearbeitung. Dazu übergibt man eine Variable vom Typ va_list, die dazu dient, die Parameter der Reihe nach abzuarbeiten, und den letzten Parameter, der einen Namen trägt.
#define va_arg(ap, type) (*((type *)(ap))++)
Das Makro va_arg bekommt die Variable vom Typ va_list und den Datentyp des nächsten Parameters und liefert den Parameter. Durch eine Folge von va_arg-Aufrufen kann man der Reihe nach jeden Parameter abarbeiten.
#define va_end(ap)
Sind alle Parameter abgearbeitet worden, muss vor Verlassen der Funktion dieses Makro aufgerufen werden.

Um mit variablen Parameterlisten sinnvoll umzugehen, muss die Funktion erkennen können, welche Parameter folgen. Bei den formatierten Ausgaben (printf...) erfolgt dies durch die Platzhalter im Formatstring.


void Test(int x,...)
{
   va_list ap;
   int i;

   puts("va_arg - Test");
   va_start(ap, x);
   for (i = 0; i < x; i++)
   {
      printf("Parameter %d hat den Wert %d\n", i, va_arg(ap,int) + 2);
   }
}
 
int main(void)
{
   Test(1,34);
   Test(3,34,42,65);
}

setjmp.h

Die Definitionen aus setjmp.h erlauben es, den normalen Ablauf eines Programms zu umgehen. Damit wird üblichereise im Fehlerfall direkt aus tief verschachtelten Funktionsaufrufen zurückgesprungen. Leider wird hierdurch der Programmablauf etwas durchbrochen. Bei einer guten Aufteilung eines Programms auf Module und Bibliotheken, die auch allgemeingültig verwendbar sind, sollten die Funktionen der Module sich geordnet beenden und einen entsprechenden Returnwert liefern.

void longjmp(jmp_buf jmp_buffer, int return_value);
Mit dieser Funktion stellt man den Zustand wieder her, der mit setjmp gespeichert wurde. Die Programmausführung geht dann so weiter, als wurde setjmp mit dem Returnwert return_value, der ungleich 0 sein muss, beendet. Die Funktion, aus der setjmp aufgerufen wurde, darf noch nicht beendet sein! Globale Variablen haben wieder den Wert von vor dem Aufruf von setjmp, der Wert von automatischen Variablen aus der Funktion, die setjmp aufruft, ist undefiniert.
int setjmp(jmp_buf jmp_buffer);
Diese Funktion speichert den aktuellen Zustand in jmp_buffer ab, um ihn später wieder anspringen zu können. Diese Funktion liefert in diesem Fall 0 zurück. Wenn mit longjmp zu dieser Stelle zurückgesprungen wird, befindet man sich hinter diesem Aufruf, aber mit einem Returnwert ungleich 0.

Damit das Verhalten etwas deutlicher wird, folgt ein kleines Beispiel:

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

int i;

int main(void)
{
   jmp_buf TestEnv;

   i = 0;
   printf("i vor setjmp = %d\n", i);
   if (setjmp(TestEnv) == 0)
   {
      puts("direkter Aufruf");
	  i = 4;
      printf("i vor longjmp = %d\n", i);
	  longjmp(TestEnv, 1);
   }
   else
   {
      puts("hierher durch Aufruf von longjmp");
      printf("i nach setjmp = %d\n", i);
   }
   puts("Programmende");
}

signal.h

In der Datei signal.h sind Funktionen für Ausnahmebehandlung bzw. Exceptions definiert. Es kann sich dabei um Fehler in der Programmausführung als auch um Signale von einem anderen Prozeß handeln. Unter UNIX oder auch MiNT mit den entsprechenden Tools ist es möglich, mittels kill ein solches Signal an ein Programm zu senden.

typedef void (*sigfunc_t)( int );
Dies ist die Definition für einen Zeiger auf die Exceptionroutine.
sigfunc_t signal( int sig, sigfunc_t func );
Mit diese Funktion setzt man eine eigene Funktion für eine bestimmte Exception. Der Parameter sig gibt an, für welche Ausnahme die Funktion func zuständig sein soll. Entsprechende Konstanten sind in signal.h definiert. Als Funktion kann auch SIG_DFL oder SIG_IGN angegeben werden. SIG_DFL bedeutet, dass der Default-Handler des Systems benutzt wird. SIG_IGN bedeutet, dass das Signal ignoriert wird.
int raise( int sig );
Mit dieser Funktion sendet man ein Signal an sich selbst.

time.h

In time.h sind Funktionen für den Umgang mit Datum und Uhrzeit definiert. Einige Funktionen benutzen den Datentyp tm, der den weiter unten aufgeführten Aufbau hat. Die Komponente tm_isdst ist positiv, wenn Sommerzeit gilt, 0, wenn keine Sommerzeit gilt und negativ, wenn diese Information nicht zur Verfügung steht. Die restlichen Komponenten sollten mit dem Kommentar selbsterklärend sein.

Man muss unterscheiden, ob eine Funktion die Kalenderzeit (die z.B. keine Zeitzonen berücksichtigt) oder die Ortszeit benutzt. Die Kalenderzeit, die in einen Datentyp time_t geschrieben wird, ist üblicherweise die Anzahl Sekunden seit dem 1.1.1970, also dem ersten 1. Januar seit dem Bestehen von UNIX.

struct tm
{
    int tm_sec;   /* Sekunden nach der vollen Minute -- [0,  59] */
    int tm_min;   /* Minuten nach der vollen Stunde  -- [0,  59] */
    int tm_hour;  /* Stunden nach Mitternacht        -- [0,  23] */
    int tm_mday;  /* Tag im Monat                    -- [1,  31] */
    int tm_mon;   /* Monate seit Januar              -- [0,  11] */
    int tm_year;  /* Jahre seit 1900                             */
    int tm_wday;  /* Tage seit Sonntag               -- [0,   6] */
    int tm_yday;  /* Tage seit 1. Januar             -- [0, 365] */
    int tm_isdst; /* Kennzeichen für Sommerzeit                  */
};
char *asctime( const struct tm *tblock );
Diese Funktion erzeugt aus der Zeit in tblock eine Zeichenkette, in der die Zeit in der Form "Sun Jan 3 15:14:13 1988\n" abgelegt ist. Wenn dieses Format nicht erwünscht ist, kann strftime benutzt werden.
char *ctime( const time_t *timer );
Diese Funktion gibt die Kalenderzeit in timer als Ortszeit in einem String aus. Damit ist die Funktion äquivalent zu asctime(localtime(timer));
struct tm *gmtime( const time_t *clock );
Diese Funktion wandelt die Kalenderzeit in tp in die Coordinated Universal Time (UTC).
struct tm *localtime( const time_t *clock );
Diese Funktion wandelt die Kalenderzeit in clock in die Ortszeit.
time_t time( time_t *timer );
Diese Funktion liefert die aktuelle Kalenderzeit. Wenn timer ungleich NULL ist, wird der Wert auch in die Variable geschrieben, auf die timer zeigt.
time_t mktime( struct tm *timeptr );
Diese Funktion wandelt die Ortszeit in timeptr in die Kalenderzeit um.
clock_t clock( void );
Diese Funktion liefert die Systemticks, die das Programm seit dem Start verbraucht hat. Die Konstante CLOCKS_PER_SEC liefert die Zahl Systemticks pro Sekunde.
size_t strftime( char *s, size_t max_size, const char *format, const struct tm *timeptr );
Diese Funktion wandelt die Zeit aus timeptr gemäß dem Formatstring format in einen String um, der nach s kopiert wird. Der Parameter max_size gibt dabei an, wieviel Zeichen s maximal fassen kann. Analog zu printf werden gewöhnliche Zeichen nach s übernommen und Platzhalter für strftime, die aus dem Prozentzeichen, gefolgt von einem Buchstaben, bestehen, werden durch den entsprechenden Wert aus der Struktur timeptr ersetzt.
double difftime( time_t time2, time_t time1 );
Diese Funktion liefert die Differenz von time2-time1 in Sekunden.

Platzhalter für strftime

  • [%a] abgekürzter Wochentag
  • [%A] ausgeschriebener Wochentag
  • [%b] abgekürzter Monatsname
  • [%B] voller Monatsname
  • [%c] Datum und Uhrzeit
  • [%d] Tag im Monat (1-31)
  • [%H] Stunde (0-23)
  • [%I] Stunde (0-12)
  • [%j] Tag im Jahr (1-366)
  • [%m] Monat (1-12)
  • [%M] Minute (00-59)
  • [%p] AM/PM
  • [%S] Sekunde (00-59)
  • [%w] Wochentag (0-6)
  • [%W] Woche im Jahr (0-52)
  • [%x] lokale Datumsdarstellung
  • [%X] lokale Zeit-Darstellung
  • [%y] Jahr ohne Jahrhundert (0-99)
  • [%Y] Jahr mit Jahrhundertangabe
  • [%Z] Name der Zeitzone (z.B. MEZ)
  • [%%] das '%'-Zeichen

limits.h

In der Datei limits.h sind die Grenzen von Datentypen definiert, also den kleinsten und den größten Wert, den ein Datentyp in dieser Implementierung aufnehmen kann.

#define  CHAR_BIT            8
#define  SCHAR_MIN        -128
#define  SCHAR_MAX         127
#define  UCHAR_MAX         255U
#define  CHAR_MIN         -128
#define  CHAR_MAX          127
#define  MB_LEN_MAX          1
#define  SHRT_MIN       -32768
#define  SHRT_MAX        32767
#define  USHRT_MAX       65535U
#define  INT_MIN        -32768
#define  INT_MAX         32767
#define  UINT_MAX        65535U
#define  LONG_MIN  -2147483648L
#define  LONG_MAX   2147483647L
#define  ULONG_MAX  4294967295LU

float.h

Die Datei floats.hbeschreibt wie limits.h die Grenzen von Datentypen, allerdings für float- und double-Werte.

#define  FLT_ROUNDS                      1
#define  FLT_RADIX                       2
#define  FLT_MANT_DIG                   24
#define  FLT_DIG                         6
#define  FLT_MIN_EXP                  -125
#define  FLT_MIN_10_EXP                -37
#define  FLT_MAX_EXP                   128
#define  FLT_MAX_10_EXP                 38
#define  FLT_EPSILON          1.192093E-07
#define  FLT_MIN              1.175494E-38
#define  FLT_MAX              3.402823E+38
#define  DBL_MANT_DIG                         64
#define  DBL_DIG                              19
#define  DBL_MIN_EXP                      -16383
#define  DBL_MIN_10_EXP                    -4932
#define  DBL_MAX_EXP                       16384
#define  DBL_MAX_10_EXP                     4932
#define  DBL_EPSILON  5.421010862427522170E-0020
#define  DBL_MIN      1.681051571556046753E-4932
#define  DBL_MAX      1.189731495357231765E+4932
#define  LDBL_MANT_DIG                        64
#define  LDBL_DIG                             19
#define  LDBL_MIN_EXP                     -16383
#define  LDBL_MIN_10_EXP                   -4932
#define  LDBL_MAX_EXP                      16384
#define  LDBL_MAX_10_EXP                    4932
#define  LDBL_EPSILON 5.421010862427522170E-0020
#define  LDBL_MIN     1.681051571556046753E-4932
#define  LDBL_MAX     1.189731495357231765E+4932

Der folgende, letzte Abschnitt liefert eine kurze Übersicht und Erklärung von Compiler-Optionen der verschiedenen Systeme.

Michael Bernstein


Anfang zurück vorwärts Ende  Seitenanfang

Copyright und alle Rechte beim ATOS-Magazin. Nachdruck und Veröffentlichung von Inhalten nur mit schriftlicher Zustimmung der Redaktion.
Impressum - Rückmeldung via Mail oder Formular - Nachricht an webmaster