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)

In diesem Kapitel sollen Funktionen aus den Standard-Headerdateien näher erläutert werden, die bisher noch nicht oder nur zu kurz zur Sprache gekommen sind. Alle Headerdateien sind vom ANSI C-Komitee vorgeschrieben und somit auf allen Systemen vorhanden.

23.1. <assert.h> - Testmöglichkeiten und Fehlersuche            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Mit der Funktion

#include <assert.h>

void assert(int ausdruck);

können Ausdrücke aus einem Programm auf logische Fehler getestet werden. Ist ausdruck gleich 0, wird das Programm mit einer Fehlermeldung beendet.

Genauer: Erst wird eine Fehlermeldung auf stderr ausgegeben, etwa:

Assertion failed: ausdruck, file filename, line nnn 

Der Dateiname und die Zeilennummern stammen von den Präprozessor-Makros __FILE__ und __LINE__. Danach wird die Ausführung des Prozesses mit der Funktion abort() beendet.

Ein einfaches Beispiel: Es werden zwei Zahlen miteinander dividiert. Dabei darf keine der Zahlen eine Null oder ein negativer Wert sein. Außerdem soll der Teiler nicht größer als der Nenner sein. Hier das Listing:

#include <stdio.h>
#include <assert.h>

int divide(int zahl1, int zahl2)
{
   assert( (zahl1>=0 && zahl1>=zahl2) && (zahl2>=0) );
   return zahl1/zahl2;
}

int main()
{
   printf("%d / %d = %d\n",5,2,divide(5,2));
   printf("%d / %d = %d\n",3,4,divide(3,4));
   printf("%d / %d = %d\n",4,4,divide(4,4));
   return 0;
}

Das Programm wird durch den Funktionsaufruf divide(3,4) mit folgender Fehlermeldung abgebrochen:

Assertion failed: assert( (zahl1>=0 && zahl1>=zahl2) &&
(zahl2>=0) ), file:Pfad_zur_Datei.c, line 6

Diese Art Programme zu testen, eignet sich sehr gut für größere Projekte. Damit Sie nicht den ganzen Code durchsuchen müssen, um anschließend bei der Fertigstellung des Programms die assert()-Anweisungen zu entfernen, müssen Sie nur das Makro

NDEBUG

angeben, und der Compiler ignoriert alle assert()-Aufrufe. NDEBUG muss allerdings noch vor der Headerdatei

#include <assert.h>

angegeben werden. Bei dem folgenden Beispiel wird die assert()-Anweisung ignoriert:

#include <stdio.h>
#define NDEBUG
#include <assert.h>

int divide(int zahl1, int zahl2)
{
   assert( (zahl1>=0 && zahl1>=zahl2) && (zahl2>=0) );
   return zahl1/zahl2;
}

int main()
{
   printf("%d / %d = %d\n",5,2,divide(5,2));
   printf("%d / %d = %d\n",3,4,divide(3,4));
   printf("%d / %d = %d\n",4,4,divide(4,4));
   return 0;
}

<ctype> - Zeichenklassifizierung und Umwandlung            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

In der Headerdatei <ctype.h> befinden sich Funktionen, mit denen einzelne Zeichen überprüft oder umgewandelt werden können. In der folgenden Tabelle finden Sie die Syntax der einzelnen Funktionen und die Beschreibungen:

Prototyp Beschreibung
int isalnum(int c); Testet, ob ein Zeichen ein alphanumerisches Zeichen ist
int isalpha(int c); Testet, ob ein Zeichen ein Buchstabe ist
int iscntrl(int c); Testet, ob ein Zeichen ein Steuerzeichen ist (z.B. CTRL + B)
int isdigit(int c); Testet, ob ein Zeichen eine Dezimalziffer ist
int isgraph(int c); Testet, ob ein Zeichen ein druckbares Zeichen ist
int islower(int c); Testet, ob ein Zeichen ein Kleinbuchstabe ist< /td>
int isprint(int c); Testet, ob ein Zeichen ein druckbares Zeichen ist, aber kein Leerzeichen
int ispunct(int c); Testet, ob ein Zeichen ein Interpunktionszeichen ist
int isspace(int c); Testet, ob ein Zeichen ein Leerzeichen (Zwischenraumzeichen) ist
int isupper(int c); Testet, ob ein Zeichen ein Großbuchstabe ist
int isxdigit(int c); Testet, ob ein Zeichen eine hexadezimale Ziffer ist
int isascii(int c); Das Makro isascii prüft, ob das Zeichen c ein normiertes Zeichen des ASCI-Zeichensatzes ist (0-127). Wenn nicht, kann es mit toascii umgewandelt werden.
int isblank(int c); Ist c ein Leerzeichen, wird 1 zurückgegeben, sonst 0 (nur bei ANSI C99-Compilern vorhanden).
int tolower(int c); Wandelt Groß- in Kleinbuchstaben um
int toupper(int c); Wandelt Klein- in Großbuchstaben um
int toascii(int c); toascii ist ein Makro, das den übergebenen Wert c (durch Löschen aller Bits, außer den 7 niederwertigsten) auf den Bereich 0 bis 127 begrenzt und das Ergebnis dieser Operation zurückliefert. Dadurch werden aber nationale Zeichen wie etwa 'ä', 'ü', 'ö' ... falsch dargestellt.

