Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 20 Weitere Headerdateien und ihre Funktionen (ANSI C)
Pfeil 20.1 <assert.h> – Testmöglichkeiten und Fehlersuche
Pfeil 20.2 <ctype.h> – Zeichenklassifizierung und Umwandlung
Pfeil 20.3 Mathematische Funktionen – <math.h>, <tgmath.h> und <complex.h>
Pfeil 20.3.1 Funktionen für reelle und komplexe Gleitpunkttypen
Pfeil 20.3.2 Funktionen nur für reelle Gleitpunkttypen
Pfeil 20.3.3 Funktionen nur für komplexe Gleitpunkttypen
Pfeil 20.3.4 Typengenerische Makros – <tgmath.h>
Pfeil 20.3.5 Gleitpunktwerte klassifizieren
Pfeil 20.3.6 Makro zum Vergleichen von reellen Zahlen
Pfeil 20.3.7 Zugriff auf die Gleitpunkt-Umgebung – <fenv.h>
Pfeil 20.4 <stdlib.h>
Pfeil 20.4.1 Programmbeendigung – »exit()«, »_exit()«, »atexit()« und »abort()«
Pfeil 20.4.2 Strings in numerische Werte konvertieren
Pfeil 20.4.3 Bessere Alternative – Strings in numerische Werte konvertieren
Pfeil 20.4.4 Zufallszahlen
Pfeil 20.4.5 Absolutwerte, der Quotient und der Rest von Divisionen
Pfeil 20.4.6 Suchen und Sortieren – »qsort()« und »bsearch()«
Pfeil 20.4.7 system()
Pfeil 20.5 <locale.h> – länderspezifische Eigenheiten
Pfeil 20.6 Nicht-lokale Sprünge – <setjmp.h>
Pfeil 20.7 <signal.h>
Pfeil 20.8 <string.h> – die »mem…«-Funktionen zur Speichermanipulation
Pfeil 20.8.1 »memchr()« – Suche nach einzelnen Zeichen
Pfeil 20.8.2 »memcmp()« – bestimmte Anzahl von Bytes vergleichen
Pfeil 20.8.3 »memcpy()« – bestimmte Anzahl von Bytes kopieren
Pfeil 20.8.4 »memmove()« – bestimmte Anzahl von Bytes kopieren
Pfeil 20.8.5 »memset()« – Speicherbereich mit bestimmten Zeichen auffüllen


Rheinwerk Computing - Zum Seitenanfang

20.4 <stdlib.h> Zur nächsten ÜberschriftZur vorigen Überschrift

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


Rheinwerk Computing - Zum Seitenanfang

20.4.1 Programmbeendigung – »exit()«, »_exit()«, »atexit()« und »abort()« Zur nächsten ÜberschriftZur vorigen Überschrift

Zur normalen Beendigung eines Programms können Sie außer return folgende Funktion verwenden:

void exit(int status);

Laut ANSI-C-Standard ist es egal, ob ein Programm mit der Funktion exit() oder return beendet wird – mit dem Unterschied, dass über exit()das Programm von einer beliebigen Position im Code 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 gelöscht, die mit der Funktion tmpfile() angelegt wurden. Anschließend wird die Routine _exit() aufgerufen, und das Programm beendet sich.

Bei exit() gelten die gleichen Rückgabewerte wie beim Beenden der main()-Funktion mittels return. Ein Rückgabewert von 0 bedeutet, dass ein Programm ordentlich beendet wurde – ein Wert ungleich 0 hingegen sagt aus, dass etwas nicht ordnungsgemäß abgeschlossen wurde. Da dies, wie schon in Abschnitt 9.17 erwähnt wurde, nicht unbedingt so implementiert sein muss, sind Sie auch hier mit den Standard-C-Makros EXIT_SUCCESS (für eine erfolgreiche Beendigung) und EXIT_FAILURE (bei einem Fehler) als Parameter für exit()auf der sicheren Seite.

Theoretisch kann die Funktion _exit auch gleich aufgerufen werden. Dies entspricht allerdings nicht dem ANSI-C-Standard. Hier sehen Sie 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 sieht so aus:

#include <stdlib.h>

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

Mit atexit() wird ein sogenannter 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:

