zurück zum News-Archiv
> Anfang zurück vorwärts Ende ## ANSI C - Ein Programmierkurs - Teil XI #### Formatstring Der Formatstring enthält einen Text, der ausgegeben bzw. genau so in der Eingabe vorhanden sein muss. In diesem Text können Platzhalter angegeben werden, an deren Stelle Variablen ausgegeben bzw. eingelesen werden. Jeder dieser Platzhalter beginnt mit dem Zeichen '%' und endet mit einem Zeichen, das den Datentyp kodiert. Zwischen dem Prozentzeichen und dem Typ können noch folgende Angaben stehen: Flags, die das Ausgabeformat genauer spezifizieren, einer Ausgabebreite, einer Genauigkeit und einem Flag h oder l für eine Größenangabe. Für jeden Platzhalter muss in der variablen Parameterliste auch eine entsprechende Variable folgen! Hier noch einmal die Kurzzusammenfassung eines Platzhalters:
>      % [Flags] [Breite] [.Präzision] [h|l|L] Typ
  Als Flags sind folgende Zeichen möglich: Minus '-', Plus '+', Doppelkreuz '#' und Leerzeichen ' '. Die Flags können in beliebiger Folge und Kombination erscheinen und haben die folgenden Bedeutung: - Die Variable wird linksbündig ausgegeben. Sind Leerzeichen für die gewünschte Breite nötig, werden sie rechts eingefügt. Ohne dieses Flag arbeitet printf immer rechtsbündig. + Eine numerische Ausgabe wird mit einem Vorzeichen gemacht. Ohne dieses Flag werden nur negative Werte mit einem Vorzeichen versehen. ' ' (Leerzeichen) Positive Werte werden mit einem führenden Leerzeichen ausgegeben. Ohne dieses Flag stellt printf positiven Werten überhaupt nichts voran. # Die Konvertierung des Arguments erfolgt in alternativer Form. Die Tabelle gibt an, für welche Typen welche Wirkungen eintritt:
  c,s,d,i,u | (keine)
  o | Voranstellen von '0'
  x,X | Voranstellen von "0x" bzw. "0X"
  e,E,f | es wird immer ein Dezimalpunkt ausgegeben
  g,G | wie e bzw. E und folgende Nullen werden nicht unterdrückt
Die Breite legt die minimale Zahl auszugebender Zeichen fest. Sie kann in zwei Formen erfolgen, entweder direkt als Zahl innerhalb des Formatstrings oder über ein Sternchen '*'. Bei der Verwendung von '*' erwartet printf einen zusätzlichen Parameter, der in der Liste direkt vor dem auszugebenden Wert stehen und den Typ int haben muss. Der Breite als Zahlkonstante kann die Ziffer 0 vorangestellt werden, dann wird mit der Ziffer '0' und nicht mit Leerzeichen aufgefüllt. Erzeugt die Ausgabe weniger Zeichen, als die Breite angibt, wird mit Leerzeichen aufgefüllt. Erzeugt die Ausgabe mehr Zeichen, wird die Breite ignoriert. Es werden keine Zeichen abgeschnitten. Die Präzision wird immer mit einem Dezimalpunkt '.' eingeleitet und gibt die maximale Anzahl Zeichen an, die ausgegeben werden soll. Auch hier kann anstelle der Konstanten der Stern '*' angegeben werden, wenn die Angabe über einen zusätzlichen Parameter erfolgt. Die Buchstaben 'h', 'l' oder 'L' geben an, ob es sich bei dem Parameter um einen short, long oder long double handelt. Der Typ kann einen der folgenden Werte annehmen:
d | Integer | signed int (dezimal);
i | Integer | signed int (dezimal);
o | Integer | unsigned int (oktal);
u | Integer | unsigned int (dezimal);
x | Integer | unsigned int (hexadezimal), Buchstaben a..f;
X | Integer | unsigned int (hexadezimal), Buchstaben A..F.
f | Fließkomma | vorzeichenbehafteter Wert der Form [-]dddd.dddd
e | Fließkomma | vorzeichenbehafteter Wert der Form [-]d.dddd e [+|-]ddd
g | Fließkomma | vorzeichenbehafteter Wert im e- oder f-Format
E | Fließkomma | wie e, aber mit dem Zeichen E vor dem Exponenten
G | Fließkomma | wie g, aber mit dem Zeichen E vor dem Exponenten
c | Zeichen | einzelnes Zeichen
s | String | Nullterminierte Zeichenkette
% | (nichts) | Ausgabe des Zeichens %
n | *int | speichert die Anzahl der bis jetzt ausgegebenen Zeichen
p | Zeiger | gibt den Parameter als Zeiger in hexadezimaler Form aus.
> #### Ein-/Ausgabe in `cookie.c` Mit dem Wissen über die stdio können nun die Funktionen für einen Cookie vervollständigt werden. Es fehlten ja noch die Eingabefunktion und die Funktionen zum Schreiben in Dateien und Lesen aus Dateien.
>     #include <string.h>
>     #include <stdio.h>
>     #include "cookie.h"

