Unterabschnitte

18.2 Lösungen

Die angegebenen Lösungen sind natürlich nicht die einzig möglichen; bei den meisten Aufgaben kann man ebenso Dutzende weitere sinnvolle Lösungen finden. Von diesem Kapitel kann man sich also höchstens Anregungen holen oder eigene Lösungen mit den hier angegebenen vergleichen.

18.2.1 Unser erstes

/* loerst1.c 30. 7.95 kw
 * Musterloesung zur ersten Aufgabe
 */

/* die verwendete Funktion deklarieren
 * (nicht unbedingt noetig, aber zweckmaessig):
 */
int puts( char *string );

/* Hier beginnt das Hauptprogramm:                          */
main()
{
  puts( "Hallo!" );
}

18.2.2 Zeilenvorschub

/* lozeil.c 30. 7.95 kw
 */

/* puts() deklarieren:
 */
int puts( const char *string );

main()
{
  puts( "Hallo!\n\n\n    Ich bin Dein dummer Rechner!" );
}

18.2.3 Ganze Zahlen ausgeben (1)

/* loganz1.c 30. 7.95 kw
 */

/* printf() vereinbaren:
 */
#include <stdio.h>

/* Hier wird ein Schleifenzaehler i vereinbart.
 * Dieser soll von 1 bis 10 laufen; sein Quadrat wird mit
 * printf() ausgegeben.
 */
main()
{
  int i;      /* Schleifenzaehler                     */

  for( i=1; i<=10; i=i+1 )   /* Schleife von 1 bis 10 */
    printf( "Das Quadrat von %d ist %d\n",
            i,
            i*i
            );
}

18.2.4 Ganze Zahlen ausgeben (2)

/* loganz2.c 30. 7.95 kw
 */

/* printf() vereinbaren:
 */
#include <stdio.h>

/* Hier wird ein Schleifenzaehler i vereinbart.
 * Dieser soll von 1 bis 4 laufen; sein Quadrat wird mit
 * printf() negativ und positiv ausgegeben.
 */
main()
{
  int i;      /* Schleifenzaehler                     */

  for( i=1; i<=4; i=i+1 )   /* Schleife von 1 bis 4   */
    printf( "%d\n%d\n",
            -i*i,
            i*i
            );
}

18.2.5 Tage im Monat

Die direkteste Lösung wäre wahrscheinlich:

/* lotim1.c 30. 7.95 kw
 */

int tage( int monat )
{
  if( monat==1  ) return 31;
  if( monat==2  ) return 28;
  if( monat==3  ) return 31;
  if( monat==4  ) return 30;
  if( monat==5  ) return 31;
  if( monat==6  ) return 30;
  if( monat==7  ) return 31;
  if( monat==8  ) return 31;
  if( monat==9  ) return 30;
  if( monat==10 ) return 31;
  if( monat==11 ) return 30;
  if( monat==12 ) return 31;
  return 0;
}

Etwas kompakter könnte man schreiben:

/* lotim2.c 30. 7.95 kw
 */

int tage( int monat )
{
  if( monat==1 || monat==3  ||
      monat==5 || monat==7  ||
      monat==8 || monat==10 ||
      monat==12
      )
    return 31;
  if( monat== 2 )
    return 28;
  if( monat==4 || monat==6  ||
      monat==9 || monat==11
      )
    return 30;
  return 0;
}

Noch deutlich kürzer wird die Funktion tage() so:

/* lotim3.c 30. 7.95 kw
 */

int tage( int monat )
{
  int tagfeld[12] =
  {
    31, 28, 31, 30, 31, 30,
    31, 31, 30, 31, 30, 31
  };

  if( monat>=1 && monat<=12 )
    return tagfeld[monat-1];
  else
    return 0;
}

oder so:

/* lotim4.c 30 .7.95 kw
 */

int tage( int monat )
{
  int tagfeld[12] =
  {
    31, 28, 31, 30, 31, 30,
    31, 31, 30, 31, 30, 31
  };

  return ( monat>=1 && monat<=12 ? tagfeld[monat-1] : 0 );
}

18.2.6 strcpy() selbst schreiben

/* lostrcp1.c 30. 7.95 kw
 */