/* atexit1.c */
#include <stdio.h>
#include <stdlib.h>

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

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

int main(void) {
   atexit(funktion1);
   atexit(funktion2);
   printf("Wir beenden unser Programm\n");
   exit(EXIT_FAILURE);
   printf("Dies wird nicht mehr ausgegeben\n");
   return EXIT_SUCCESS;
}

Solch ein Beispiel macht natürlich wenig Sinn. Sie können atexit() beispielsweise verwenden, um Logdateien 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 es im folgenden Beispiel geschieht:

/* atexit2.c */
#include <stdio.h>
#include <stdlib.h>

char *memPtr;

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

int main(void) {
   memPtr =(char *) malloc(10000);

   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. */
   printf("Ein Fehler - Programm wird beendet - \n");
   exit(EXIT_FAILURE);
   printf("Wird nicht mehr ausgegeben\n");
   return EXIT_SUCCESS;
}

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

#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:

/* abort.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   abort();
   return EXIT_SUCCESS;
}

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. Ebenso werden nach einem abort()-Aufruf die Funktionen, die mit atexit() eingerichtet wurden, nicht mehr ausgeführt.

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


Rheinwerk Computing - Zum Seitenanfang

20.4.2 Strings in numerische Werte konvertieren Zur nächsten ÜberschriftZur vorigen Überschrift

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 folgender 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, dann 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 20.1 Strings in numerische Werte konvertieren

Hier sind der Quellcode und die Funktionen atof() und atol():

/* string2val1.c */
#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");
      return EXIT_FAILURE;
   }
   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);
      }
    return EXIT_SUCCESS;
}

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.


Rheinwerk Computing - Zum Seitenanfang

20.4.3 Bessere Alternative – Strings in numerische Werte konvertieren Zur nächsten ÜberschriftZur vorigen Überschrift

Sicherlich ist Ihnen an der Methode (im Listing string2va1.c) mit atof() bzw. atol() aufgefallen, dass diese Funktionen recht unflexibel sind und vor allem das Manko haben, keinen Fehler bei der Eingabe feststellen zu können. Eine häufig gestellte Frage lautet nämlich, wie man prüfen könne, ob der richtige Datentyp eingegeben wurde. Und genau diese »bessere« Alternative stellt Ihnen die Standard-Bibliothek mit den Funktionen strtod() und strtol() zur Verfügung. Beide Funktionen sind ebenfalls in der Headerdatei <stdlib.h> enthalten. Zuerst die Funktion strtod():

double strtod(
   const char * restrict string,
   char ** restrict 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 * restrict string,
   char ** restrict 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 strtoul(const char *string,
                     char **end_ptr,int basis);

Im Falle eines Fehlers liefern all diese Funktionen 0 zurück. Wird der Wertebereich des zu konvertierenden Typs unter- bzw. überschritten (bei long sind dies LONG_MIN bzw. LONG_MAX), wird errno auf ERANGE gesetzt. Somit können Sie, wenn der Rückgabewert der Funktionen nicht 0, der end_ptr gleich NULL und die Fehlervariable errno nicht ERANGE ist, mit ziemlicher Sicherheit davon ausgehen, dass die von Ihnen eingegebene Zahl richtig ist (eine 100 %ige Garantie gibt es aber nicht).

Hier ein kurzes Beispiel, das den gleichwertigen Fall von atol() und strtol() demonstrieren soll:

/* string2val2.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   char string1[] =  "256Vorrat";
   char string2[]=   "128Benoetigt";
   long zahl1, zahl2;

   zahl1 = atol(string1);
   zahl2 = strtol(string2, NULL, 10);
   printf("zahl1: %ld; zahl2: %ld\n", zahl1, zahl2);
   return EXIT_SUCCESS;
}

Hierbei wird »korrekterweise« einmal die Zahl 256 und einmal 128 ausgegeben. Beide Funktionen konvertieren also bis zum ersten Zeichen, das nicht mehr zum Datentyp gehört. Was aber, wenn eine derartige Eingabe überprüft werden soll – denn eigentlich sind ja beide Strings keine »korrekten« Zahlen, sondern nur Strings, die eben Zahlen beinhalten bzw. die mit Zahlen beginnen. Eben hier ist atol() am Ende. Mit strtol() hingegen haben Sie hierbei noch den zweiten Parameter, der Ihnen dabei helfen wird.

Daher soll hier ein Beispiel erstellt werden, das jeweils zweimal die Eingabe eines Strings erfordert und überprüft, ob es sich dabei um einen »echten« long-Wert handelt (strtol()), und noch eine Funktion hat, die überprüft, ob sich der String korrekt in einen double-Wert konvertieren lässt (mit strtod()).

/* check_input.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define CHARS 20

void chomp(char *str) {
   size_t p=strlen(str);
   /* '\n' mit '\0' ueberschreiben */
   str[p-1]='\0';
}

