Datentypen



C definiert fuer Variable grundsätzliche Datentypen. Die Bezeichnungen für die Ganzzahlentypen ist nicht ganz unproblematisch denn die Speichergröße (und damit der Wertebereich) kann auf unterschiedlichen Prozessorsystemen unterschiedlich ausfallen.

Der Datentyp ' int ' hat auf 8 und 16 Bit Prozessoren in aller Regel eine Speichergröße von 2 Byte (16 Bit), jedoch auf 32 Bit Systemen eine Speichergröße von 4 Byte (und hiermit auch einen anderen Wertebereich). Manche C-Compiler für 64 Bit CPU's verwenden für einen Integer sogar 8 Byte.

Werden die Datentypen aus stdint.h verwendet, wird dem Compiler explizit mitgeteilt, welchen Speicherbedarf und welchen Wertebereich eine Variable des entsprechenden Datentyps hat. Für alle C Compiler gilt hier verbindlich, daß bspw. ein uint16_t  2 Bytes Speicher belegt (und hierbei einen entsprechenden Wertebereich besitzt), egal ob der Prozessor ein 8-, 16-, 32- oder 64-Bit Prozessor ist.

Die nachfolgende Tabelle wird, wie eingangs beschrieben, beim Datentyp int  und unsigned int je nach Compiler abweichen. Bei einem 32-Bit System entspricht dieser Datentyp int32_t / uint32_t, bei einem 64-Bit System int64_t / uint64_t.


    Type Bytegröße Wertebereich stdint.h
    Typen für ganze Zahlen
    char
    1 -128 .. 127
    int8_t
    unsigned char
    1
    0 .. 255
    uint8_t
    int
    2
    -32768 .. 32767
    int16_t
    unsigned int
    2
    0 .. 65535
    uint16_t
    long
    4
    -231 .. 231-1
    int32_t
    unsigned long
    4
    0 .. 231-1
    uint32_t
    long long
    8
    -263 .. 263-1
    int64_t
    unsigned long long
    8
    0 .. 264-1
    uint64_t
    Type für Kommazahlen (Gleitkomma)
    float
    4
    1.5e-45 .. 3.4e38


In den meisten C-Implementierungen entsprechen die Datentypen float und double dem international gültigen Standard IEC 559 oder dem älteren IEEE 754. Unter dieser Annahme implementiert float das einfach lange Format, double das doppelt lange Format. Hierbei umfasst ein float 32 Bit und double 64 Bit. Doubles sind also genauer. Die Größe von long doubles ist je nach Implementierung unterschiedlich, ein long double darf aber auf keinen Fall kleiner als ein double sein.

Anmerkung: 8-Bit Compiler implementieren die Datentypen im Gegensatz zu mehrbittigen Systemen für double und long double anderst. Der AVR-GCC Compiler bspw. definiert die Typen für Gleitkommazahlen alle gleich:

#define FLOAT_TYPE_SIZE 32
#define DOUBLE_TYPE_SIZE 32
#define LONG_DOUBLE_TYPE_SIZE 32
Hier wird klar, daß für beide erweiterten Gleitkomma-Typen keine genauere Berechnungen erfolgen, von daher bringt die Verwendung dieser Typen mit der Benutzung mit AVR-GCC keine Vorteile.


Variable



Programme müssen während eines Programmlaufes verwendete Daten häufig zwischenspeichern um mit ihnen später weiterrechnen zu können.

Eine Variable dient grundsätzlich dazu, eine Information zu speichern. Hierfür wird eine Stelle oder ein Speicherbereich des Computers benutzt um diese Information speichern und wieder abrufen zu können.

Grundsätzlich sind sämtliche Speicherstellen eines Speichers nummeriert, d.h. jede Speicherstelle wird durch eine Nummer identifiziert. Für den Programmierer wäre es sehr mühsam innerhalb eines Programms mit der Nummer des Speicherorts der die Information trägt zu arbeiten.

Angenommen, es würde an Speicherstelle 2087 (das wäre die Adresse im Speicher) die Länge eines Rechtecks gespeichert werden, so müsste er, um an die Information zu gelangen, innerhalb eines Programms sagen:

