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 |