Kurs C-Programmierung

http://www.tu-chemnitz.de/urz/kurse/unterlagen/C/index.htm - Leider sind viele Links nicht mehr gültig und von mir gelöscht

 

0 Einführung

1 Grundlagen

1.1 Programmstruktur

1.2 Grunddatentypen

1.2.1 Variable

1.2.2 Konstanten

1.3 Ausdrücke und Operatoren

1.3.1 Wertzuweisung

1.3.2 Arithmetische Operatoren

1.3.3 Vergleichsoperatoren

1.4 Ablaufsteuerung

1.4.1 Einfache Anweisung

1.4.2 Anweisungsblock

1.4.3 if-else-Anweisung

1.4.4 while-Schleifen

1.4.5 do-while-Schleifen

1.5 Ausgabe und Eingabe

2 Sprachkonzepte von C

2.1 Spezielle Operatoren

2.1.1 Inkrement-und Dekrementoperatoren

2.1.2 Bitorientierte Operatoren

2.1.3 Logische Operatoren

2.1.4 Zusammengesetzte Zuweisungsoperatoren

2.1.5 Entscheidungsoperator

2.1.6 Kommaoperator

2.1.7 Vorrangregeln und Assoziativität

2.1.8 Konstantenausdrücke

2.2 Strukturierte Datentypen

2.2.1 Felder

2.2.2 Strukturen

2.2.3 Bitfelder

2.2.4 Unions

2.2.5 Aufzählungstyp

2.2.6 Typdefinitionen

2.2.7 sizeof - Operator

2.3 Zeigertyp

2.3.1 Vereinbarung von Zeigern

2.3.2 Zeigeroperatoren

2.3.3 Beziehung zwischen Zeigern und Feldern

2.3.4 Zeigerarithmetik

2.3.5 Zeiger auf Strukturen

2.4 Steuerstrukturen

2.4.1 switch - Anweisung

2.4.2 for-Anweisung

2.4.3 Anweisungen zur unbedingten Steuerungsübergabe

2.5 Funktionen

2.5.1 Vereinbarung von Funktionen

2.5.2 Funktionsaufruf und Argumentübergabe

2.5.3 Übergabe des Funktionswertes

2.5.4 Rekursiver Aufruf von Funktionen

2.5.5 Zeiger auf Funktionen

2.6 Speicherklassen, Blockstruktur und Lebensdauer

2.6.1 Speicherklasse auto

2.6.2 Speicherklasse extern

2.6.3 Speicherklasse static

2.6.4 Speicherklasse register

2.6.5 Speicherklasse für Funktionen

2.6.6 Blockstruktur und Gültigkeit

2.7 Initialisierung

2.7.1 Initialisierung von extern bzw. static Variablen

2.7.2 Initialisierung von auto bzw. register Variablen

2.8 Typkonvertierung

2.8.1 Implizite Typkonvertierung

2.8.2 Explizite Typkonvertierung

2.8.3 Tabelle Typkonvertierung

2.9 Beispiele

2.9.1 Textformatierung

2.9.2 Sortieren

3 Programmierumgebung

3.1 Argumentübergabe an die main-Funktion

3.2 Präprozessor

3.2.1 Definition symbolischer Konstanten und Makros

3.2.2 Einfügen von Files

3.2.3 Bedingte Compilierung

3.3 Standardbibliothek

3.3.1 Beziehung Headerfile - Bibliotheksfunktion

3.3.2 Shell-Umgebungsvariable einlesen

3.3.3 Filearbeit

3.3.4 Zahlenumwandlung

3.3.5 Dynamische Speicherverwaltung

3.3.6 Zeichenkettenverarbeitung

3.3.7 Signalbehandlung

3.3.8 Kommandoschnittstelle

3.3.9 Zugriff auf serielle Schnittstelle

3.3.10 Filezugriff auf Remote Host

3.4 Komplexbeispiele

3.4.1 CGI-Programmierung

3.4.2 Laufzeit-Messung

 

2.1.7 Vorrangregeln und Assoziativität

Die Auswertungsreihenfolge von Ausdrücken wird durch den Vorrang der Operatoren bestimmt. Die Assoziativität gibt an, ob eine Folge von Operatoren gleichen Vorrangs von links oder von rechts abgearbeitet wird.