char *strcpy( char *wohin, const char *woher )
{
  char   *wohinkopie;     /* Kopie von wohin zum
                           * Zurueckgeben
                           */
  char    zeichen;        /* Zu kopierendes Zeichen
                           */
  /* wohin merken fuer Rueckgabewert:
   */
  wohinkopie = wohin;
  /* Kopieren, bis 0 auftritt:
   */
  do
  {
    zeichen = *woher;   /* Zeichen holen ...            */
    *wohin  = zeichen;  /* ... und schreiben.           */
    wohin = wohin + 1;  /* Beide Zeiger um eins ...     */
    woher = woher + 1;  /* ... erhoehen.                */
  }
  while (zeichen!=0);   /* Das ganze, bis die 0 kopiert
                         * wurde.
                         */
  /* Fertig.
   */
  return wohinkopie;
}

/* Testprogramm fuer strcpy()                             */
main()
{
  char    platz[50];  /* genug Platz zum Ausprobieren     */
  char   *zeiger;

  zeiger = strcpy( platz, "Teststring" );
  puts( zeiger );
}

Eine kompaktere, aber nicht so leicht verständliche Lösung wäre:

/* lostrcp2.c 30. 7.95 kw
 */

char *strcpy( char *wohin, const char *woher )
{
  char   *wohinkopie;     /* Kopie von wohin zum
                           * Zurueckgeben
                           */

  /* wohin merken fuer Rueckgabewert:
   */
  wohinkopie = wohin;

  /* Kopieren, bis 0 auftritt:
   */
  while( (*wohin++=*woher++) )
    ;       /* In der while-Bedingung wird schon alles
             * erledigt, also leere Anweisung.
             */
  /* Fertig.
   */
  return wohinkopie;
}

/* Testprogramm fuer strcpy()
 */
main()
{
  char    platz[50];  /* genug Platz zum Ausprobieren
                       */
  strcpy( platz, "Teststring" );
  puts( platz );
}

18.2.7 strcat() selbst schreiben

Die ausführliche Lösung wieder zuerst (der Quelltext von strcpy() ist aus Platzgründen weggelassen worden):

/* lostrca1.c 30. 7.95 kw
 */

char *strcpy( char *wohin, const char *woher )
     /* ... siehe Loesung der letzten Aufgabe ...
      */

     char *strcat( char *wohin, const char *woher )
{
  char   *wohinkopie;     /* Kopie von wohin zum
                           * Zurueckgeben
                           */
  char    zeichen;        /* Zu kopierendes Zeichen
                           */

  /* wohin merken fuer Rueckgabewert:
   */
  wohinkopie = wohin;

  /* wohin bis zur abschliessenden 0 erhoehen:
   */
  while( *wohin!=0 ) wohin = wohin + 1;

  /* Kopieren, bis 0 auftritt:
   */
  do
  {
    zeichen = *woher;   /* Zeichen holen ...            */
    *wohin  = zeichen;  /* ... und schreiben.           */
    wohin = wohin + 1;  /* Beide Zeiger um eins ...     */
    woher = woher + 1;  /* ... erhoehen.                */
  }
  while (zeichen!=0);   /* Das ganze, bis die 0 kopiert
                         * wurde.
                         */

  /* Fertig.
   */
  return wohinkopie;
}

/* Testprogramm fuer strcpy() und strcat()
 */
main()
{
  char    platz[50];  /* genug Platz zum Ausprobieren     */
  char   *zeiger;

  strcpy( platz, "Teststring" );
  strcat( platz, " mit Anhang" );
  puts( platz );
}

Und hier eine kompaktere Lösung:

/* lostrca2.c 30. 7.95 kw
 */

char *strcpy( char *wohin, const char *woher )
     ...;

     char *strcat( char *wohin, const char *woher )
{
  char   *wohinkopie;     /* Kopie von wohin zum
                           * Zurueckgeben
                           */

  /* wohin merken fuer Rueckgabewert:
   */
  wohinkopie = wohin;

  /* wohin auf die abschliessende 0 erhoehen:
   */
  while( *wohin ) wohin++;

  /* Kopieren, bis 0 auftritt:
   */
  while( (*wohin++=*woher++) )
    ;       /* In der while-Bedingung wird schon alles
             * erledigt, also leere Anweisung
             */
  /* Fertig.
   */
  return wohinkopie;
}