void is_long(char *str) {
   static long val;
   char *stop_ptr = NULL;

   val = strtoul(str, &stop_ptr, 10);
   if(errno == ERANGE) {
      printf("Werteueber- bzw. unterlauf!!!\n");
      exit(EXIT_FAILURE);
   }
   if(!val) {
      printf("Wert konnte nicht umgewandelt werden\n");
      exit(EXIT_FAILURE);
   }
   if(*stop_ptr) {
      printf("Kein korrekter long-Wert: %s\n", str);
      printf("Fehler der Umwandlung ab Pos.: %s\n", stop_ptr);
      printf("Umgewandelt ---> %ld\n", val);
   }
   else
      printf("Yeah! Korrekter long-Wert : %ld\n", val);
}

void is_double(char *str) {
   static double val;
   char *stop_ptr = NULL;

   val = strtod(str, &stop_ptr);
   if(errno == ERANGE) {
      printf("Werteueber- bzw. unterlauf!!!\n");
      exit(EXIT_FAILURE);
   }
   if(!val) {
      printf("Wert konnte nicht umgewandelt werden\n");
      exit(EXIT_FAILURE);
   }
   if(*stop_ptr) {
      printf("Kein korrekter double-Wert: %s\n", str);
      printf("Fehler der Umwandlung ab Pos.: %s\n", stop_ptr);
      printf("Umgewandelt ---> %lf\n", val);
   }
   else
      printf("Yeah! Korrekter double-Wert : %lf\n", val);
}

int main(void) {
   char val[CHARS];
   /* Testen eines long-Wertes */
   printf("Bitte geben Sie einen long-Wert ein : ");
   fgets(val, CHARS, stdin);
   chomp(val);
   is_long(val);
   /* Gleiches nochmals mit einem double-Wert */
   printf("Bitte geben Sie einen double-Wert ein : ");
   fgets(val, CHARS, stdin);
   chomp(val);
   is_double(val);

   return EXIT_SUCCESS;
}

Sofern Sie jetzt hierbei auf andere Typen wie beispielsweise int überprüfen wollen, müssen Sie dazu die Limit-Konstanten der Headerdatei <limits.h> (beispielsweise INT_MIN oder INT_MAX) verwenden und mit dem long-konvertieren Wert vergleichen.


Hinweis

Sofern Ihr Compiler den ISO-C99-Standard versteht, finden Sie hierbei noch weitere solcher sehr nützlichen Funktionen mit strtoll() (String to long long), strtoimax() (String to intmax_t; Funktion in <inttypes.h>), strtoumax() (String to uintmax_t; Funktion in <inttypes.h>), strtof() (String to float) und strtold() (String to long double). Mehr dazu entnehmen Sie dann der Dokumentation Ihres Compilers (beispielsweise der Manual-Page). Ebenso stehen Ihnen auch die Funktionen zur Konvertierung von Zahlen und breiten Strings zur Verfügung. Die entsprechenden Gegenstücke haben alle das Präfix w vorangestellt und sind in der Headerdatei <wchar.h> definiert. Lediglich zu atoi() gibt es hier kein Gegenstück für breite Zeichen.


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:

/* my_itoa.c */
#include <stdio.h>
#include <stdlib.h>