Die folgende Tabelle enthält eine Liste von Operatoren, welche nach Vorrangregeln geordnet sind. (1. Gruppe hat den höchsten Vorrang.)

Operatoren

Bezeichnung

Assoziativität

( )
[ ]
->
.

Funktions- / Ausdrucksklammer
Feldindizierung
Zeiger auf Strukturelement
Strukturelementvariable

L => R

!
~
++
--
-
( typ)
*
&
sizeof

Negation
Einerkomplement
Inkrement
Dekrement
Vorzeichen Minus
explizite Typkonvertierung
Zugriff über Zeiger
Adresse des Datenobjektes
Größe des Datenobjektes in Bytes

R => L

*
/
%

Multiplikation
Division
Modulo

L => R

+
-

Addition
Subtraktion

L => R

<<
>>

Linksverschiebung von Bits
Rechtsverschiebung von Bits

L => R

<
<=
>
>=

Test kleiner
Test kleiner gleich
Test größer
Test größer gleich

L => R

==
!=

Test gleich
Test ungleich

L => R

&

bitweises UND

L => R

^

bitweises exklusives ODER

L => R

|

bitweises inklusives ODER

L => R

&&

logisches UND

L => R

||

logisches ODER

L => R

?:

bedingte Zuweisung

R => L

=
op=

Wertzuweisungen
op: + - * / % & | ^ << >>

R => L

,

Kommaoperator

L => R

Soll ein Ausdruck niedrigeren Ranges zuerst ausgeführt werden, so muss dieser geklammert werden. Der Ausdruck

x = y = z = 0

wird behandelt, als wäre er geklammert

x = (y = (z = 0))

ACHTUNG: Unabhänging vom Rang der Operatoren darf der Compiler selbst entscheiden in welcher Reihenfolge Teilausdrücke ausgewertet werden. Das kann insbesondere bei Ausdrücken mit Nebeneffekten zu Problemen führen, z.B.:

fu ( x++ , f[x] )
a == b && c == d++
x = f(y) + g(y++)
a[i] = i++

Häufig wird ein Compiler zuerst komplizierte Teilausdrücke berechnen.

#include <stdio.h>
 
main()  /* Vorrang  und bitorientierte Operatoren */
{
    int x=3, y=2, z=1;
 
    printf("x = %d, y = %d, z = %d\n", x, y, z);
    printf("x | y & z  => %d\n", x | y & z );
    printf("x | y & ~z => %d\n", x | y & ~z);
    printf("x ^ y & ~z => %d\n", x ^ y & ~z);
    printf("x & y && z => %d\n", x & y && z);
    printf("! z | z    => %d\n", ! z | z   );
    printf("~ z | z    => %d\n", ~ z | z   );
    printf("z ^ z      => %d\n", z ^ z     );
    printf("z<<3       => %d\n", z=z<<3    );
    printf("(-1)<<3    => %d\n", (-1)<<3   );
}

 

#include <stdio.h>
 
main()                 /* Vorrang */
{   int x,y,z;
    x=y=z=  1; ++x || ++y && ++z; printf("x=%d, y=%d, z=%d\n", x,y,z);
    x=y=z=  1; ++x && ++y || ++z; printf("x=%d, y=%d, z=%d\n", x,y,z);
    x=y=z=  1; ++x && ++y && ++z; printf("x=%d, y=%d, z=%d\n", x,y,z);
    x=y=z= -1; ++x || ++y && ++z; printf("x=%d, y=%d, z=%d\n", x,y,z);
    x=y=z= -1; ++x && ++y || ++z; printf("x=%d, y=%d, z=%d\n", x,y,z);
    x=y=z= -1; ++x && ++y && ++z; printf("x=%d, y=%d, z=%d\n", x,y,z);
}

 

 

2.2.1 Felder

allg. Form einer Feldvereinbarung:

speicherklasse typ bezeichner [konst_ausdruck]

konst_ausdruck muss einen Integerwert ergeben.

 

#define N 1000
...
float farray[10];             /* 10 Elemente vom Typ float */
char  string[100];            /* Zeichenfeld für 100 Zeichen */
long  z[N];                   /* 1000 long-Elemente */

 

#include <stdio.h>
#define N 10
 