/* Testprogramm fuer strcpy() und strcat()
 */
main()
{
  char    platz[50];  /* genug Platz zum Ausprobieren     */
  char   *zeiger;

  strcpy( platz, "Teststring" );
  strcat( platz, " mit Anhang" );
  puts( platz );
}

18.2.8 Pascalstrings

/* lopasstr.c 30. 7.95 kw
 */

typedef char        pstring[81];


pstring *cstr_pstr( pstring ps, const char *cs );
char *pstr_cstr( char *cs, const pstring ps );
pstring *pstrcpy( pstring wohin, pstring woher );
pstring *pstrcat( pstring woran, pstring woher );
int psprintf( pstring ps, const char *format, ... );


pstring *cstr_pstr( pstring ps, const char *cs )
{
  char   *p = &ps[1];     /* Zeiger in Pascalstring    */
  ps[0] = 0;              /* Laenge zu 0 setzen        */
  while( *cs )            /* bis C-Stringende ...      */
  {
    *p++ = *cs++;         /* ... Zeichen kopieren      */
    ps[0]++;              /* und Laenge korrigieren.   */
  }
  return ps;              /* Pascalstring zurueckgeben */
}


char *pstr_cstr( char *cs, const pstring ps )
{
  char
    *p = &ps[1],          /* Zeiger in Pascal- und     */
    *c = cs;              /* C-string                  */
  int
    i = ps[0];            /* Schleifenzaehler          */

  while( i-- )
    *c++ = *p++;              /* i Zeichen kopieren    */
  *c = 0;                     /* Stringende            */
  return cs;                  /* C-String zurueckgeben */
}


pstring *pstrcpy( pstring wohin, pstring woher )
{
  int     i = woher[0] + 1;   /* Schleifenzaehler      */
  char   *p = wohin;

  while( i-- ) *p++ = *woher++;  /* i Zeichen kopieren */
  return wohin;
}


pstring *pstrcat( pstring woran, pstring woher )
{
  int     i = woher[0];          /* Schleifenzaehler   */
  char   *p = woran+woran[0];    /* Ende von woran     */

  *woran += i;                   /* Laenge korrigieren */
  while( i-- ) *++p = *++woher;  /* i Zeichen kopieren */
  return woran;
}


/* Fuer psprintf() wird die Funktion vsprintf() aus der
 * Standardbibliothek verwendet. Diese macht die ganze
 * Arbeit, ausser dass man von ihr den Pascalstring ab ps[1]
 * eintragen lassen muss.
 * vsprintf() liefert die Anzahl der Zeichen als
 * Rueckgabewert; diese Zahl kann man in ps[0] eintragen
 * und ist damit schon fertig.
 */

#include <stdarg.h>

int psprintf( pstring ps, const char *format, ... )
{
  va_list     args_p;
  va_start( args_p, format );

  ps[0] = vsprintf( &ps[1], format, args_p );
  va_end( args_p );
  return ps[0];
}


/* Testprogramm fuer die Pascalstringfunktionen.
 */
int main()
{
  pstring     ap, bp;
  char        ac[80];

  cstr_pstr( ap, "Das ist ein Teststring" );

  pstr_cstr( ac, ap );
  printf( "ac ist [%s]\n", ac );

  cstr_pstr( bp, " und mehr" );
  pstrcat( ap, bp );

  pstr_cstr( ac, ap );
  printf( "ac ist [%s]\n", ac );

  psprintf( ap,
            "%d, %d, %d; Mandela ist %s",
            1,
            2,
            3,
            "frei"
            );

  pstr_cstr( ac, ap );
  printf( "ac ist [%s]\n", ac );
}

18.2.9 Stringausgabe

/* lostr.aus 30. 7.95 kw
 *
 * Musterloesung zur Stringausgabe.
 */

/* Hier ist putchar() vereinbart:
 */
#include <stdio.h>


/* Das ist die zu schreibende Funktion.
 * Der String ab der Stelle s wird zeichenweise auf dem
 * Bildschirm ausgegeben, anschliessend ein Zeilenvorschub.
 */
void schreibwas( char *s )
{
  while( *s ) putchar( *s++ );  /* String ausgeben.   */
  putchar( '\n' );              /* Zeilenvorschub.    */
}


/* Testprogramm.                                      */
main()
{
  schreibwas( "hallo!" );
  schreibwas( "nochmal hallo!" );
}

18.2.10 Struktur für 5 Personen

/* lostru5p.c 30. 7.95 kw
 * Musterloesung zum Lesen und Ausgeben von Personen.
 */

/* Vereinbarungen zur Ein- und Ausgabe:
 */
#include <stdio.h>

/* Soviele Personen sollen es sein:
 */
#define         N_PERSONEN    5


/* Datenstruktur fuer eine Person:                          */
typedef struct
{
  char      vname[40];
  char      nname[40];
  int       alter;
}
person_t;


/* Ab hier kann man mit dem Datentypen person_t Variablen
 * vereinbaren.
 */

/* Funktion zum Lesen einer Person von der Tastatur:
 * Da lese_person() eine Struktur vom Typ person_t aendern
 * soll, muss man einen Zeiger auf die Struktur uebergeben.
 * Deshalb hat der Parameter den Typ person_t
 * Dementsprechend kann man nicht mit p.vname etc. auf die
 * Elemente der Struktur zugreifen, sondern nur mit
 * (*p).vname etc.; dafuer gibt es die abkuerzende Schreib-
 * weise p->vname.
 * An scanf() muessen die Adressen uebergeben werden, an
 * welche die eingelesenen Wert geschrieben werden sollen.
 * Deshalb das &-Zeichen vor p->alter.
 * Bei den Elementen vname und nname ist der Adressoperator
 * & nicht noetig, da diese Elemente Felder sind
 * (char ...[40]) und Felder ohnehin als Adressen ueber-
 * geben werden.
 */

void lese_person( person_t *p )
{
  printf( "Bitte Vorname, Nachname und Alter: " );
  scanf( "%s%s%d", p->vname, p->nname, &(p->alter) );
}


/* Funktion zum Schreiben einer Person:
 */
void schreibe_person( person_t p )
{
  printf( "%s %s ist %d Jahre alt.\n",
          p.vname,
          p.nname,
          p.alter
          );
}


main()
{
  int         i;
  person_t    personen[N_PERSONEN];

  /* Die Personen einlesen:
   */
  printf( "Bitte %d Personen eingeben!\n",
          N_PERSONEN
          );
  for( i=0; i<N_PERSONEN; i++ )
    lese_person( &personen[i] );

  /* Alle Personen wieder ausgeben:
   */
  printf( "Sie hatten eingegeben:\n" );
  for( i=0; i<N_PERSONEN; i++ )
    schreibe_person( personen[i] );
}

18.2.11 Struktur für 10 Personen

Wenn man die vorhergehende Aufgabe sauber gelöst hat, also für die Anzahl der Personen überall im Programm nur eine einzige Praeprozessorkonstante verwendet hat, dann braucht man jetzt auch nur diese einzige #define-Anweisung ändern:

#define         N_PERSONEN    5
ist zu ändern in:
#define         N_PERSONEN    10
und fertig.

18.2.12 lsearch()

/* lolsear.c 30. 7.95 kw
 * Musterloesung fuer lsearch()
 */

void *lsearch( const void *schluessel,
               const void *feldanfang,
               size_t elemente,
               size_t elementgroesse,
               int(* vergleichsfkt)( const void *e1,
                                     const void *e2
                                     )
               )
{
  char       *feld_p = (char *)feldanfang;

  /* Parameter pruefen:
   */
  if( !( schluessel
         && feldanfang
         && elemente
         && elementgroesse
         && vergleichsfkt   )      ) return NULL;

  /* Schleife ueber alle Elemente:
   */
  while( elemente-- )
  {
    /* trifft das aktuelle Element zu?
     */
    if( !(*vergleichsfkt)( schluessel, feld_p ) )
      return feld_p;
    /* Wenn nicht, dann naechstes Element:
     */
    feld_p += elementgroesse;
  }

  /* Ende des Feldes und trotzdem nichts gefunden?
   */
  return NULL;
}

18.2.13 Zahlen aus Datei lesen

/* lozahdat.c 30. 7.95 kw
 * Musterloesung zum Lesen von Zahlen aus werte.dat
 */

#include <stdio.h>

#define DATEINAME       "werte.dat"


int main()
{
  FILE       *f;            /* Datei, aus der gelesen
                             * werden soll
                             */
  long int
    summe=0,                /* Zum Aufsummieren           */
    wert;                   /* gelesener Wert             */
  int         anzahl=0;     /* Anzahl gelesener Werte     */

  if( (f=fopen( DATEINAME, "r" )) )      /* Datei oeffnen */
  {
    while( 1==fscanf( f, "%ld", &wert ) ) /* Lesen  */
    {
      summe += wert;        /* Summieren */
      anzahl++;             /* Anzahl merken */
    }

    fclose( f );            /* Datei schliessen */
    /* Ergebnis ausgeben:                       */
    printf( "%d Werte gelesen; Mittel ist %f\n",
            anzahl, (double)summe/anzahl
            );
  }
  else
    puts( "Ich kann " DATEINAME "nicht oeffnen!" );

  return 0;
}

18.2.14 Operatoren auf die Schnelle

-1<0<1 ist ein ganzzahliger Ausdruck mit dem Wert 0.

18.2.14.1 Begründung:

-1<0<1 besteht aus zwei Vergleichen (mit < für kleiner). Da Ausdrücke mit dem Operator < von links her zusammengefaßt werden (siehe Tabelle auf Seite [*]), ist der gegebene Ausdruck identisch mit (-1<0)<1. Der innere Teilausdruck davon ((-1<0)) ist ein logischer Vergleich, der mit wahr bewertet wird, da -1 ja kleiner ist als 0. Ein erfüllter logischer Ausdruck liefert den Wert 1 (siehe Seite [*]); wodurch der Gesamtausdruck als 1<1 geschrieben werden kann.

Dies ist wiederum ein Vergleich, der nicht erfüllt ist (1 ist nicht kleiner als 1) und deshalb den Wert 0 für logisch falsch liefert. Dies ist dann das Ergebnis des gesamten Ausdrucks.

18.2.15 Mit Zeigern jonglieren, aber richtig! (I)

Das angegebene Programm funktioniert, und zwar gibt es folgenden Text aus:

C ist gar nicht dumm
Danach wartet das Programm darauf, daß der Benutzer auf die Eingabetaste drückt.

Zur Erklärung:

Die Definition

char       *c[]  = { "he dast ga", "lllt dumm", "C i", "dar nich"
                   };
legt im Speicher 4 Stringkonstanten (Felder vom Typ char) an und vereinbart ein Feld c mit 4 Elementen vom Typ char*. Die Stringkonstanten liegen irgendwo im Speicher und sind alle mit je einem Nullbyte abgeschlossen. Die 4 Zeiger auf char (c[0] bis c[3]) zeigen jeweils auf das erste Zeichen einer Stringkonstante.

Die Stringkonstanten sollen in unserem Beispiel an den Adressen 2000, 2100, 2150 und 2180 im Arbeitsspeicher liegen.

Das Feldc möge bei 2500 beginnen.

Die Anzahl der Feldelemente in c ergibt sich aus der Anzahl der Initialisierungen.

Die Situation im Speicher (zur Laufzeit) sieht also etwa so aus:


\begin{picture}(77.00,128.00)
\setlength{\unitlength}{0.85mm}\linethickness{0.4p...
...[cc]{\mbox{$<$}}}
\put(20.00,12.00){\makebox(0,0)[cc]{\mbox{$<$}}}
\end{picture}

c[0] hat in unserem Beispiel den Wert 2000 und liegt selbst an der Stelle 2500 im Speicher, c[1] den Wert 2100 und liegt bei 2504, c[2] ist 2150 und c[3] enthält 2180.

Dabei wird angenommen, daß eine char-Variable ein Byte belegt und jeder Zeiger 4 Byte. Neben den Speicherzellen stehen in der Zeichnung gegebenenfalls die zugehörigen Variablennamen und gedachte Adressen der Elemente im Speicher.

char      **cp[] = {   c+3, c+2, c+1, c
                   };

vereinbart ein Feld cp mit 4 Zeigern, die auf die eben erst angelegten Zeiger c zeigen; und zwar zeigt cp[0] auf c[3] (3 Elemente hinter die Anfangsadresse des Feldes c), cp[1] zeigt auf c[2] und so fort.

cp[0] hat in unserem Beispiel also den Wert 2512, cp[1] den Wert 2508, cp[2] ist 2504 und cp[3] enthält 2500.

Das Feld cp soll an der Speicherstelle 3000 beginnen.

Dadurch ergibt sich folgende Situation:


\begin{picture}(127.00,128.00)
\setlength{\unitlength}{0.85mm}\linethickness{0.4...
...86.00,40.00){\line(1,0){5.00}}
\put(86.00,20.00){\line(1,0){9.00}}
\end{picture}

Die folgende Definition:

char     ***cpp  =   cp;

vereinbart einen Zeiger cpp, der auf ein Element vom Typ char** zeigt, nämlich auf cp[0].

Da cp[0] in unserem Beispiel an der Stelle 3000 liegt, wird cpp mit der Adresse 3000 initialisiert.

Im Speicher könnte es also nun so aussehen:


\begin{picture}(157.00,150.00)
\setlength{\unitlength}{0.85mm}\linethickness{0.4...
...86.00,40.00){\line(1,0){5.00}}
\put(86.00,20.00){\line(1,0){9.00}}
\end{picture}

Der Bildschirm soll (außer einem kleinen Cursor: _) noch leer sein.

Die Anweisung:

                printf( "%s", **++cpp );
erhöhtcpp um 1, also zeigtcpp nun auf cp[1]34.

Mit ,,Erhöhung um 1`` ist nicht die Addition (3000+1) gemeint, sondern vielmehr die Operation (3000+1*sizeof(char**)), siehe dazu Zeigerarithmetik. cpp erhält also den neuen Wert 3004, weil wir für unser Beispiel 4 Byte pro Zeigervariable spendierten.

Mit dem rechten * wird der Inhalt von cp[1] beschafft (2508); das ist ein Zeiger auf c[2]35. Mit dem linken * wird dann wiederum dessen Inhalt beschafft, also der Wert von c[2] (2150). Diese Adresse zeigt auf den Anfang der Stringkonstante "C i", also auf das 'C'. Ab diesem Zeichen wird mit printf() ein String bis zur abschließenden Null ausgegeben, also die drei Zeichen 'C', ' ' und 'i'.

Zu beachten ist aber, daß cpp nun nicht mehr auf cp[0] wie nach der Initialisierung (3000), sondern ab jetzt auf cp[1] (3004) zeigt.

Unser Beispielspeicher und der Bildschirm haben sich also etwas geändert:


\begin{picture}(157.00,150.00)
\setlength{\unitlength}{0.85mm}\linethickness{0.4...
...86.00,40.00){\line(1,0){5.00}}
\put(86.00,20.00){\line(1,0){9.00}}
\end{picture}

Die Anweisung:

        printf( "%s", *--*++cpp+5 );

erhöht cpp nochmals (auf 3008), so daß cpp nun auf cp[2] zeigt. Mit dem rechten * wird nun der Inhalt von cp[2] beschafft (2504). Mit - wird dieser Wert um 1 erniedrigt (2504-sizeof(char*) ergibt 2500), so daß cp[2] nicht mehr wie bisher auf c[1], sondern auf c[0] zeigt. Mit dem linken * wird der Inhalt dieses Zeigers (2000) beschafft; also ein Zeiger auf die erste Stringkonstante.

Der Ausdruck *-*++cpp zeigt also auf das erste Zeichen von "he dast ga".

Dementsprechend zeigt *-*++cpp+5 auf das s in der Stringkonstante. Ab diesem Zeichen wird mit printf() eine Zeichenkette bis zur abschließenden Null ausgegeben, also die Zeichenkette st ga. Damit steht schon der Text C ist ga auf dem Bildschirm:


\begin{picture}(157.00,150.00)
\setlength{\unitlength}{0.85mm}\linethickness{0.4...
...86.00,24.00){\line(1,0){6.00}}
\put(86.00,20.00){\line(1,0){9.00}}
\end{picture}

An dem Inhalt der Zeiger ändert sich mit den folgenden Anweisungen nichts mehr: deshalb wird nur noch die Änderung des Bildschirmspeichers gezeigt.

                printf( "%s", cpp[-2][0]+2 );
greift mit cpp[-2] zwei Elemente vor das, worauf cpp zeigt, also auf cp[0], da cpp auf cp[2] zeigt. Mit [0] wird auf das zugegriffen, worauf der zuletzt erhaltene Wert zeigt (c+3, bzw. 2180), also ein Zeiger auf die letzte Stringkonstante. cpp[-2][0] zeigt also auf das erste Zeichen in "dar nich". cpp[-2][0]+2 zeigt damit zwei Zeichen dahinter (2182), also auf das 'r'. Mit dem printf()-Aufruf werden folglich die Zeichen "r nich" ausgegeben. Jetzt steht insgesamt der Text "C ist gar nich" auf dem Bildschirm.


\begin{picture}(120.00,45.00)
\setlength{\unitlength}{0.85mm}\linethickness{0.4p...
...00){\oval(1.88,1.88)[lb]}
\put(105.03,11.00){\oval(2.04,1.88)[rb]}
\end{picture}

Es geht weiter:

                printf( "%s\n", *(cpp[1]+1)+3 );
greift mit cpp[1] auf ein Element zu, das eine Position hinter dem liegt, auf das cpp zeigt, also auf cp[3] an der Stelle 3012. cp[3] enthält den Wert c (2500), also die Adresse von c[0]. cpp[1]+1 ist damit die Adresse von c[1] (2504). *(cpp[1]+1) ist die Adresse des Strings "lllt dumm" (2100) und *(cpp[1]+1)+3 ist die um drei größere Adresse (2103), also die Adresse des 't'-Zeichens in dem String. Ab diesem Zeichen wird mit printf() wieder bis zur Null ausgegeben, also die Zeichen "t dumm". Insgesamt steht nun der Text "C ist gar nicht dumm" auf dem Schirm:


\begin{picture}(120.00,45.00)
\setlength{\unitlength}{0.85mm}\linethickness{0.4p...
...oval(2.04,1.88)[rb]}
\put(57.06,29.01){\makebox(0,0)[lt]{{\tt\_}}}
\end{picture}

                getchar();

wartet dann nur noch auf eine Eingabe des Benutzers, damit dieser die Programmausgabe in Ruhe lesen kann.

18.2.16 Mit Zeigern jonglieren, aber richtig! (II)

Hier ist meine Musterlösung zu qsort():
/* loqsort.c 30. 7.95 kw
 */

#include <stdlib.h>
#include <string.h>

char  *temp;


/* swap() vertauscht base[i] mit base[j], unabhaengig von
 * dem Datentyp der in base[] enthaltenen Elemente.
 */

static void swap( void *base, int i, int  j, size_t len )
{
  char
    *pi = (char*)base + (i*len),
    *pj = (char*)base + (j*len);

  memcpy( temp, pi, len );
  memcpy( pi, pj, len );
  memcpy( pj, temp, len );
}


static void myqsort( void *v,
                     int left,
                     int right,
                     size_t len,
                     int (*compar)(const void*, const void*)
                     )
{
  int i,last;

  if(left >= right)     /* Rekursion abbrechen, wenn nicht    */
    return;             /* mindestens zwei Elemente.          */

  /* Element waehlen und am Vektoranfang sichern:
   */

  swap( v, left,(left + right)/2, len );

  last = left;          /* in last zum Mitzaehlen merken      */

  for(i = left+1; i <= right; i++)    /* aufteilen            */
    if( compar( (char*)v+(i*len), (char*)v+(left*len)) < 0 )
      swap(v, ++last, i, len );

  /* Gewaehltes Element wieder zwischen den Teil mit den
   * kleineren und den Teil mit den groesseren Elementen
   * setzen:
   */
  swap(v, left, last, len );

  /* linkes und rechtes Teilfeld sortieren:
   */
  myqsort(v,left, last-1, len, compar);
  myqsort(v,last+1,right, len, compar);
}


void qsort( void *base,
            size_t nmemb,
            size_t size,
            int (*compar)(const void*, const void*)
            )
{
  temp = malloc( size );  /* Platz fuer 1 Element             */
  if( temp )
  {
    myqsort((int*)base, 0, 0+nmemb-1, size, compar);
    free( temp );
  }
}

18.2.17 Kleiner Praeprozessor

Die Musterlösung wäre etwas zu umfangreich; bei Bedarf bitte auf extra anfordern.

AnyWare@Wachtler.de