char *my_itoa(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(void) {
   printf("%s\n", my_itoa(1234,4));
   printf("%s\n", my_itoa(5432,6));
   return EXIT_SUCCESS;
}

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

Da ich in diesem Kapitel nur auf die Funktionen zum Konvertieren zwischen Zahlen und Strings aus der Headerdatei <stdlib.h> eingegangen bin, es aber auch weitere Funktionen für breite Zeichen in <wchar.h> und ganzzahlige Typen maximaler Breite in <inttypes.h> gibt (siehe Anhang B), folgt hier eine Tabelle mit einem Überblick, mit welcher Funktion Sie am besten einen String in welchen Typ konvertieren.


Tabelle 20.11 Übersicht zu Funktionen zum Konvertieren von Strings in Zahlen

String zu... <stdlib.h> <inttypes.h> 1) <wchar.h> 1)
int
atoi()
long
strtol()
wcstol()
unsigned long
strtoul()
wcstoul()
long long
strtoll()
wcstoll()
unsigned long long
strtoull()
wcstoull()
float
strtof()
wcstof()
double
strtod()
wcstod()
long double
strtold()
wcstold()
intmax_t
strtoimax()
wcstoimax()
uintmax_t
strtoumax()
wcstoumax()

1) Headerdatei und Funktionen erst seit dem C99-Standard verfügbar



Rheinwerk Computing - Zum Seitenanfang

20.4.4 Zufallszahlen Zur nächsten ÜberschriftZur vorigen Überschrift

Die Funktion

int rand(void);

liefert eine Pseudo-Zufallszahl im Bereich 0 bis RAND_MAX zurück, beispielsweise mit:

/* zufall1.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   int zufallszahl, i;

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

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);

Mit ihr kann der Startpunkt für die Zufallszahl selbst bestimmt werden.

Ein Beispiel:

/* zufall2.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   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 EXIT_SUCCESS;
}

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 wäre:

/* zufall3.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
   int zufallszahl, i;

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

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


Rheinwerk Computing - Zum Seitenanfang

20.4.5 Absolutwerte, der Quotient und der Rest von Divisionen Zur nächsten ÜberschriftZur vorigen Überschrift

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:

/* absolut.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   int zahl = 5;

   printf("%d\n", abs(zahl-20));
   return EXIT_SUCCESS;
}

Werden 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:

/* division.c */
#include <stdio.h>
#include <stdlib.h>

int main(void) {
   div_t x = div(10,3);


   printf("10 div 3 = %d Rest %d\n",  x.quot, x.rem);
   return EXIT_SUCCESS;
}

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

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

Neu ab dem C99-Standard sind auch die entsprechenden Gegenstücke imaxabs() und imaxdiv() für den Typ intmax_t. Beide Funktionen sind in der Headerdatei <inttypes.h> definiert und haben folgende Syntax:

#include <inttypes.h>

intmax_t imaxabs(intmax_t j);
imaxdiv_t imaxdiv(intmax_t numer, intmax_t denom);

Rheinwerk Computing - Zum Seitenanfang

20.4.6 Suchen und Sortieren – »qsort()« und »bsearch()« Zur nächsten ÜberschriftZur vorigen Überschrift

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. Hier sehen Sie 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():

/* qsort.c */
#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(void) {
   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 EXIT_SUCCESS;
}

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, indem es mit der Suche im mittleren Bereich eines Arrays beginnt und je nach Resultat mit der Suche auf der linken oder rechten Hälfte fortfährt. Genaueres dazu erfahren Sie in Kapitel 22, »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 folgt 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
   // Jetzt folgt die Vergleichsfunktion
   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, die Sie zuvor noch mit qsort() sortieren.

/* bsearch.c */
#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(void) {
   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 Nummer 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 EXIT_SUCCESS;
}

Rheinwerk Computing - Zum Seitenanfang

20.4.7 system() topZur vorigen Überschrift

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

#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) {
   // Kommandozeilenprozessor 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 folgt 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 heißt das Kommando dir. Im folgenden Listing soll dieses Kommando auf dem jeweiligen System mithilfe der Funktion system() ausgeführt werden.

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

int main(void) {
  if( system(NULL) == 0) {
     fprintf(stderr,"Kein Kommandozeilenprozessor vorhanden \n");
     return EXIT_FAILURE;
  }
  else
     KOMMANDO;
  return EXIT_SUCCESS;
}

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 Multi-Byte-Zeichen bearbeiten können, da diese recht selten benötigt wird.

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



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: C von A bis Z

 C von A bis Z
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: C/C++






 C/C++


Zum Rheinwerk-Shop: Einstieg in C






 Einstieg in C


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern