Das ATOS-Magazin
 
  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