"Programm, lese mir den Wert an der Speicherstelle 2087 aus, damit ich die Kantenlänge des Rechtecks weiß".

Würde dieses Vorgehen mit wenigen zu speichernden Informationen noch handhabbar sein, wäre es jedoch spätestens bei Verwendung von vielleicht 20, 100 oder gar 1000 Informationen gänzlich unpraktikabel.

In C (und in jeder anderen Programmiersprache auch) können zu diesem Zweck den Speicherstellen Namen zugewiesen werden. Hierbei wird dem C Compiler (in aller Regel) sogar überlassen, an welcher Speicherstelle er diese Variable ablegt.

Damit ein Programm mit Variablen arbeiten kann, muss im Programm festgelegt werden, dass es die zu verwendende Variable geben soll.

Da es jedoch Daten unterschiedlichster Art gibt, muss zudem angegeben werden, um welche Art ( Datentyp )es sich bei der Variable handelt.

Eine Variable, die die Kantenlaenge eines Rechtecks aufnehmen
soll würde bspw. mit

    int kantenlaenge;
oder vielleicht auch mit
    int laenge;
definiert werden. Fortan kann innerhalb des Programms dieser Variable ein Wert des Datentyp's Integer zugewiesen werden (ein Integer kann ganze Zahlen, keine Kommazahlen aufnehmen).
/* -----------------------------------------------
                               001_example.c
     Beispiel fuer Variable
     17.03.2024
   ----------------------------------------------- */

#include "smallio.h"

int   laenge, breite, hoehe, flaeche;
char  ch;

int main(void)
{

  smallio_init();              

  while (1)
  {
    printf("\n\n\rDemo fuer Variable");
    printf("\n\rQuaderberechnung\n\r");
    printf("\n\rLaenge des Quaders: ");
    laenge= readint();
    printf("\n\rBreite des Quaders: ");
    breite= readint();
    printf("\n\rHoehe des Quaders : ");
    hoehe= readint();
    flaeche= (2 * (laenge*breite + laenge*hoehe + breite*hoehe));
    printf("\n\rQuaderoberflaeche: %d", flaeche);
    ch= getchar();
    printf("\n\r");
  }
}
Im Beispielsprogramm "001_example.c" werden insgesamt 4 Variablevdeklariert: laenge, breite und hoehe vom Typ Integer sowie eine Variable des Datentyps char mit dem Namen ch. Da diese Variablen nicht explizit dem Hauptprogramm "main" zugeteilt sind (und auch keiner anderen Funktion) sind diese sogenannte globale Variable.

Global / Local



Variable besitzen eine sogenannte "Gültigkeit". Im ersten Beispiel "001_example.c" können die Variable innerhalb des gesamten Programms verwendet und auf sie zugegriffen werden. Sie haben somit eine "globale" Gültigkeit.

In vielen Fällen ist dieses jedoch NICHT wünschenswert. Verwendet eine Funktion bspw. denselben Namen für eine Variable wie das Hauptprogramm, so ist nach einem Funktionsaufruf eventuell ein weiterhin benötigter Wert des Hauptprogramms verloren. Variable, die nur innerhalb einer Funktion benötigt werden sollten deshalb nur innerhalb ihrer eigenen Funktion Gültigkeit haben. Hiermit ist es möglich, in einer Funktion A dieselbe Variable wie in Funktion B oder im Hauptprogramm (main) zu verwenden ohne dass der Inhalt von einer anderen lokalen Variable überschrieben wird.

In Zählschleifen finde dieses häufig eine Anwendung:


/* -----------------------------------------------
                    002_example.c

     Beispiel lokale und globale Variable
     29.04.2015

   ----------------------------------------------- */

#include "smallio.h"

void myfunc(void)
// Funktion mit lokaler Variable i
{
  int i;                      // lokale Variabel, nur
                              // gueltig innerhalb von myfunc

  printf("Zaehler innerhalb von myfunc\n\r");
  for (i= 0; i< 10; i++)
  {
    printf("%d ",i);  
  }
  printf("\n\r\n\r");
}