main()
{
   int f[N], i;    /* f ist ein Feld mit 10 int-Elementen */
 
   i = 0;                          /* i dient als Index   */
   while(i < N)        /* f[0] bis f[9] sind Feldelemente */
      f[i++] = 0;      /* alle Feldelemente auf 0 setzen  */
   printf("i= %d\n", i);
}

 

#include <stdio.h>
#define YES 1
#define NO  0
#define VON   100
#define BIS   250
#define ANZ 5
 
main()    /* Primzahlberechnung: Sieb des Erathostenes */
{
   short prim_anz[ BIS+1 ];
   int i, j, k, n;
 
   printf("Berechnung der Primzahlen von %d bis %d: \n", VON, BIS);
 
   n=BIS; i=2;
   /* Init feld prim_anz */
   while ( i <= n )
      prim_anz[i++] = YES;
   i=2; k=1;
   while ( i <= n ) {
      if (prim_anz[i]) {
        j = i;
        if ( i >= VON )
           printf("  %6d%c", i, k++ % ANZ ? '\t' : '\n');
        while (i*i < n && ( j += i ) <= n )
           prim_anz[j] = NO;
      }
      i++;
   }
   printf("\n");
}

strlen(s)

/* Länge des Zeichenfeldes s */

strcpy(s1,s2)

/* Kopieren von s2 nach s1 */

strcmp(s1,s2)

/* Vergleich von s1 und s2 */

strncmp(s1,s2,n)

/* Vergleich von max. n Zeichen */

strcat(s1,s2)

/* Kopiere s2 an Ende von s1 */

strchr(s,c)

/* Suche Zeichen c in String s */

Beispiel:

void strcpy(s1, s2)    /* kopiert Zeichenfeld s2 nach s1 */
char s1[], s2[];
{
     int i;
 
     i=0;
     while(s1[i] = s2[i]) i++;
}

C unterstützt mehrdimensionale Felder:

float

a [ N ] [ N ];

/* N x N Matrix */

short

b [ 2 ] [ 4 ] [ 3 ];

/* 2 x 4 x 3 Feld */

Es erfolgt eine zeilenweise Abspeicherung der Elemente (der letzte Index ändert sich am schnellsten):

b[0][0][0], b[0][0][1], b[0][0][2], b[0][1][0], ...

 

 

2.2.2 Strukturen

struct datum {                       /* Deklaration des Typs */
     short jahr; 
     char monat[4]; 
     short tag; 
};

Die Deklaration reserviert keinen Speicherplatz, sondern legt lediglich die Typen der zu diesem Strukturtyp gehörenden Komponenten fest. Die Länge der Struktur ergibt sich nicht aus der Summe der Elementlängen (Alignment).

struct datum heute, hochzeits_tag;             /* Definition */ 

Dadurch erfolgt die Definition der Variablenbezeichner heute und hochzeits_tag als Strukturinstanzen des Typs struct datum und damit auch die Speicherplatzvergabe.

Deklaration und Definition in einer Anweisung:

struct datum {                /* Deklaration des Typs */
     short jahr; 
     char monat[4]; 
     short tag; 
} heute, feiertage[20];       /* Definition von Variablen dieses Typs */

Die letzte Vereinbarung definiert ein Feld mit 20 Elementen. Jedes Element ist eine Struktur des Typs datum.

Mit dem Operator . kann man auf einzelne Komponenten der Struktur zugreifen.

allg.: einfacher_ausdruck . bezeichner

einfacher_ausdruck ist ein Bezeichner für eine Strukturinstanz.

heute.jahr = 1992; 
feiertage[i].tag=24;

C erlaubt die Verwendung gleicher Bezeichner für Strukturtyp, Strukturelemente und Variablenbezeichner:

struct wert {         /* Deklaration eines Typs */
     char kennung; 
     int wert;        /* Deklaration Strukturelement */
 
} wert;               /* Definition einer Variable */

 

#include <stdio.h>
 
struct datum {
   short jahr;
   char monat[4];
   short tag;
} heute; 
 
main()                           /* Ausgabe: 9. Nov. 1989 */
{
   heute.jahr = 1989; 
   strcpy(heute.monat, "Nov");              /* 3 Zeichen! */
   printf("%d. %s. %d\n", heute.tag = 9, heute.monat,
                   heute.jahr);
}

 

#include <stdio.h>
#define MAX 20
 
struct person {
   char name[MAX];
   int alter;
};
 
main()                                   /* Strukturen */
   int i=1;
   struct person person, pf[MAX];
 
   strcpy(person.name,"Maxi");
   person.alter=18;
   printf("%s ist %d Jahre\n",person.name, person.alter);
   while(i<MAX) pf[i++]=person;
   pf[6].alter+=6;
   pf[6].name[3]='\0';
   printf("%s ist %d Jahre\n",pf[6].name, pf[6].alter);
   printf("%s ist %d Jahre\n",&pf, pf[0].alter);
}                               /* pf[0] unbelegt ! */  

 

2.2.3 Bitfelder

Bitfelder oder Bitstrukturen werden über Strukturelemente des Datentyps struct auf Teile eines Maschinenwortes abgebildet. Sie erlauben den symbolischen Zugriff auf Bits und stellen somit eine alternative Möglichkeit der Bitmanipulation dar. Allgemein gilt für die Vereinbarung von struct-Elementen:

typ bezeichner : konst_ausdruck

typ sollte immer unsigned sein !

Hinter dem Typ der Strukturkomponente (Bildfeld) wird der Name angegeben, feofolgt vom : und der Anzahl der Bits.

#include <stdio.h>
 
struct byte {
   unsigned bit_1 : 1;
   unsigned bit_2 : 1;
   unsigned rest : 6;
};
 
main()                                             /* Bitfelder */
   struct byte b;
 
   b.bit_2 = b.bit_1 =1;
   b.rest = 63;
   printf("Bitfeld = %d\n", b);
   b.bit_1--;
   printf("Bit1=%d, Bit2=%d, Rest=%d\n",b.bit_1, b.bit_2, b.rest);
   if(b.bit_1 && b.bit_2)
      b.bit_1++;
   else
      b.bit_1--;
   b.rest&=(b.bit_1+b.bit_2);
   printf("Bit1=%d, Bit2=%d, Rest=%d\n",b.bit_1, b.bit_2, b.rest);
}

 

2.2.4 Unions

Eine Union ist eine spezielle Form des Typs struct. Jede Komponente einer Union-Instanz wird auf den gleichen Speicherbereich abgebildet, d.h. zu einer Zeit ist immer nur ein Wert in einer Union gespeichert. (Vorsicht: Typ!)

union operand {

int i;

float f;

long l;
} u;

Für den Zugriff gelten die gleichen Regeln wie bei Strukturen.

#include <stdio.h>
 
union wert {                                  /* Unions */
   int i;
   char c;
   double d;
};
 
main()
   union wert w,x;
 
   w.i=6;     printf("i=%d, c=%c, d=%f\n", w.i, w.c, w.d);
   w.c='a';   printf("i=%d, c=%c, d=%f\n", w.i, w.c, w.d);
   w.d=33.33; printf("i=%d, c=%c, d=%f\n", w.i, w.c, w.d);
}

 

2.2.5 Aufzählungstyp

Mit dem Aufzählungstyp wird ein Wertebereich explizit durch eine Bezeichnerliste festgelegt. Die Bezeichner sind einfach eine symbolische Konstantenrepräsentation.

#include <stdio.h>
 
enum farbe { rot, gelb, blau };
 
main()                 /* implizit: rot = 0, gelb = 1, ... */
   enum farbe farbe;
 
   farbe = rot;
   if (farbe == blau) farbe = gelb;
   printf("Farbe %s (%d)\n", farbe==rot?"rot":"gelb", farbe);
}

 

2.2.6 Typdefinitionen

Eine Typdefinition ist nur eine Neubenennung, kein neuer Typ, d.h. bezeichner wird als Synonym für typ benutzt.

allg.: typedef typ bezeichner

#define int INDEX,FLAG ;
#define char TEXT[100] ;

INDEX i, j;     /* Vereinbarungen */
TEXT   string;

#include <stdio.h>
#define TRUE 1
#define FALSE 0
 
main()                              /* typedef */
   typedef int BOOLEAN, INTEGER;
   BOOLEAN ok; INTEGER i; 
 
   i = 10;
   if (i % 2)
      ok = FALSE;
   else
      ok = TRUE;
   printf("ok: %d\n", ok);
}

 

2.2.7 sizeof - Operator

Der Operator sizeof liefert einen Wert vom Typ unsigned, der die Länge des Speicherbereiches (gemessen in Bytes) angibt, den der Operand benötigt.

allg.: sizeof ausdruck
liefert die Anzahl der Bytes, die zum Speichern von ausdruck benötigt werden
ausdruck wird nicht ausgewertet (keine Nebeneffekte)

oder: sizeof ( typ_spezifikation)
liefert die Anzahl der Bytes, die benötigt werden um ein Objekt des Typs typ_spezifikation zu speichern.

sizeof gilt als Konstantenausdruck, d.h. seine Berechnung erfolgt durch den Compiler und kann zur Initialisierung benutzt werden.

sizeof ist nicht auf Operanden vom Typ Funktion, void und Felder ohne Längenangabe anwendbar.

#include <stdio.h>
main()                            /* sizeof */
{    
     int a[10], n, i;
 
     i = 256; n = sizeof(a);
     printf("Laenge von a: %d\n",n);
     printf("Laenge von i: %d\n",sizeof(int));
}

Dieser Operator wird oft auf Strukturen angewendet. Deren Länge ergibt sich nicht zwangsläufig aus der Summe der Länge von allen Stukturelementen. Ursache sind "Füllbytes", die zur Ausrichtung der Strukturelemenete eingeschoben werden (auf Wortgrenzen der konkreten Maschine).

 

2.3 Zeigertyp

Motivation:

Zeiger enthalten die Adresse eines Objektes eines bestimmten Datentyps. Somit ist es möglich, auf diese Objekte "indirekt" mittels Zeiger zuzugreifen. Zeiger spielen zentrale Rolle in C.

 

2.3.1 Vereinbarung von Zeigern

Ein Zeiger ist eine Adressbezugnahme (Lvalue) auf ein Speicherobjekt.

allg.: speicherklasse typ * bezeichner

 int *pi;               /* pi zeigt auf Objekt vom Typ int */
 int x,*px;   /* x ist int-Variable, px ist Zeiger auf int */
 char *cp;                          /* Zeiger auf Typ char */
 float *fp[10];          /* Feld von Zeigern auf Typ float */
 struct datum *pd;         /* Zeiger auf Strukturtyp datum */
 char **ppc;             /* Zeiger auf Zeiger auf Typ char */

 

2.3.2 Zeigeroperatoren

Der Zeigeroperator & liefert die Adresse seines Operanden.

allg.: & lvalue

 int a;
 int *pi;
 pi = &a;            /* pi zeigt auf a */

Der Zeigeroperator * löst eine Adressbezugnahme auf, d.h. er ermöglicht, auf das Datenobjekt ''indirekt'' zuzugreifen, auf welches der Operand verweist.

allg.: * zeiger

 int x, y, *pint;
 
 x = 10;
 pint = &x;
 y = *pint - 1;                /* y = x - 1; */
 *pint = 0;                        /* x = 0; */
 *pint += 2;                      /* x += 2; */

 

#include <stdio.h>
 
void tausche(x, y)
   int *x, *y;
{
   int z;
 
   z = *x; *x = *y; *y = z;
}  /* "return" nicht notwendig */
 
main()
{
   int a, b;
   void tausche();
 
   a = 4; 
   b = 8;
   printf("a= %d, b= %d\n", a, b);
   tausche(&a, &b);
   printf("a= %d, b= %d\n", a, b);   
}

 

 

2.3.3 Beziehung zwischen Zeigern und Feldern

In C orientieren sich Zeiger an Feldern:

int i, *pi, f[10]; /* Vereinbarung */
 
pi = &f[0];        /* Adresse des ersten Feldelementes */
pi = f;            /* analog: Adresse des Feldes: */
      /* f ist Konstantenbezeichner fuer Adresse des Feldanfangs */
pi++;              /* ok */
f++;               /* falsch! kein Lvalue */
 
 
char g[100][200];  /* Definition eines Feldes von Zeichenfeldern */
char *pc;          /* Zeiger auf Zeichenfeld */
 
pc=&g[50][0];      /* Adresse des 1.Zeichens der 51.Zeichenkette */
pc=g[50];          /* entspricht &g[50][0] */

 

i = *(pi + 2);

ANALOG: i = f[2];

/* Arithmetik in Feldelementen */

pi + i

ANALOG: &f[i]

ANALOG: f + i

pi[2] = 5;

ANALOG: f[2] = 5;

/* pi als Feldbezeichner gilt auch */

Besondere Aufmerksamkeit erfordern Zeigeroperationen mit Nebeneffekt (Vorrang):

#define N 20
int f[N], *p1, *p2, *p3, *p4, i, x;
 
i=5;
p1 = p2 = p3 = p4 = &f[i];
 
x = *p1++;     /* analog: x = f[i++]; */
x = *++p2;     /* analog: x = f[++i]; */
x = (*p3)++;   /* analog: x = f[i]++; */
x = ++*p4;     /* analog: x = ++f[i]; */

Diese Zeigeroperationen finden z.B. in den Funktionen zur Zeichenkettenverarbeitung häufig Verwendung:

void strcpy(char *s1, char *s2)
{     while ( *s1++ = *s2++ ) ;     }

#include <stdio.h>
 
main()                             /* Beziehung Zeiger - Felder */
{
    int i, *ip, f[5];
 
    i = 5;
    while(i--) 
        f[i] = i;
    i = 3; ip = &f[i];
 
    printf("f      : %d\n", f);       /* Adresse des Feldanfangs */
    printf("ip     : %d\n", ip); /* Adresse des 3. Feldelementes */
    printf("f+i    : %d\n", f+i);                        /* dto. */
    printf("f[i]   : %d\n", f[i]);  /* Wert des 3. Feldelementes */
    printf("*(ip+i): %d\n", *(ip+i));                    /* dto. */
 
    /* * und ++ haben gleichen Vorrang => 
            rechter Operator wird zuerst auf Operand angewendet  */
 
    i = *ip++;                         /* i = f[3]; ip++ (&f[4]) */
    printf("i = *ip++; i: %d, ip: %d, *ip: %d\n", i, ip, *ip);
    i = (*ip)--;                         /* i = f[4]; f[4] -= 1; */ 
    printf("i = (*ip)--; i: %d, ip: %d, *ip: %d\n", i, ip, *ip);
}

 

 

2.3.4 Zeigerarithmetik

Zeigerarithmetik erfolgt immer in Einheiten des Typs, auf den ein Zeiger verweist. Folgende Operationen mit Zeigern sind möglich:

zeiger1 op zeiger2     für op:   -   -=   ==   !=   <   <=   >   >=

Sinnvolle Ergebnisse werden nur erzielt, wenn zeiger1 und zeiger2 auf das gleiche Feld verweisen. Vom Compiler oder zur Laufzeit erfolgen keine entsprechenden Kontrollen!

zeiger op integer     für op:   +   -   +=   -=   ++   --

Es wird nicht kontolliert ob "Feldgrenzen" über- oder unterschritten werden. Derartige Fehler führen zur Laufzeit nur dann zum Programmabbruch, wenn der Adressraum des Prozesses verlassen wird.

#include <stdio.h>
 
int catlen(a, b)           /* Verketten zweier Zeichenfelder und */
char *a, *b;               /* Berechnung der Zeichenanzahl */
{
     char *p;
    
     p=a;                                       /* Anfang Feld a */
     while(*p)                             /* Suchen Ende Feld a */
         p++;
     while(*p++ = *b++)              /* Kopieren b an Ende von a */
         ;           
     return(--p - a);                   /* Ende - Anfangsadresse */
}                               /* ausser Endekennzeichen ('\0') */
 
main()
{
    char f1[512], f2[512];
 
    printf("String f1 eingeben:");
    scanf("%s",f1);
    printf("String f2 eingeben:");
    scanf("%s",f2);
    printf("Laenge f1 & f2: %d\n", catlen(f1,f2));
    printf("f1 nach catlen %s\n", f1);
}    

 

 

2.3.5 Zeiger auf Strukturen

Definition eines Zeigers auf Struktur datum:

struct datum *pd;

Der Operator -> dient der Bezugnahme auf Komponenten einer Struktur, die über einen Zeiger adressiert wird.

pd -> jahr = 1989;

ist die vereinfachte Schreibweise für

( * pd ) . jahr = 1989;

Der Operator . hat höheren Vorrang als * .

#include <stdio.h>
 
struct person {                      /* Personenbeschreibung */
  char name[20];
  int alter;
  struct person *vater; /* Zeiger auf Struktur gleichen Typs */
} pf[2];                    /* Feld von 2 Personenstrukturen */
 
main()
{
   int i=0;
   struct person *p = pf; /* Initialisierung mit Adresse des Feldes pf */
                          /* siehe Abschnitt 2.7.                      */
 
   strcpy(p->name,"Maxi");           /* Name "Maxi" zuweisen */
   p->alter=18;
   p->vater = pf + 1;                              /* &pf[1] */ 
   p->vater->alter = 45;   /* Zugriff auf pf[1] ueber Zeiger */
   strcpy(p->vater->name,"Fritz");
   while(i++ <= 1) {                 /* elementweise Ausgabe */
       printf("%s ist %d Jahre\n",p->name, p->alter);
       p++;
   }
}

Über Zeiger können jetzt auch Felder von Strukturen rekursiv durchsucht werden. Z.B. könnte man mit folgender Vereinbarung eine Ahnentafel erstellen:

#define FAM_ANZ 99;
 
struct stamm {
   char name[20];           /* Zeichenfeld */
   char vorname[20];        /* Zeichenfeld */
   struct datum geb_datum;  /* Struktur */
   struct stamm *vater;     /* Zeiger auf Struktur gleichen Typs */
   struct stamm *mutter;    /* Zeiger auf Struktur gleichen Typs */
} ahnentafel[FAM_ANZ];

 

2.4 Steuerstrukturen

Motivation:

Zur Erzeugung gut lesbarer und effizienter C-Programme gibt es in C die for- und die switch-Anweisung zur Steuerung des Programmablaufs.

 

2.4.1 switch - Anweisung

Die Anweisung switch ist eine spezielle bedingte Anweisung. Allg.:

switch ( ausdruck) {
case konst_ausdruck1 :

anweisungs_liste1

... case konst_ausdrucki :

anweisungs_listei default:

anweisungs_listed case konst_ausdrucki+1 :

anweisungs_listei+1

... case konst_ausdruckn :

anweisungs_listen }

Der Wert von ausdruck wird mit den Werten von konst_ausdruckj(j = 1, 2, ... ) verglichen. Bei Übereinstimmung wird die dazugehörige anweisungs_listej abgearbeitet.

ACHTUNG! Nach Ausführung der anweisungs_listej eines case-Zweiges wird die switch-Anweisung nicht automatisch beendet, sondern anweisungs_listej+1 usw. ausgeführt. Dies kann aber mit Hilfe der break-Anweisung verhindert werden.

Der default-Zweig wird ausgeführt, wenn keine Übereinstimmung mit konst_ausdruckj (j = 1, ... n) festgestellt wurde.

Insbesondere können fehlen:

- der default-Zweig oder
- die case-Zweige vor dem default-Zweig oder
- die case-Zweige nach dem default-Zweig.

#include <stdio.h>
#define YES 1
#define NO  0
 
main()      /* Folgen von ' ' bzw. '\t' durch ' ' ersetzen */
{
  int c;
  int inleer; inleer = NO;
 
  while((c = getchar()) != EOF)
    switch(c)  {
      case '\t':
      case ' ' :
        if(inleer == NO)  {
          inleer = YES;
          putchar(' ');
        }
        break; /* unbedingter Abbruch der switch-Anweisung */
      default:
        inleer = NO;
        putchar(c);
        break;
    }
}

 

#include <stdio.h>
 
main()                    /* einfacher Taschenrechner */
{
    double op1, op2;
    char op[2];
 
    printf("Eingabe: zahl <op> zahl ENTER\n\n");
    while(scanf("%lf %1s %lf", &op1, op, &op2) != EOF) {
        switch(op[0]) {
            case '+':
                op1 += op2;
                break;
            case '-':
                op1 -= op2;
                break;
            case '*':
                op1 *= op2;
                break;
            case '/':
                op1 /= op2;
                break;
            default:
                printf("illegal operator\n");
                continue;
        }
        printf(" = %g\n", op1);
    }
}

Aufgabe:

#include <stdio.h>
 
main()           /* switch-Anweisung */
{ 
  int i = 6;
 
  while (i--)
    switch(i){
      case 0: printf("NULL ");
      case 1: printf("EINS "); break;
      case 3: printf("DREI "); break;
      case 5: printf("FUENF ");
      default: printf("%d ", i);
    }
  printf("\n");
}