>     void CookieSetL(CookieEntry *Cookie,long Name,long Value)
>     {
>        Cookie->name.name_long = Name;
>        Cookie->value = Value;
>     }

>     void CookieSetS(CookieEntry *Cookie,char *Name,long Value)
>     {
>        memcpy(Cookie->name.name_array,Name,4);
>        Cookie->value = Value;
>     }

>     void CookiePrint(CookieEntry *Cookie)
>     {
>        printf("Name des Cookies: %d\n", Cookie->name.name_long);
>        printf("Wert des Cookies: %d\n", Cookie->value);
>     }

>     void CookieInput(CookieEntry *Cookie)
>     {
>        printf("Name des Cookies (long): ");
>        scanf("%ld\n", &(Cookie->name.name_long));
>        printf("Wert des Cookies (long): ");
>        scanf("%ld\n", &(Cookie->value));
>     }

>     int CookieIsNullCookie(CookieEntry *Cookie)
>     {
>        return Cookie->name.name_long == NULL_COOKIE;
>     }

>     int CookieIsCookie(CookieEntry *Cookie,long Name)
>     {
>        return Cookie->name.name_long == Name;
>     }

>     int CookieRead(CookieEntry *Cookie,FILE *stream)
>     {
>        return(fread(Cookie,sizeof(CookieEntry),1,stream) == 1);
>     }

>     int CookieWrite(CookieEntry *Cookie,FILE *stream)
>     {
>        return(fwrite(Cookie,sizeof(CookieEntry),1,stream) == 1);
>     }
> ### `ctype.h` Die `ctype.h` definiert Funktionen zum Testen von Zeichen. Die Prüfroutinen geben einen Wert ungleich 0 zurück, wenn der Test erfolgreich war. Die Funktionen sind nur für 7-Bit ASCII definiert, d. h., die Umlaute sind keine Buchstaben! Ein Kleinbuchstabe umfasst nur den Bereich von 'a' .. 'z'. `int isalnum( int c );` Diese Funktion entspricht `isalpha(c) || isdigit(c)` `int isalpha( int c );` Diese Funktion entspricht `islower(c) || isupper(c)` `int iscntrl( int c );` Ist das Zeichen ein Steuerzeichen, also kleiner als das Leerzeichen ' '? `int isdigit( int c );` Ist das Zeichen eine Ziffer? `int isgraph( int c );` Ist das Zeichen ein sichtbares Zeichen? Also auch kein Leerzeichen. `int isprint( int c );` Ist das Zeichen darstellbar? Also keine Steuerzeichen, aber es kann ein Leerzeichen sein. `int ispunct( int c );` Ist das Zeichen ein darstellbares Zeichen mit Ausnahme von Buchstaben, Ziffer und Leerzeichen? `int islower( int c );` Ist das Zeichen ein Kleinbuchstabe? `int isupper( int c );` Ist das Zeichen ein Großbuchstabe? `int ixdigit( int c );` Ist das Zeichen eine hexadezimale Ziffer? `int isspace( int c );` Ist das Zeichen ein Leerzeichen, Seitenvorschub '`\f`', Zeilentrenner (Linefeed) '`\n`', Wagenrücklauf (Return) '`\r`', Tabulator '`\t`' oder ein Vertikaltabulator '`\v`'? Diese Zeichen nennt man auch white space, da sie die Cursorposition auf dem Bildschirm verändern, aber selbst keine Ausgabe produzieren. `int tolower( int c );` Diese Funktion wandelt c in einen Kleinbuchstaben. `int toupper( int c );` Diese Funktion wandelt c in einen Großbuchstaben. #### Testfunktionen in `cookie.c` Mit den Testfunktionen lässt sich nun prüfen, ob der Name des Cookies nur aus darstellbaren Zeichen ohne Leerzeichen besteht. Wenn ja, könnte es sich um ein Kürzel handeln und der Name des Cookies lässt sich auch als Text ausgeben. Da im Cookie selbst der Platz für die abschließende Null fehlt, wird die Textausgabe im nachfolgenden Kapitel über `string.h` besprochen.
>     #include <string.h>
>     #include <stdio.h>
>     #include <ctype.h>
>     #include "cookie.h"

>     void CookieSetL(CookieEntry *Cookie,long Name,long Value)
>     {
>        Cookie->name.name_long = Name;
>        Cookie->value = Value;
>     }

>     void CookieSetS(CookieEntry *Cookie,char *Name,long Value)
>     {
>        memcpy(Cookie->name.name_array,Name,4);
>        Cookie->value = Value;
>     }

>     void CookiePrint(CookieEntry *Cookie)
>     {
>        printf("Name des Cookies: %d\n", Cookie->name.name_long);
>        printf("Wert des Cookies: %d\n", Cookie->value);
>        if (isgraph(Cookie->name.name_array[0]) &&
>            isgraph(Cookie->name.name_array[1]) &&
>            isgraph(Cookie->name.name_array[2]) &&
>            isgraph(Cookie->name.name_array[3]))
>         {
>            printf("Der Cookie besteht aus darstellbaren Zeichen\n");
>         }
>     }

>     void CookieInput(CookieEntry *Cookie)
>     {
>        printf("Name des Cookies (long): ");
>        scanf("%ld\n", &(Cookie->name.name_long));
>        printf("Wert des Cookies (long): ");
>        scanf("%ld\n", &(Cookie->value));
>     }

>     int CookieIsNullCookie(CookieEntry *Cookie)
>     {
>        return Cookie->name.name_long == NULL_COOKIE;
>     }

>     int CookieIsCookie(CookieEntry *Cookie,long Name)
>     {
>        return Cookie->name.name_long == Name;
>     }

>     int CookieRead(CookieEntry *Cookie,FILE *stream)
>     {
>        return(fread(Cookie,sizeof(CookieEntry),1,stream) == 1);
>     }

>     int CookieWrite(CookieEntry *Cookie,FILE *stream)
>     {
>        return(fwrite(Cookie,sizeof(CookieEntry),1,stream) == 1);
>     }
> ### `string.h` In `string.h` sind Funktionen für die Arbeit mit Zeichenketten (Strings) und Speicherblöcken definiert. Wenn nichts anderes beschrieben ist, muss für jede dieser Funktionen der Speicher für das Ziel vom Aufrufer angelegt werden! Dabei muss auch der Platz für die Null als String-Ende berücksichtigt werden. Wenn nichts anderes beschrieben ist, liefern die Funktionen das Ziel als Ergebnis. Die `mem...`-Funktionen operieren auf einem beliebigen Speicherblock.
>     void richtig(void)
>     {  char s[6];

>        strcpy(s,"Hallo");
>     }

>     void falsch(void)
>     {  char *s;

>        strcpy(s,"Hallo");
>     }
> `char *strcat( char *s1, const char *s2 );` Diese Funktion hängt den String s2 an den String s1 an. `char *strncat( char *s1, const char *s2, size_t n );` Diese Funktion hängt maximal n Zeichen des Strings s2 an den String s1. `int strcmp( const char *s1, const char *s2 );` Diese Funktion vergleicht beide Strings. Ist s1<s2, liefert sie einen Wert < 0, bei s2 > s1 einen Wert > 0 und für Gleichheit 0. `int strncmp( const char *s1, const char *s2, size_t n );` Diese Funktion vergleicht maximal n Zeichen aus beiden Strings. Ist s1<s2 für die ersten n Zeichen, liefert sie einen Wert < 0, bei s2 > s1 einen Wert > 0 und für Gleichheit 0. `char *strcpy( char *s1, const char *s2 );` Diese Funktion kopiert den String s2 nach s1. `char *strncpy( char *s1, const char *s2, size_t n );` Diese Funktion kopiert maximal n Zeichen von s2 nach s1. `size_t strlen( const char *s );` Diese Funktion liefert die Länge von s. `char *strchr( const char *s, int c );` Diese Funktion liefert einen Zeiger auf das erste Auftreten von c in s oder NULL, falls c nicht in s vorkommt. `char *strrchr( const char *s, int c );` Diese Funktion liefert einen Zeiger auf das letzte Auftreten von c in s. oder NULL, falls c nicht in s vorkommt. `size_t strspn( const char *s, const char *set );` Diese Funktion liefert die Anzahl der Zeichen am Anfang von s, die sämtlich in set vorkommen. `size_t strcspn( const char *s, const char *set );` Diese Funktion liefert die Anzahl der Zeichen am Anfang von s, die sämtlich nicht in set vorkommen. `char *strpbrk( const char *s, const char *set );` Diese Funktion liefert einen Zeiger auf das erste Zeichen, das in set vorkommt oder NULL, falls keines vorkommt. `char *strstr( const char *src, const char *sub );` Diese Funktion liefert einen Zeiger auf das erste Vorkommen von sub in src oder NULL, falls sub nicht in set vorkommt. `char *strtok( char *str, const char *set );` Diese Funktion durchsucht s nach Zeichenfolgen, die durch ein Zeichen aus set begrenzt sind. Der erste Aufruf wird mit dem zu untersuchenden String str gemacht und liefert die erste Zeichenfolge. Anschließend wird strtok solange mit NULL für str aufgerufen, wie sie einen Wert ungleich Null und damit das nächste Token liefert. Die Auswahl an Trennern in set kann bei jedem Aufruf verschieden sein. `char *strerror( int errnum );` Diese Funktion liefert einen Zeiger auf den Fehlertext, der für den Fehler errnum definiert ist. Bei der Fehlernummer kann es sich um den Wert von errno handeln. `void *memchr( const void *ptr, int val, size_t len );` Diese Funktion liefert einen Zeiger auf das erste Auftreten von val im Speicherblock ptr oder NULL, falls val nicht in den ersten len Zeichen von ptr vorkommt. `int memcmp( const void *ptr1, const void *ptr2, size_t len );` Diese Funktion vergleicht die ersten len Zeichen von ptr2 mit ptr1. Das Ergebnis ist das Gleiche wie bei strncmp. `void *memcpy( void *dest, const void *src, size_t len );` Diese Funktion kopiert len Zeichen von src nach dest. `void *memmove( void *dest, const void *src, size_t len );` Diese Funktion kopiert wie memcpy, funktioniert aber auch, wenn sich src und dst überlappen. `void *memset( void *ptr, int val, size_t len );` Diese Funktion setzt len Zeichen von ptr auf den Wert val. #### Stringfunktionen in `cookie.c` Mit den Stringfunktionen lassen sich die 4 Bytes des Cookie-Ids in einen String kopieren und damit auch als String ausgeben, wenn sie aus darstellbaren Zeichen ohne Leerzeichen bestehen. Für einen Benutzer ist es einfacher, mit dem Kürzel zu arbeiten, da es aussagekräftiger ist und sich damit auch leichter merken lässt. Die Kopie wurde mit memcpy durchgeführt, da die abschließende Null im Cookie nicht vorhanden ist und deshalb immer 4 Bytes kopiert werden müssen.
>     #include <string.h>
>     #include <stdio.h>
>     #include <ctype.h>
>     #include "cookie.h"

>     void CookieSetL(CookieEntry *Cookie,long Name,long Value)
>     {
>        Cookie->name.name_long = Name;
>        Cookie->value = Value;
>     }

>     void CookieSetS(CookieEntry *Cookie,char *Name,long Value)
>     {
>        memcpy(Cookie->name.name_array,Name,4);
>        Cookie->value = Value;
>     }

>     void CookiePrint(CookieEntry *Cookie)
>     {
>        char Name[5];

>        if (isgraph(Cookie->name.name_array[0]) &&
>            isgraph(Cookie->name.name_array[1]) &&
>            isgraph(Cookie->name.name_array[2]) &&
>            isgraph(Cookie->name.name_array[3]))
>        {
>           memcpy(Name,Cookie->name.name_array,4);
>           Name[4] = '\0';
>           printf("Name des Cookies: %s\n", Name);
>        }
>        else
>           printf("Name des Cookies: %d\n", Cookie->name.name_long);
>        printf("Wert des Cookies: %d\n", Cookie->value);
>     }

>     void CookieInput(CookieEntry *Cookie)
>     {
>        printf("Name des Cookies (long): ");
>        scanf("%ld\n", &(Cookie->name.name_long));
>        printf("Wert des Cookies (long): ");
>        scanf("%ld\n", &(Cookie->value));
>     }

>     int CookieIsNullCookie(CookieEntry *Cookie)
>     {
>        return Cookie->name.name_long == NULL_COOKIE;
>     }

>     int CookieIsCookie(CookieEntry *Cookie,long Name)
>     {
>        return Cookie->name.name_long == Name;
>     }

>     int CookieRead(CookieEntry *Cookie,FILE *stream)
>     {
>        return(fread(Cookie,sizeof(CookieEntry),1,stream) == 1);
>     }

>     int CookieWrite(CookieEntry *Cookie,FILE *stream)
>     {
>        return(fwrite(Cookie,sizeof(CookieEntry),1,stream) == 1);
>     }
> #### Stringfunktionen in `cjar.c` Auch für die Suche nach einem bestimmten Cookie werden die Stringfunktionen benötigt. Da sich ein Vergleich eines long schneller durchführen lässt als ein strncmp, wurde in `cookie.c` nur eine Vergleichsfunktion für den Namen als long implementiert. Deshalb müssen die 4 Bytes des Cookie-Ids von der Zeichenkette in einen long kopiert werden. Dazu wird eine long-Variable deklariert und mittels memcpy werden die 4 Bytes des Cookienamens an den Speicherplatz kopiert, den der long belegt. Da sich solche Tricks auf eine bestimmte Größe der Datentypen verlassen, sind sie nicht unbedingt portabel. Außerdem erschweren sie das Verständnis des Programms. In diesem Fall ist der Trick aber gerechtfertigt, weil der Name des Cookies zwar in der Regel ein Kürzel ist, aber keinen nullterminierten C-String darstellt.
>     #ifdef __TURBOC__
>     #include <tos.h>
>     #else
>     #ifdef __GNUC__
>     #include <osbind.h>
>     #else
>     #include <tosbind.h>
>     #endif
>     #endif

>     #include <stddef.h>
>     #include <string.h>
>     #include cookie.h
>     #include cjar.h

>     #define _p_cookies (void *)0x5a0l

>     CookieEntry *CookieJar = NULL;

>     int CjarInit(void)
>     {  long OldStack;

>        OldStack = Super(0L);
>        CookieJar = *((CookieEntry**)_p_cookies);
>        Super((void *)OldStack);
>        return (CookieJar != NULL);
>     }

>     CookieEntry *CjarSearchL(long Name)
>     {  CookieEntry *AktCookie;

>        if (CookieJar != NULL)
>           CjarInit();
>        if (CookieJar != NULL)
>        {
>           AktCookie = CookieJar;
>           while (!CookieIsNullCookie(AktCookie) &&
>                  !CookieIsCookie(AktCookie,Name))
>           {
>              AktCookie++;
>           }
>           if (CookieIsNullCookie(AktCookie))
>              return NULL;
>           else
>              return AktCookie;
>        }
>        else
>           return NULL;
>     }

>     CookieEntry *CjarSearchS(char *Name)
>     {  long CookieNameAsLong;

>        memcpy(&CookieNameAsLong, Name, 4);
>        return CjarSearchL(CookieNameAsLong);
>     }

>     void CjarTraverse(CookieAction ActionFkt)
>     {  CookieEntry *AktCookie;

>        if (CookieJar != NULL)
>           CjarInit();
>        if (CookieJar != NULL)
>        {
>           AktCookie = CookieJar;
>           while (!CookieIsNullCookie(AktCookie))
>           {
>              (*ActionFkt)(AktCookie);
>              AktCookie++;
>           }
>        }
>     }
Die nächste Folge zeigt Einblicke in die math.h. Dort sind häufig benötigte mathematische Funktionen zusammengefasst. 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