Tabelle 23.1: Funktionen zur Zeichenklassifizierung und Zeichenumwandlung

Alle Funktionen in der Tabelle erwarten ein int-Argument, dessen Wert als EOF oder unsigned char darstellbar sein muss. Bei Erfolg geben diese Funktionen einen Wert ungleich 0 zurück. Tritt ein Fehler auf, ist der Rückgabewert immer 0. Zur Demonstration der einzelnen Funktionen das folgende Listing:

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

/*Prototypen*/
void grosschrift(char *, char *);
void kleinschrift(char *, char *);
void international(char *,char *);
void oeffne_dateien(char *, char *, FILE **, FILE **);

void grosschrift(char *arg2, char *arg3)
{
   FILE *in,*out;
   int c;
   oeffne_dateien(arg2,arg3,&in,&out);
   while((c=getc(in)) !=EOF)
      {
         if(islower(c))
            putc(toupper(c),out);
         else
            putc(c,out);
      }
}

void kleinschrift(char *arg2, char *arg3)
{
   FILE *in,*out;
   int c;
   oeffne_dateien(arg2,arg3,&in,&out);
   while((c=getc(in)) !=EOF)
      {
         if(isupper(c))
            putc(tolower(c),out);
         else
            putc(c,out);
      }
}

void international(char *arg2, char *arg3)
{
   FILE *in,*out;
   int c;
   oeffne_dateien(arg2,arg3,&in,&out);
   while((c=getc(in)) !=EOF)
      {
         if(isascii(c)==0)
            putc(toascii(c),out);
         else
            putc(c,out);
      }
}

void oeffne_dateien(char *quelle, char *ziel,
                    FILE **input, FILE **output)
{
   if((*input=fopen(quelle,"r+")) == NULL)
      {
         printf("Fehler beim Öffnen (lesen) von %s\n",quelle);
         exit(0);
      }
   if((*output=fopen(ziel,"w+")) == NULL)
      {
         printf("Fehler beim Öffnen (schreiben) von %s\n",ziel);
         exit(0);
      }
}

int main(int argc, char **argv)
{
   if(argc<4)
      {
         printf("Verwendung: "
      "Programmname -[Schalter] datei.txtZieldatei.txt\n"
      "für -[Schalter] : -b (kompl. Textdatei in Grossschrift)\n"
      "                  -s (kompl. Textdatei in Kleinschrift)\n"
      "                  -i (nat. Zeichen zu ASCII-Zeichen\n");
         exit(0);
      }
   if(strcmp(argv[1],"-b")==0)
      grosschrift(argv[2],argv[3]);
   else if(strcmp(argv[1],"-s")==0)
      kleinschrift(argv[2],argv[3]);
   else if(strcmp(argv[1],"-i")==0)
      international(argv[2],argv[3]);
   else
      {
         printf("Verwendung: "
      "Programmname -[Schalter] datei.txtZieldatei.txt\n"
      "für -[Schalter] : -b (kompl. Textdatei in Grossschrift)\n"
      "                  -s (kompl. Textdatei in Kleinschrift)\n"
      "                  -i (nat. Zeichen zu ASCII-Zeichen\n");
         exit(0);
      }
   return 0;
}

Mit diesem Beispiel kann bei einer Textdatei jeder Buchstabe in einen großen, einen kleinen oder ein ASCII-Zeichen umgewandelt werden.