int main(void)
{
  int i;                       // Variable i ist nur im
                               // Hauptprogramm main gueltig

  smallio_init();              // damit I/O Anweisungen funktioneren  

  printf("\n\r");  
  for (i= 0; i< 3; i++)
  {
    printf("%d-ter Aufruf von myfunc\n\r",i+1);
    myfunc();
  }
}

static



Eine statische Variable (static) ist grundsätzlich eine lokale Variable, die nur innerhalb einer Funktion verwendet werden kann.

Im Unterschied zu einer rein lokalen Variable wird der Speicherplatz, den eine statische Variable belegt, nach Beenden der Funktion in der sie benutzt wird NICHT freigegeben. D.h. dass die Variable nach Beendigung der Funktion ihren Werteinhalt behält und bei einem späteren erneuten Aufruf der Funktion dieser Inhalt verfügbar ist.

/* -----------------------------------------------
                     003_example.c

     Beispiel statische Variable
     29.04.2015

   ----------------------------------------------- */

#include "smallio.h"

void myfunc(void)
{
  // Funktion mit statisch lokaler Variable i
 
  static int i = 1;           // statisch lokale Variabel, nur
                              // gueltig innerhalb von myfunc

  printf("%d-ter Aufruf von myfunc\n\r", i);
  i++;
}

int main(void)
{
  char ch;

  smallio_init();              // damit I/O Anweisungen funktioneren

  printf("Demo -statische Variable-\n\r");
  printf("Taste zum Zaehlen druecken\n\r");
  while (1)
  {
    ch= getchar();
    myfunc();
  }
}


volatile



Wird 'volatile' bei der Definition einer Variablen benutzt, nimmt der Compiler an diesen keinerlei Optimierungen vor. Als 'volatile' definierte Variablen werden darum nie in Prozesorregister geladen.

'volatile' Variablen werden eingesetzt, wenn die Variablen zu einem beliebigen Zeitpunkt verändert werden können, die außerhalb der Ablaufkontrolle des Programms liegen. Typischerweise ist dies bei Variablen der Fall, die in einer Interruptroutine verwendet werden:

    volatile static char myvariable
