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

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 22 Weitere Headerdateien und ihre Funktionen (ANSI C)
  gp 22.1 <assert.h> -– Testmöglichkeiten und Fehlersuche
  gp 22.2 <ctype.h> – Zeichenklassifizierung und Umwandlung
  gp 22.3 Mathematische Funktionen – <math.h>
  gp 22.4 <stdlib.h>
    gp 22.4.1 Programmbeendigung – exit(), _exit(), atexit() und abort()
    gp 22.4.2 Konvertieren von Strings in numerische Werte
    gp 22.4.3 Bessere Alternative – Konvertieren von Strings in numerische Werte
    gp 22.4.4 Zufallszahlen
    gp 22.4.5 Absolutwerte, Quotient und Rest von Divisionen
    gp 22.4.6 Suchen und Sortieren – qsort() und bsearch()
    gp 22.4.7 system()
  gp 22.5 <locale.h> – Länderspezifische Eigenheiten
  gp 22.6 <setjmp.h>
  gp 22.7 <signal.h>
  gp 22.8 <string.h> – Die mem…-Funktionen zur Speichermanipulation
    gp 22.8.1 memchr() – Suche nach einzelnen Zeichen
    gp 22.8.2 memcmp() – Bestimmte Anzahl von Bytes vergleichen
    gp 22.8.3 memcpy() – Bestimmte Anzahl von Bytes kopieren
    gp 22.8.4 memmove() – Bestimmte Anzahl von Bytes kopieren
    gp 22.8.5 memset() – Speicherbereich mit bestimmten Zeichen auffüllen
  gp 22.9 Erweiterter ANSI C-Standard (ANSI C99)
    gp 22.9.1 Neue elementare Datentypen
    gp 22.9.2 <stdint.h> – Ganzzahlige Typen mit vorgegebener Breite
    gp 22.9.3 Komplexe Gleitpunkttypen
    gp 22.9.4 <iso646.h> – Symbolische Konstanten für Operatoren
    gp 22.9.5 Deklaration von Bezeichnern
    gp 22.9.6 inline-Funktionen
    gp 22.9.7 Vordefinierte Makros
    gp 22.9.8 <math.h> – Neue Funktionen
    gp 22.9.9 <wchar.h> – (NA1)
    gp 22.9.10 <wctype.h> (NA1)
    gp 22.9.11 <fenv.h> – Kontrolle der Gleitpunkzahlen-Umgebung
    gp 22.9.12 <inttypes.h> – Für genauere Integer-Typen
    gp 22.9.13 <tgmath.h> – Typengenerische Mathematik-Funktionen
    gp 22.9.14 Zusammenfassung


Galileo Computing - Zum Seitenanfang

22.4 <stdlib.h>  downtop

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


Galileo Computing - Zum Seitenanfang

22.4.1 Programmbeendigung – exit(), _exit(), atexit() und abort()  downtop

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.

Auch bei exit() gilt Gleiches mit dem Rückgabewert als Parameter wie schon 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 11.7 erwähnt, nicht unbedingt so implementiert sein muss, ist man auch hier mit den Standard-C Makros EXIT_SUCCESS (für eine erfolgreiche Beendigung) und EXIT_FAILURE (bei einem Fehler) auf der sicheren Seite als Parameter für exit().

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:

/* 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 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:

/* 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, 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:

/* 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.


Galileo Computing - Zum Seitenanfang

22.4.2 Konvertieren von Strings in numerische Werte  downtop

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
Hier klicken, um das Bild zu Vergrößern

Abbildung 22.1   Konvertieren von Strings in numerische Werte

Hierzu 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.


Galileo Computing - Zum Seitenanfang

22.4.3 Bessere Alternative – Konvertieren von Strings in numerische Werte  downtop

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 gerne gestellte Frage lautet nämlich, wie man auf die Richtigkeit der Eingabe eines Datentyps prüfen könne. 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 *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);

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 mit ziemlicher Sicherheit davon ausgehen, wenn der Rückgabewert der Funktionen nicht 0, der end_ptr gleich NULL und die Fehlervariable errno nicht ERANGE ist, dass die von Ihnen eingegebene Zahl richtig ist (100%-ige Garantie gibt es aber nicht).

Hier ein kurzes Beispiel, welches 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, was nicht mehr zum Datentypen 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. welche 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, welches 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, welche ü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' überschreiben */
   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("Werteüber- bzw. Unterlauf!!!\n");
      exit(EXIT_FAILURE);
   }
   if(!val) {
      printf("Wert konnte nicht umgewandelt werden\n");
      exit(EXIT_FAILURE);
   }
   if(*stop_ptr != NULL) {
      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("Werteüber- bzw. Unterlauf!!!\n");
      exit(EXIT_FAILURE);
   }
   if(!val) {
      printf("Wert konnte nicht umgewandelt werden\n");
      exit(EXIT_FAILURE);
   }
   if(*stop_ptr != NULL) {
      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 eine long-Wert ein : ");
   fgets(val, CHARS, stdin);
   chomp(val);
   is_long(val);
   /* Gleiches nochmals mit einem double-Wert */
   printf("Bitte geben Sie eine double-Wert ein : ");
   fgets(val, CHARS, stdin);
   chomp(val);
   is_double(val);
   return EXIT_SUCCESS;
}

Sofern Sie jetzt hierbei auf andere Typen wie bspw. int überprüfen wollen, müssen Sie dazu die Limit-Konstanten der Headerdatei <limits.h> (bspw. 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 int max), strtof() (string to float) und strtold (string to long double). Mehr dazu entnehmen Sie dann der Dokumentation Ihres Compilers (bspw. Manual-Page).


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.


Galileo Computing - Zum Seitenanfang

22.4.4 Zufallszahlen  downtop

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

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

/* 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–10.


Galileo Computing - Zum Seitenanfang

22.4.5 Absolutwerte, Quotient und Rest von Divisionen  downtop

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:

/* absolut.c */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
   int zahl = 5;
   printf("%d\n", abs(zahl-20));
   return EXIT_SUCCESS;
}

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:

/* 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;

Galileo Computing - Zum Seitenanfang

22.4.6 Suchen und Sortieren – qsort() und bsearch()  downtop

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():

/* 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 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 24, 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
   // 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, welche 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 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 EXIT_SUCCESS;
}

Galileo Computing - Zum Seitenanfang

22.4.7 system()  toptop

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:

/* 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 Kommandoprozessor 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 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 26, CGI mit C, besprochen.

 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2006
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.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de