23.3. <math.h> - Mathematische Funktionen            zurück  Ein Kapitel tiefer  Ein Kapitel höher  zum Inhaltsverzeichnis

Häufig werden bei Berechnungen die Quadratwurzel, der Sinus, der Cosinus oder der Tangens einer Zahl benötigt. Damit Sie nicht jedes Mal eine neue Funktion schreiben müssen, wurde die Bibliothek <math.h> geschrieben. Sie beinhaltet viele nützliche mathematische Funktionen.

Hinweis
 

(Anmerkung für Linux-User) Damit ein Programm die <math.h>-Bibliothek verwenden kann, muss diese erst mit dem Compilerflag -lm hinzugelinkt werden. Beispiel:
gcc -o programm programm.c -lm

Hierzu ein Überblick über die Funktionen in der Headerdatei <math.h> und ihre Bedeutungen:

Funktion Beschreibung
double acos(double zahl) Arcuscosinus
double asin(double zahl) Arcussinus
double atan(double zahl) Arcustangens
double atan2(double zahl1,double zahl2) Arcustangens von zahl1 und zahl2
double cos(double zahl) Cosinus
double sin(double zahl) Sinus
double tan(double zahl) Tangens
double cosh(double zahl) Cosinus hyperbolicus
double sinh(double zahl) Sinus hypberbolicus
double tanh(double zahl) Tangens hypberbolicus
double exp(double zahl) Exponentialfunktion berechnen
double log(double zahl) Logarithmus von zahl zur Basis e = 2.71828...
double log10(double zahl) Logarithmus zur Basis 10
double sqrt(double zahl) Quadratwurzel
double ceil(double zahl) Gleitpunktzahl aufrunden
double fabs(double zahl) Absolutwert
double floor(double zahl) Gleitpunktzahl abrunden
double frexp(double zahl,int zahl2) Zerlegt zahl in eine Mantisse und einen ganzzahligen Exponenten
double modf(double1 zahl1,double2 *zahl2) Zerlegt den Wert von zahl1 in einen gebrochenen und einen ganzzahligen Wert. Der ganzzahlige Wert (Vorkommateil) befindet sich dann in der Adresse von zahl2.
double pow(double zahl1,double zahl2) Potenz zahl1zahl2
int fmod(double zahl1,double zahl2) float modulo errechnet den Rest von zahl1/zahl2

Tabelle 23.2: Mathematische Funktionen

Es fällt außerdem auf, dass all diese Funktionen mit dem Typ double deklariert sind. Abhilfe schafft allerdings erst der ANSI C99-Standard. Dazu in ein paar Seiten mehr.

Diese Funktionen lassen sich einfach einsetzen, wie das folgende Beispiel zeigt:

/*Bei Linux den Compilerflag -lm mit angeben*/
#include <stdio.h>
#include <math.h>

int main()
{
   double i=5.5;
   printf("Quadradwurzel von %f = %f\n",i,sqrt(i));
   printf("Der Sinus von %f = %f\n",i,sin(i));
   printf("Der Tangens von %f = %f\n",i,tan(i));
   printf("Der Cosinus von %f = %f\n",i,cos(i));
   printf("Der Absolute Wert von %f = %f\n",i,fabs(i));
   return 0;
}

Sollte der double-Wert nicht mehr richtig darstellbar sein, geben all diese Funktionen die Konstante HUGE_VAL zurück, die ebenfalls in der Headerdatei <math.h> deklariert ist.

23.4. <stdlib.h>            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

In der Headerdatei <stdlib.h> befinden sich außer den Funktionen zum Allokieren von Speicherplatz noch weitere nützliche.

23.4.1 Programmbeendigung - exit(), _exit(), atexit() und abort()
Zur normalen Beendigung eines Programms können Sie außer return folgende Funktion verwenden:

void exit(int status);

Laut ANSI C-Standard ist es gleich, ob ein Programm mit der Funktion exit() oder return beendet wird - mit dem Unterschied, dass über exit() von einer beliebigen Position des Programms aus beendet werden kann. Bei return gelingt dies nur in der main()-Funktion. Der Ablauf von exit() lässt sich so erklären: Bei Programmbeendigung mit exit() werden zuvor alle gefüllten Puffer geleert, alle geöffneten Dateien geschlossen und alle temporären Dateien, die mit der Funktion tmpfile() angelegt wurden, gelöscht. Anschließend wird die Routine _exit() aufgerufen, und das Programm beendet sich.

Theoretisch kann die Funktion _exit auch gleich aufgerufen werden. Dies entspricht allerdings nicht dem ANSI C-Standard. Hier die Syntax:

#include <unistd.h>    /*unter Linux/UNIX*/
#include <stdlib.h>    /*unter MS-DOS*/

void _exit(int status);

Damit werden die oben genannten "Aufräumarbeiten" nicht vorgenommen. Eine weitere Funktion für Beendigungsroutinen in der Headerdatei <stdlib.h> ist die Funktion atexit(). Die Syntax:

#include <stdlib.h>

int atexit(void (*funktion) (void));

Mit atexit() wird ein so genannter Funktionshandler eingerichtet. Alle Funktionen, die in atexit() angegeben sind, werden in einer Funktionsliste eingetragen und bei Beendigung des Programms in umgekehrter Reihenfolge ausgeführt, also nach dem LIFO-Prinzip (Last In First Out). Laut ANSI C können insgesamt 32 solcher Funktionen verwendet werden. Hierzu ein Beispiel:

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

void funktion1()
{
   printf("Die Funktion funktion1 wurde aufgerufen\n");
}

void funktion2()
{
   printf("Die Funktion funktion2 wurde aufgerufen\n");
}

int main()
{
   printf("Wir beenden unser Programm\n");
   atexit(funktion1);
   atexit(funktion2);
   return 0;
}

Solch ein Beispiel macht natürlich wenig Sinn. Sie können atexit() beispielsweise verwenden, um Log-Dateien zu schreiben, etwa wenn der User das Programm beendet, oder wenn ein Programm mit einem Fehler beendet wurde. Oder Sie können noch diverse Aufräumarbeiten durchführen, wie dies im folgenden Beispiel geschieht:

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

char *memPtr;

void free_malloc( )
{
   /*Wurde überhaupt Speicher reserviert?*/
   if(memPtr == NULL)
      printf("Kein Speicher war reserviert!!!\n");
   else
      {
         free(memPtr);
         printf("Speicher wurde freigegeben!!\n");
      }
}

int main()
{
   memPtr =(char *) malloc(1000);
   if(memPtr==NULL)
      printf("Konnte keinen Speicher reservieren\n");
   if( atexit (free_malloc) != 0)
      printf("Konnte Funktionshandler nicht einrichten\n");

   /*Nach vielen Zeilen Code beenden wir das Programm*/
   return 0;
}

Die nächste Funktion zur Beendigung oder in diesem Fall besser, zur abnormalen Beendigung eines Programms, ist die Funktion abort(). Die Syntax:

#include <stdlib.h>

void abort(void);

Diese Funktion bewirkt - wie der Name schon sagt - eine abnormale Programmbeendigung. abort() schickt dem aufrufenden Prozess das Signal SIGABRT. Dieses Signal sollte niemals ignoriert werden. Hier ein Mini-Beispiel dazu:

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

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

Das Programm wird mit folgender Fehlermeldung beendet:

Abnormal Programm termination 

Hier wurde die Fehlerausgabe über stderr realisiert. Im Gegensatz zur Funktion exit() gibt es bei der Funktion abort() keine Vorgaben, ob der Ausgabepuffer geleert oder die temporären Dateien automatisch gelöscht werden. Somit ist diese Funktion nicht für Programme geeignet, die auf mehreren Systemen laufen müssen.

POSIX.1 hingegen schreibt vor, dass zumindest alle noch offenen Standard-E/A-Streams mit fclose() ordentlich geschlossen werden müssen.

23.4.2 Konvertieren von Strings in numerische Werte
Müssen Sie einen String in einen numerischen Wert konvertieren, gibt es hierfür in der Headerdatei <stdlib.h> gleich mehrere Funktionen. Einen String in einen int-Wert umwandeln können Sie mit der Funktion:

int atoi(char *string);

Ein String kann in einen long int-Wert mit der Funktion

long int atol(char *string); 

umgewandelt werden, und soll ein double-Wert aus einem String werden, ist diese Funktion verfügbar:

double atof(char *string);

Es soll ein Programm geschrieben werden, das z.B. folgende Eingabe von der Kommandozeile verarbeiten kann:

typen 5 5.55 A 255555 3E234 

Die Ausgabe sollte dann so aussehen:

Abbildung 23.1: Konvertieren von Strings in numerische Werte
Abbildung 23.1: Konvertieren von Strings in numerische Werte

Hierzu der Quellcode und die Funktionen atof() und atol():

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <limits.h>

int main(int argc, char **argv)
{
   if(argc==1)
      {
         printf("Keine Zahlen zum Auswerten vorhanden!\n");
         exit(0);
      }
   while(*++argv)
      {
         if(strchr(*argv,'.')|| strchr(*argv,'e')
                             ||strchr(*argv,'E'))
            {
               if(((atof(*argv))<=FLT_MAX)&&
                  ((atof(*argv))>=FLT_MIN))
                  {
                     printf("\n%s ist ein float-Wert\n",*argv);
                     printf("Maximaler float-Wert:%f\n",FLT_MAX);
                     printf("Kleinster pos. float-Wert : %f\n"
                                                       ,FLT_MIN);
                  }
               else if(((atof(*argv))<=DBL_MAX)&&
                       ((atof(*argv))>=DBL_MIN))
                  {
                     printf("\n%s ist ein double-Wert\n",*argv);
                     printf("Max. double-Wert:%f\n",DBL_MAX);
                     printf("Kleinster pos. double-Wert : %f\n",
                                                       DBL_MIN);
                  }
            }
         else if(((atol(*argv))<SHRT_MAX)&&
                 ((atol(*argv))>SHRT_MIN)&&(atol(*argv)!=0))
            {
               printf("\n%s ist ein short int-Wert\n",*argv);
               printf("Maximaler short int-Wert: %d\n",SHRT_MAX);
               printf("Kleinster short int-Wert: %d\n",SHRT_MIN);
            }
         else if(((atol(*argv))<LONG_MAX)&&
                 ((atol(*argv))>LONG_MIN)&&(atol(*argv)!=0))
            {
               printf("\n%s ist ein long-Wert\n",*argv);
               printf("Maximaler long-Wert : %ld\n",LONG_MAX);
               printf("Kleinster long-Wert : %ld\n",LONG_MIN);
            }
         else
            printf("\nUnbekannter Typ (%s)!\n",*argv);
      } /*Ende while*/
    return 0;
}

Es wurde hier nicht auf alle Datentypen geprüft, und anderweitig ist das Programm auch nicht wasserdicht. Aber dies würde den Rahmen dieses Kapitels sprengen. Hier wurden außerdem die (ANSI C-) Konstanten aus den Headerdateien <float.h> und <limits.h> verwendet, damit das Programm auch auf jedem System läuft. Egal, welche Grenzen gesetzt sind.

Es gibt noch weitere drei Funktionen in der Headerdatei <stdlib.h>, mit denen Sie Strings in numerische Werte konvertieren können:

double strtod(const char *string, char **endptr); 

strtod() konvertiert einen String in einen double-Wert. strtod() bricht die Analyse beim ersten Zeichen ab, das nicht mehr als Teil eines double-Werts interpretiert werden kann. Solange der Parameter endptr nicht NULL ist, wird *endptr von strtod() auf das Zeichen innerhalb von string gesetzt, durch das die Analyse abgebrochen wurde (*endptr=&abbruch_zeichen).

long strtol(const char *string, char **endptr, int basis); 

strtol() konvertiert einen String in einen long-Wert. basis legt das Zahlensystem fest, in das die Zahl umgewandelt werden soll ( Basis = 8 ist Oktalzahl, Basis = 16 (0-9,A-F) ist eine Hexadezimalzahl, Basis = 10 ist das Dezimalsystem). Für basis sind Werte von 2 bis 36 möglich. Für endptr können Sie den NULL-Zeiger angeben. Falls kein NULL-Zeiger angegeben wird, zeigt endptr auf den Rest des long-Werts (sollte einer übrig bleiben). Für diese Funktion gibt es auch den unsigned-Bruder mit derselben Bedeutung:

unsigned long stroul(const char *string,
                     char **end_ptr,int basis);

Alle Funktionen liefern im Fehlerfall 0 zurück. Ein kurzes Beispiel noch zur Funktion strtol():

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

int main()
{
   char string[] =   "256 Vorrat";
   char string2[]=   "128 Benoetigt";
   long zahl;

   zahl=strtol(string,NULL,10)-strtol(string2,NULL,10);
   printf("Ergebnis : %ld\n",zahl);
   return 0;
}

strtod() ist übrigens äquivalent zu atof() wie strtol() zu atoi() und atol(), außer im Verhalten bei einem Fehlerfall.

Eine häufige Frage lautet: Wo ist itoa(), oder wie kann ich einen Integerwert in einen String konvertieren? itoa() ist keine ANSI C-Standardfunktion und daher hängt es vom Compiler ab, ob diese Funktion vorhanden ist oder nicht. Sollten Sie aber portabel bleiben müssen, macht diese Funktion ohnehin keinen Sinn. Also, basteln Sie sich diese Funktion selbst zusammen:

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

char *it_oa(int wert, int laenge)
{
   char *ret =(char *) malloc(laenge+1 * sizeof(char));
   int i;

   for(i=0; i<laenge; i++)
      {
         ret[laenge-i-1] = (wert % 10) + 48;
         wert = wert / 10;
      }
   ret[laenge]='\0';
   return ret;
}

int main()
{
   printf("%s\n", it_oa(1234,4));
   printf("%s\n", it_oa(5432,6));
   return 0;
}

Falls für die Länge zu viele Zahlen angegeben wurden, werden diese mit voranstehenden Nullen gefüllt.

23.4.3 Zufallszahlen
Die Funktion

int rand(void);

liefert eine Pseudo-Zufallszahl im Bereich 0 bis RAND_MAX zurück. Beispielsweise mit:

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

int main()
{
   int zufallszahl,i;

   for(i=0;i<5;i++)
      printf("Die Zufallszahl lautet %d\n",zufallszahl=rand());
   return 0;
}

Bei Ausführung des Listings werden fünf verschiedene Zufallszahlen zwischen 0 und RAND_MAX ausgegeben. Aber spätestens, wenn das Programm jetzt ein zweites Mal gestartet wird, merken Sie, dass sich diese Zufallszahlen immer wiederholen. Das Problem an der Funktion rand() ist, dass diese immer denselben Startpunkt zur Berechnung der Zufallszahl benutzt.

Anders dagegen die Funktion

void srand(unsigned int startwert);

Hiermit kann der Startpunkt für die Zufallszahl selbst bestimmt werden. Ein Beispiel:

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

int main()
{
   int zufallszahl,i,startpunkt;

   printf("Geben Sie irgendeine Zahl ein : ");
   scanf("%d",&startpunkt);

   srand(startpunkt);

   for(i=0;i<5;i++)
      printf("Die Zufallszahl lautet  %d\n",zufallszahl=rand());
   return 0;
}

Jetzt wollen Sie aber sicher nicht andauernd einen Startwert für den Zufallsgenerator eingeben. Zum einen ist dies umständlich, und zum anderen bekommen Sie wieder dieselbe Zahl zurück, sollte zweimal der gleiche Wert eingegeben werden. Was eignet sich also besser als die Funktion time() für den Startwert. Und wie gehen Sie vor, falls eine Zufallszahl im Bereich zwischen 1 und 10 benötigt wird? Hier eignet sich der Modulo-Operator bestens. Ein entsprechendes Beispiel:

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

int main()
{
   int zufallszahl,i;

   srand(time(NULL));

   for(i=0;i<5;i++)
      printf("Zufallszahl lautet %d\n",zufallszahl=rand()%10+1);
   return 0;
}

Jetzt erhalten Sie schon etwas bessere Zufallszahlen im Bereich zwischen 1-10.

23.4.4 Absolutwerte, Quotient und Rest von Divisionen
Um Absolutwerte von Ganzzahlen zu ermitteln, können zwei Funktionen verwendet werden:

long int labs(long int zahl);
int abs(int zahl);

So erhalten Sie den Absolutwert zum ganzzahligen Argument zahl. Das Beispiel:

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

int main()
{
   int zahl=5;
   printf("%d\n",abs(zahl-20));
   return 0;
}

Wird der Quotient und der Rest einer Division benötigt, können folgende Funktionen verwendet werden:

div_t div(int zaehler, int nenner);
ldiv_t ldiv(long int zaehler, long int nenner);

div_t und ldiv_t sind Strukturtypen mit folgendem Inhalt:

typedef struct{
                int quot;  /* quotient */
                int rem;   /* remainder */
              } div_t;

... bzw. ...

typedef struct{
                long int quot;  /* quotient */
                long int rem;   /* remainder */
              } ldiv_t;

Damit berechnen Sie zaehler/nenner. Der Rest des Werts steht in rem, falls die Rechnung ungenau ist, und der Quotient befindet sich in quot. Ein Beispiel:

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

div_t x;

int main(void)
{
   x = div(10,3);
   printf("10 div 3 = %d Rest %d\n",  x.quot, x.rem);
   return 0;
}

Diese Funktion macht im Prinzip nichts anderes, als Folgendes zu berechnen:

quot=zaehler/nenner;
rem=zaehler%nenner;

23.4.5 Suchen und Sortieren - qsort() und bsearch()
Mit der Funktion qsort() kann ein Array der Wahl nach beliebigen Kriterien sortiert werden. Die qsort()-Funktion basiert auf dem Quicksort-Algorithmus von C.A.R. Hoare. Die Syntax von qsort(), ausführlich dokumentiert:

void qsort(
         void *array, /* Anfangsadresse des Vektors */
         size_t n,    /* Anzahl der Elemente zum Sortieren */
         size_t size, /* Größe des Datentyps der sortiert wird */
         /* Jetzt folgt die Vergleichsfunktion */
         int (*vergleich_func)(const void*, const void*)
          );

Die Bedeutungen der einzelnen Parameter dürften klar sein - bis auf die Vergleichsfunktion. Diese müssen Sie selbst implementieren. Hierzu ein einfaches Beispiel mit der Funktion qsort():

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

/* Vergleichsfunktion */
int cmp(const void *ptr1, const void *ptr2)
{
   if( *(int *)ptr1 < *(int *)ptr2 )
      return -1;
   else if( *(int *)ptr1 > *(int *)ptr2 )
      return 1;
   else
      return 0; /* Beide Elemente sind gleich */
}

int main()
{
   int wert[] = { 2,5,2,7,6,4,2 };
   int i;

   printf("Daten vor dem Sortieren\n");
   for(i=0; i<sizeof(wert)/sizeof(int); i++)
      printf("%d\t", wert[i]);
   printf("\n");

   /* Jetzt sortieren mit qsort() */
   qsort(wert, sizeof(wert)/sizeof(int), sizeof(int), cmp);

   printf("Daten nach dem Sortieren mit qsort()\n");
   for(i=0; i<sizeof(wert)/sizeof(int); i++)
      printf("%d\t", wert[i]);
   printf("\n");
   return 0;
}

Das Listing sortiert das unsortierte Integer-Feld wert, die Ausgabe des Programms bestätigt dies.

Wollen Sie ein Element wieder in Ihrem sortierten Vektor finden, dann können Sie die Funktion bsearch() verwenden. bsearch steht für "binäre Suche" und sucht die Elemente, in dem die Suche mit dem mittleren Bereich eines Arrays begonnen wird, und fährt je nach Resultat mit der Suche auf der linken oder rechten Hälfte fort. Genaueres dazu erfahren Sie in Kapitel 25, Algorithmen. Wird ein entsprechendes Element gefunden, liefert diese Funktion die Adresse zurück. Wird kein entsprechendes Element gefunden, dann wird der NULL-Zeiger zurückgegeben. Hier die Syntax:

void *bsearch(
    const void *key,  /* gesuchte Elemente */
    const void *array,/* Anfangsadresse der Tabelle zum Suchen */
    size_t n,         /* Anzahl der Elemente */
    size_t size,      /* Elementgröße */
    int (*vergleich_func)(const void*, const void*)
             );

Die Syntax ist also der Funktion qsort() recht ähnlich. Zur Abwechslung soll aber hier nach einem String in einer Stringtabelle gesucht werden, welche Sie zuvor noch mit qsort() sortieren.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Anzahl der Strings */
#define MAX 5

/* Vergleichsfunktion für zwei Strings */
int cmp_str(const void *s1, const void *s2)
{
   return (strcmp(*(char **)s1, *(char **)s2));
}

int main()
{
   char *daten[MAX], puffer[80], *ptr, *key_ptr, **key_ptrptr;
   int count;

   /* Wörter eingeben */
   printf("Geben Sie %d Wörter ein\n", MAX);
   for (count = 0; count < MAX; count++)
      {
         printf("Wort %d: ", count+1);
         fgets(puffer, 80, stdin);
         /* Speicher für das Wort Numer count reservieren */
         daten[count] =(char *) malloc(strlen(puffer)+1);
         strcpy(daten[count], strtok(puffer,"\n") );
      }
   /* Die einzelnen Wörter sortieren */
   qsort(daten, MAX, sizeof(daten[0]), cmp_str);
   /* Sortierte Daten ausgeben */
   for (count = 0; count < MAX; count++)
      printf("\nWort %d: %s", count+1, daten[count]);

   /* Jetzt nach einem Wort suchen */
   printf("\n\nNach welchem Wort wollen Sie suchen: ");
   fgets(puffer, 80, stdin);
   /* Zur Suche übergeben Sie zuerst den puffer an key,
    * danach benötigen Sie einen weiteren Zeiger, der
    * auf diesen Such-Schlüssel zeigt
    */
   key_ptr = strtok(puffer, "\n");
   key_ptrptr = &key_ptr;
   /* Der Zeiger ptr bekommt die Adresse des Suchergebnisses */
   ptr =(char *) bsearch(key_ptrptr, daten, MAX,
                         sizeof(daten[0]), cmp_str);

   if(NULL == ptr)
      printf("Kein Ergebnis stimmt mit %s überein\n",puffer);
   else
      printf("%s wurde gefunden\n",puffer);
   return 0;
}

23.4.6 system()
Um aus einem lauffähigen Programm ein anderes Programm zu starten, steht Ihnen die Funktion system() zur Verfügung. Die Syntax:

#include <stdlib.h>

int system(const char *kommandozeile);

Beim Ausführen der Funktion system() übergeben Sie den String kommandozeile an den Kommandozeilenprozessor. Konnte der Aufruf erfolgreich ausgeführt werden, gibt die Funktion einen Wert ungleich 0 zurück, ansonsten -1. Für den String kommandozeile können Sie alles angeben, was auch in der Kommandozeile erlaubt ist.

Um zu testen, ob auf Ihrem System der Kommandozeilenprozessor überhaupt zur Verfügung steht, müssen Sie die Funktion system() mit dem NULL-Zeiger aufrufen:

if(system(NULL) == 0)
   { /* Kommandoprozessor steht nicht zur Verfügung */}
else
   { /* Kommandozeilenprozessor ist bereit */ }

Wird dabei ein Wert ungleich Null zurückgegeben, können Sie die Funktion system() ohne Bedenken verwenden. Hinweis: Wenn Sie sich mit der Linux-Systemprogrammierung ein wenig auskennen, dürfte Ihnen das Verhalten der Funktion system() bekannt vorkommen. Mit der Funktion system() werden fork(), exec() und waitpid() praktisch auf einmal aufgerufen.

Zum Abschluss ein einfaches Beispiel zur Funktion system(). Auf jedem System gibt es einen Kommandozeilenbefehl, mit dem sich das vollständige Verzeichnis auflisten lässt. Unter Linux/UNIX ist dies ls und unter Windows/MS-DOS das Kommando dir. Im folgenden Listing soll dieses Kommando auf dem jeweiligen System mithilfe der Funktion system() ausgeführt werden. Hier das Listing:

#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
   #define KOMMANDO system("ls -C")
#else
   #define KOMMANDO system("dir /w")
#endif

int main()
{
  if( system(NULL) == 0)
     {
        fprintf(stderr,"Kein Kommandoprozessor vorhanden ...\n");
        exit(0);
     }
  else
     KOMMANDO; /* system() - Aufruf */
  return 0;
}

Achtung
 

Vermeiden Sie den system()-Funktionsaufruf, mit dem sich der Anwender einen eigenen String zusammenbasteln kann. Böse Anwender könnten dabei so manchen gefährlichen Konsolenbefehl ausführen lassen.

Nicht besprochen in diesem Abschnitt wurde die Funktion der Headerdatei <stdlib.h>, mit der Sie Vielbyte-Zeichen bearbeiten können, da diese recht selten benötigt wird.

Die Funktion getenv(), mit der Sie Umgebungsvariablen auslesen können, wird in Kapitel 27, CGI mit C, besprochen.

Weiter mit 23.5. <setjmp.h>            zum Inhaltsverzeichnis