definiert eine Variable namentlich 'myvariable', die:
  • nicht 'wegoptimiert' werden kann (d.h. der Compiler stellt die Variable zur Verfuegung auch wenn dieser 'glaubt' dass diese Variable nicht benutzt wird ('volatile').

  • die im Speicher immer denselben Platz einnimmt (static) und dieser Speicherplatz somit anderen NICHT zur Verfuegung steht.

Zeiger (Pointer)



Zeiger sind Variable, die anstelle von Datentypen die Adresse (reference) einer Variablen beinhalten, an der der Variableninhalt gespeichert ist. Ein Zeiger ist gewissermassen der Verweis oder der Link auf eine Variable, an der diese im Speicher zu finden ist.

Definition eines Zeigers / Pointers:
    Datentyp *variable;
Das "*" Zeichen zeigt an, dass es sich bei der Variable um einen Zeiger handelt. Zu beachten ist hierbei, dass das "*" Zeichen VOR dem Variablennamen steht. Ein Zeiger muss immer vom selben Datentyp sein, auf den er zeigt.

Zeiger kommen vor allem immer dann zum Einsatz, wenn Variable, die aus mehr als nur einem einem Wert (Value) bestehen, bearbeitet werden sollen.

Beispiel:

Ein Char-Array für einen Text (String) wird definiert:

    char mytext[4]= "Ich";
Hier wird der Compiler für diesen Text insgesamt 4 Bytes des Speichers belegen (3 für die Buchstaben und ein sogenanntes 0-Byte als Endekennung des Textes). Es wird angenommen, dass der Compiler hierfür die Adresse 0x0210 gewaehlt hat (Adresse wurde hier absolut zufällig gewählt und dient hier nur zur Verdeutlichung, der Compiler legt die Adresse eigentständig fest).

Im Speicher sieht das dann folgendermaßen aus:
    Adresse
    Inhalt (hex)
    Inhalt (Zeichen)

    0x0210
    0x49
    'I'
    0x0211
    0x63
    'c'
    0x0212
    0x68
    'h'
    0x0213
    0x00
    Endekennung String

Möchte man nun auf die einzelnen Elemente des Arrays zugreifen um dieses zu bearbeiten oder auszulesen hat man nun 2 Möglichkeiten:

Man kann eine "Indexvariable" erstellen die dann im Stile von:
    char mytext[4]= "Ich";
    int strindex;
    char ch;

    strindex= 0;
    ch= mytext[strindex];  

    // ch beinhaltet nun das 'I' Zeichen 
auf das Array zugreift.

Wird nun aber eine Funktion benötigt, die die Bearbeitung des Arrays vornimmt funktioniert dieses Vorgehen nur noch bedingt. Hier kommen mit der zweiten Möglichkeit die Zeiger ins Spiel:
    char mytext[4]= "Ich";
    char *p; // ein Zeiger auf Datentyp char
    char ch;

    p= &mytext[0];

    ch= *p;
    // ch beinhaltet nun das 'I' Zeichen


Erklärung:


Das "*" Zeichen vor der Variable "p" ist ein Operator und bedeutet, dass dies keine "normale" Variable ist, sondern dass sie als Inhalt eine Speicheradresse aufnimmt, die auf eine Variable vom Typ char zeigt. Somit ist "p" eine Zeigervariable (korrekt bezeichnet als Indirektionsoperator).
ch = *p;
Das Sternchen VOR dem p gibt an, dass ch der Speicherinhalt übergeben werden soll, auf den p zeigt.

Das "&" Zeichen in der Anweisung
    p = &mytext[0];
gibt an, dass dem Zeiger "p" die Adresse des Arrays "mytext[0]" zugewiesen wird. Somit hat die Zeigervariable "p" nach obigem Beispiel die Adresse 0x210 und "zeigt" somit auf den Anfang des Arrays.

Da dem Zeiger eine Adresse übergeben wird (und kein Inhalt gemeint ist), erhält die Variable p KEIN Sternchen davor.

Beispielprogramm mit Zeiger

Das Beispielprogramm zeigt, wie ein Array, dessen Inhalt ein Text (String) ist, ausgegeben werden kann:
 /* -----------------------------------------------
                     003_example.c

     Beispiel fuer Pointer
     29.04.2015

    ----------------------------------------------- 
 
#include "smallio.h" int main(void) { char mytext[] = "Mein Name ist Hase\n\r"; char *p; // ein Zeiger auf ein Zeichen char *p2; // ein zweiter Zeiger char ch; smallio_init(); p= &mytext[0]; // p beinhaltet die Adresse des Textes p2= p; // ... und p2 auch ! while(*p) // wiederhole so lange, wie das Element { // auf das p zeigt groesser als 0, // also keine Endekennung ist. ch= *p; // ch beinhaltet das Zeichen auf // das p zeigt p++; // Zeiger auf naechstes Element // setzen putchar(ch); // Zeichen ausgeben } p= p2; // da der originale Zeiger in der // While-Schleife veraendert wurde // zeigt er hiermit wieder auf den // originalen Speicherbereich }

struct


Fasst Variable zusammen:
    struct
       { [ Typ Feldname ] ;
         ...
       } [ struct-Variable ] ;
Eine struct-Definition fasst mehrere Felder unterschiedlichen Typs unter einem gemeinsamen Typbezeichner zusammen und kann (bei Angabe eines  Strukturtyp-Namens) als Typdefinition oder als aktuelle Variablendeklaration erfolgen. Ein einmal definierter Strukturtyp lässt sich (wie char, int usw.) für beliebige Variablendeklarationen verwenden.

Die Definition eines Feldes besteht aus einem Typ-Bezeichner, gefolgt von einem oder mehreren Feldnamen, die durch Komms voneinander getrennt sind. Felder
verschiedener Typen werden durch Semikolons voneinander getrennt:

    struct my_struct // Strukturtyp
    {

      char name[80], tel_nummer[20];
      int alter, groesse;

    } freund, kollege; // - Variable
Hier werden sowohl ein Strukturtyp (my_struct) als auch zwei Variablen dieses Typs (freund und kollege) vereinbart. Jede dieser beiden Variablen enthält vier Felder.

Der Zugriff auf ein Feld einer struct-Variablen erfolgt über "structName.Feldname":
    strcpy(freund.name,"Max Mustermann");
    freund.alter= 39;
Nach der Vereinbarung eines Strukturtyp-Namens können mit diesem Namen weitere Variablen desselben Typs deklariert werden, hier ein Array mit 100 Elementen der Struktur:
    struct my_struct meine_freunde[100];
Der Zugriff auf ein Element des Arrays geschieht folgendermaßen:
    strcpy(meine_freunde[32].name,"Michaela Musterfrau"); meine_freunde[32].alter= 34;
Da der Speicherbedarf einer Struktur (vor allem bei Verwendung eines Array welches innerhalb der Struktur selbst Arrays beinhaltet) sehr gross werden kann, empfiehlt es sich, Bearbeitungen von Strukturen mittels "call by reference"(Zeigerprogrammierung) vorzunehmen.

Der Zugriff auf ein Element einer Struktur, die mittels einer Adresse referenziert ist, funktioniert folgenderweise:
    alter= (*freund).alter;
Eine bessere Möglichkeit auf das Mitglied (engl. member) " alter " der zeigerreferenzierten Struktur " freund " zuzugreifen ist:
    alter= freund -> alter;
Hinweis: Das folgende Beispiel hat einen erhöhten RAM-Speicherbedarf und funktioniert erst mit Systemen, die mindestens 2 kByte RAM aufweisen!

Beispiel:
/* ----------------------------------------------------------
                       004_example.c

     Beispiel zur Verwendung von struct
   ---------------------------------------------------------- */

#include <string.h> #include "smallio.h" #define name_len 30 struct my_struct // Strukturtyp { char name[name_len], tel_nummer[20]; int alter, groesse; }; /* ---------------------------------------------------------- zeigt die Mitglieder einer einzelnen Struktur an ---------------------------------------------------------- */ void show_struct_val(struct my_struct *freunde) { char my_name[name_len]; char my_tel[20]; int alter, groesse; alter= freunde -> alter; // Zugriff auf Mitglied alter groesse= freunde -> groesse; // dto. groesse strcpy(my_name, freunde -> name); // dto. name strcpy(my_tel, freunde -> tel_nummer); // dto. tel_nummer // Anzeige der Mitglieder printf ("\n\r Name : %s", my_name); printf ("\n\r Tel. : %s", my_tel); printf ("\n\r Alter : %d", alter); printf ("\n\r Groesse: %d cm \n\r", groesse); } /* ---------------------------------------------------------- zeigt die Mitglieder eines Arrayelementes ueber einen Index an ---------------------------------------------------------- */ void show_struct_indexval(struct my_struct *freunde, int index) { char my_name[name_len]; char my_tel[20]; int alter, groesse; freunde += index; // Zeiger um die Elementposition der Struktur um // deren Position erhoehen // Mitglieder der Struktur auslesen alter= freunde -> alter; // Zugriff auf Mitglied alter groesse= freunde -> groesse; // dto. groesse strcpy(my_name, freunde -> name); // dto. name strcpy(my_tel, freunde -> tel_nummer); // dto. tel_nummer // Anzeige der Mitglieder printf ("\n\r Name : %s", my_name); printf ("\n\r Tel. : %s", my_tel); printf ("\n\r Alter : %d", alter); printf ("\n\r Groesse: %d cm \n\r", groesse); } /* --------------------------------------------------------------------------- M A I N --------------------------------------------------------------------------- */ int main(void) { struct my_struct meine_freunde[10]; int index; smallio_init(); index= 5; strcpy(meine_freunde[index].name, "Max Mustermann"); strcpy(meine_freunde[index].tel_nummer,"0555/323232"); meine_freunde[index].alter= 39; meine_freunde[index].groesse= 181; index= 7; strcpy(meine_freunde[index].name, "Michaela Musterfrau"); strcpy(meine_freunde[index].tel_nummer,"0555/454545"); meine_freunde[index].alter= 34; meine_freunde[index].groesse= 163; show_struct_val(&meine_freunde[5]); show_struct_indexval(&meine_freunde[0], 7); }
Ausgabe:
  Name   : Max Mustermann    
  Tel.   : 0555/323232
  Alter  : 39
  Groesse: 181 cm
  
  Name   : Michaela Musterfrau
  Tel.   : 0555/454545
  Alter  : 34
  Groesse: 163 cm

typedef


Mittels typedef können neue Datentypen definiert werden.

Beispiel:
    typedef unsigned char byte; // es gibt einen Datentyp "byte"
    typedef char string40[41];  // es gibt einen Datentyp "string40" der 40 Zeichen aufnehmen kann
Beispiel der Kombination von struct und typedef
    struct farbenstruct // Strukturtyp
    {
      unsigned char rot, gruen, blau
    };

    typedef struct farbenstruct rgbcolors; // es gibt einen Datentype "rgbcolors"

Programmbeispiel:
  /* -------------------------------------------------------
                        005_example.c

Beispielprogramm zu typedef ---------------------------------------------------- */ #include <stdio.h> #include <string.h> #include "smallio.h" struct farbenstruct { unsigned char rot, gruen, blau; }; typedef struct farbenstruct rgbcolors; typedef unsigned char byte; typedef char string40[40]; int main(void) { smallio_init(); rgbcolors farben; // Variable "farben" vom Typ "rgbcolors" byte b; // Variable "b" vom Typ "byte" string40 name; // Variable "name" vom Typ "string40" farben.rot= 12; farben.gruen= 24; farben.blau= 8; b= 234; strcpy(name,"Mein Name ist Hase"); printf("\n\r Farben rot: %d, gruen %d, blau %d", farben.rot, farben.gruen, farben.blau); printf("\n\r Ein Byte: %d", b); printf("\n\r Ein Text: %s\n\r", name); }

Programmablaufkontrolle


Damit Programme in einer Programmiersprache erstellt werden können, ist es notwendig, dass diese Sprache Kontrollstrukturen besitzt, die in der Lage sind, bestimmte Anweisungsblöcke nur unter bestimmten Bedingungen auszuführen oder diese bei Bedarf mehrfach zu wiederholen. Die häufigsten in C verwendeten Kontrollstrukturen sind das if-Statement zur bedingten Ausführung und die for-Schleife zur Wiederholung von zusammengefassten Anweisungen.

Daneben existieren noch die while- und do{}while Schleife und das switch() case:-Statement, das aufgrund des Inhalts einer Variablen einen auszuführenden Block anspringen kann.



if - else


Bedingte Programmausführung
    if ( ' ausdruck ' )
         ' anweisung1 '

         ODER

    if ( ' ausdruck ' )
         ' anweisung1 '
    else
         ' anweisung2 '
if ohne else: Wenn der Wert von 'ausdruck' != 0 ist (ungleich 0 entspricht 1 oder größer), wird ' anweisung1 ' ausgeführt, ansonsten wird diese Anweisung übersprungen.

if..else: Wenn ' ausdruck ' != 0 ist, wird 'anweisung1' ausgeführt und 'anweisung2 ' übersprungen. Wenn 'ausdruck' == 0 ergibt, wird 'anweisung1' übersprungen und 'anweisung2' ausgeführt.

Bei der bedingten Programmausführung ist das else optional. Ist ein else jedoch angegeben, darf zwischen einer if-Anweisung und einem
else keine weiteren Anweisungen stehen.


   -----------------------------------------------   
                     006_example.c
                     
     Beispielprogramm zu if .. else
   ----------------------------------------------- */

  #include "smallio.h"

  int main(void) 
  {
    char ch;

    smallio_init();

    printf("\n\rif - else Demo\n\r--------------\n\n\r");
    while(1)
    {
      printf("\n\rgeben sie eine Zahl ein: ");
      ch= readint();

      if (ch > 50)
      {
        printf("\n\rEingabe war groesser 50...\n\r");
      }
      else
      {
        printf("\n\rEingabe war kleiner-gleich 50...\n\r");
      }

      printf("\n\rgeben sie eine Zahl ein: ");
      ch= readint();
      
      if (ch) { printf("\n\rEingabe war NICHT 0!\n\r"); }
         else { printf("\n\rEingabe war 0 !\n\r"); }
    }
  }


while


wiederholte Ausführung
    while ( 'Ausdruck' ) 'Befehl'
'Befehl' wird so oft wiederholt, bis die Auswertung von 'Ausdruck' den Wert FALSE (0) ergibt. Jeder andere Wert als FALSE (also jeder Wert größer 0) wiederholt den oder die Anweisungen.

Die Prüfung von 'Ausdruck' findet VOR der Ausführung von 'Befehl' statt, d.h. zu Anfang jedes Schleifendurchlaufs.

Beispiel zu while:


  /* -----------------------------------------------
                   007_example.c
     
     Beispiel zu while
     ----------------------------------------------- */

  #include "smallio.h"

  int main(void)
  {
    char  mytext[] = "Mein Name ist Hase\n\r";
    char  index;
    char  ch;
    char  *ptr;
    
    smallio_init();

    ptr= &mytext[0];              // Zeiger erhaellt
                                // Adresse von mytext

    // solange der Wert, auf den ptr zeigt ungleich 0
    // ist, wird die Schleife wiederholt.

    while (*ptr)
    {
      putchar(*ptr);
      ptr++;
    }

    index= 0;;

    // wiederhole so lange, wie der gelesene
    // Buchstabe in mytext ungleich 'H' ist

    while (mytext[index] != 'H')
    {
      index++;
    }
    
    printf("Buchstabe 'H' wurde gefunden an Position: %d \n\r", index);
}

do -while


do - while Schleife
    do 'Befehl' while ( 'Ausdruck' );
'Befehl' wird solange ausgeführt, bis die Auswertung von 'Ausdruck' den Wert 0 (FALSE) ergibt. Die Prüfung von 'Ausdruck' findet jeweils NACH der Ausführung von 'Befehl' (d.h. nach dem Durchlaufen des Schleifenrumpfes) statt. Somit wird MINDESTENS ein Schleifendurchlauf ausgeführt (im Gegensatz zu einer while - Schleife, bei der die Pruefung VOR dem Schleifendurchlauf stattfindet).

Beispiel 1:

  /* ------------------------------------------------
                     008a_example.c
                     
     Beispiel zu do - while
     ------------------------------------------------ */

  #include "smallio.h"

  int main(void)
  {
    unsigned int ch;

    smallio_init();

    printf("\n\rdo - while Demo1\n\r----------------\n\r");

    do
    {
      printf("\n\rEingabe Zahl 1..99 (inkl.):  ");
      ch= readint();
    } while ( !((ch> 0) && (ch <100)));

    printf("\n\rEingabe korrekt...\n\r");
  }

Beispiel 2 - Zahlensuchspiel:

  /* ---------------------------------------------
                   008b_example.c

      Beispiel zu do .. while
    ---------------------------------------------- */
 
  #include "smallio.h"
 
  int main(void)
  {
    char suchzahl, versuche, ch;
 
    smallio_init();
 
    printf("\n\rdo - while Demo: Zahlensuchspiel");
    printf("\n\r--------------------------------\n\r");
 
    // Zufallszahl generieren, Zaehler zaehlt so
    // lange immer wieder von 1..99 , bis ein Zeichen
    // auf der seriellen Schnittstelle eingeht und
    // stopt somit den Zaehler

    printf("\n\r generiere Zufallszahl, Taste fuer Stop\n\n\r");
    do
    {
      suchzahl++;
      suchzahl= (suchzahl % 99)+1;
      printf(" **\r ");
    } while (!keypressed());
 
    printf("\r    ");                // Zahlenanzeige loeschen
    ch= getchar();
  
    versuche= 1;
  
    do
    {
      do
      {
        printf("\n\r %d", versuche);
        printf(". Eingabe ( 1..99 ):  ");
        ch= readint();
 
      } while ( !((ch> 0) && (ch <100)));
 
      if (ch == suchzahl)
      {
        printf("\n\n\r   Yeaaah, Zahl wurde gefunden");
        printf("\n\r   Benoetigte Versuche: %d", versuche);
        printf("\n\n\rTaste fuer weiter...\n\r");
      }
      else
      {
        if (ch> suchzahl) { printf("\n\rGesuchte Zahl ist kleiner !"); }
                     else { printf("\n\rGesuchte Zahl ist groesser !"); }
      }
      versuche++;
    } while (ch != suchzahl);
    ch= getchar();
  }


for


for-Schleife
    for ( 'init' ; 'endebedingung' ; 'loopanweisung' )
          'Befehl';

'Befehl' wird solange wiederholt ausgeführt, bis das Ergebnis von 'endebedingung' FALSE ergibt (d.h. den Wert 0).

'init' wird vor dem ersten Durchlauf ausgeführt und initialisiert in aller Regel eine sogenannte Laufvariable, die in den meisten Fällen in 'endebingung' verwendet wird.

'loopanweisung' wird nach jedem Durchlauf ausgeführt und in aller
Regel wird hier die Laufvariable manipuliert. Alle drei Ausdrücke sind optional. Für ein nicht definiertes 'endebedingung' wird eine 1 angenommen (was zu einer Endlosschleife führt).


   /* -----------------------------------------------
                     009_example.c

       Beispiel zu for
     ----------------------------------------------- */

  #include "smallio.h"

  int main(void)
  {
    char ch;
    char i;

    smallio_init();

    printf("\n\rfor Demo\n\r--------\n\r");

    for (i= 11; i< 21; i++)
    {
      printf("\n\rdas Quadrat von  %d = %d", i, i*i);
    }
  }
Ausgabe:
  for Demo
  --------

  das Quadrat von  11 = 121
  das Quadrat von  12 = 144
  das Quadrat von  13 = 169
  das Quadrat von  14 = 196
  das Quadrat von  15 = 225
  das Quadrat von  16 = 256
  das Quadrat von  17 = 289
  das Quadrat von  18 = 324
  das Quadrat von  19 = 361
  das Quadrat von  20 = 400
            

switch - case


Mehrweg-Verzweigung
    switch ( 'Ausdruck' ) 'Anweisung'
'Ausdruck' wird berechnet und muss einen Integerwert ergeben, danach folgt ein Vergleich mit der 'Konstante' eines jeden case-Zweiges. Bei Übereinstimmung werden der oder die Anweisungen dieses Zweiges ausgeführt. Jeder Konstanten-Wert der case-Liste darf nur einmal definiert sein.

Die optionale break-Anweisung nach den Programmanweisungen führt einen Sprung zum Ende von switch aus, d.h. zum nächsten auf das switch-Konstrukt folgenden Befehl.

Der (optionale) default-Zweig wird nur dann ausgeführt, wenn alle vorherigen Vergleiche fehlgeschlagen sind.
    switch(Ausdruck)
    {
       case Konstante1 : Anweisung;
         break;
       case Konstante2 : Anweisung;
         break;
        .
        .
        .
       default: Anweisung;
    }
Switch - case ist somit in Verbindung mit vielen Verzweigungsoptionen meistens übersichtlicher als stark verschachtelte else - if Ketten.

Beispiel:
  /* -----------------------------------------------
                     010_example.c

       Beispiel fuer switch-case
     ----------------------------------------------- */
 
  #include "smallio.h"
 
  uint16_t calculate(char func, uint16_t x, uint16_t y)
  {
    uint16_t z;
 
    switch (func)
    {
      case '/' : { z= x / y; break; }      // div
      case '+' : { z= x+y; break; }        // add
      case '-' : { z= x-y; break; }        // subb
      case 'i' : { z= x+1; break; }        // inc
      case 'x' :
      case 'X' :
      case '*' : { z= x*y; break; };       // mul

      case 's' :                           // sqrt
      case 'e' :                           // exp
      case 'm' : { printf(" Funktion nicht verfuegbar !");    // mod
                   break;
                 }
    default : {
                printf(" Unerwarteter Fehler !");
              }
    }
    return z;
  }
 
  int main(void)
  {
    char func;
    uint16_t op1, op2;
 
    smallio_init();                
 
    printf("\n\rswitch - case demo : Integer calc");
    printf("\n\rPC-Programmabbruch mit CTRL-C\n\r");
    printf(    "---------------------------------");
    while(1)
    {
      printf("\n\n\rOperand1: ");
      op1= readint();

      printf("\n\rRechenfunktion (* + - / i): ");
      func= getchar();
      printf("%c", func);

      if (func != 'i')
      {
        printf("\n\rOperand2: ");
        op2= readint();
      }

      printf("\n\n\r Ergebnis: %d", calculate(func,op1,op2));
    }
  }