Programmieren
 
 
 
 
 
C-Grundkurs
 
 
 
 
 
.. Strukturen - struct
 
 

 
 
 
 

 

Strukturen sind eine Art Variable mit Untervariablen, bei der die Untervariablen unterschiedliche Typen besitzen können. Anders als bei der union (Variante) kann zu einem Zeitpunkt, jede dieser Untervariablen einen eigenen Wert besitzen.


Dementsprechend gross wird der benötigte Speicherraum einer Strukturvariablen. Bei der unten angegebenen Struktur sind das mindestens 11 Bytes, denn für den Integer i werden 2 Bytes, für die Fliesskommazahl x, 8 Bytes und für den Character c, 1 Byte benötigt.


Beispiel 1:

 

struct {
         int i;
         double x;
         char c;
       } y, z;

 

Im oberen Beispiel werden zwei Variablen y und z einer Strukur deklariert. Jede dieser zwei Variablen y und z besitzt die Untervariablen i, x, und c, deren Variablentypen voneinander abweichen. Es können folgend keine weiteren Variablen von dieser Struktur abgeleitet werden.
Die Variablen y und z werden im Speicher eines Computers jeweils mindestens 11 Bytes benötigen. Die genaue Anzahl, die von der Ausrichtung im Speicher abhängt kann mit dem sizeof()-Operator ermittelt werden.

 

 

 

 

 

Beispiel 2:

 

struct zahl {
              int i;
              double x;
              char c;
            };

 

Im oberen Beispiel wird eine Struktur mit dem Namen Zahl deklariert. In der Folge kann der Name dieser Struktur wie ein Typbezeichner verwendet werden, so dass man zu beliebiger Zeit Variable von der Art dieser Struktur erzeugen kann. Beispielsweise ..


struct zahl y;

                        oder an andere Stelle ..

struct zahl z;


Jede dieser zwei Variablen y und z besitzt die Untervariablen i, x, und c, deren Variablentypen voneinander abweichen.

 

 

 

 

 

Beispiel 3:

 

typedef struct {
                 int i;
                 double x;
                 char c;
               } zahl;

 

 

Wen es stört, dass man vor den Namen des Struktur-Variablentyps immer struct schreiben muss, der kann mit der Schlüsselwort typedef arbeiten. Dieses funktioniert ein Stück weit wie #define. Es wird also das gesamte Literal struct { ...} hinter dem Ausdruck zahl verborgen. Nun kann man so tun, als sei der Strukturname zahl ein eigenständiger Variablentyp, denn man darf schreiben ..


zahl y;

                        oder an andere Stelle ..

zahl z;


.. was indirekt dem Beispiel 1 ähnlich ist.

Jede der zwei Variablen y und z besitzt die Untervariablen i, x, und c, deren Variablentypen voneinander abweichen.

 

 

 

 

 

Deklaration und Initialisation

Schon bei der Deklaration können den Untervariablen einer Strukturvariablen Werte übergeben werden.


Beispiel:

zahl z = {0x1234, 1234.57, 0xC3};

bei neueren Compilern kann es notwendig sein, um jede Untervariable geschweifte Klammern zu setzen, wodurch der Ausdruck dann wie folgt aussieht.

zahl z = {{0x1234}, {1234.57}, {0xC3}};


Nach dieser Zuweisung werden die Variablen folgende Werte besitzen ..

z.i = 0x1234, z.x = 1234.57, z.c = 0xC3

 

 

 

 

 

Zuweisung von Werten und Zugriff auf die Variableninhalte zur Laufzeit
Um die Untervariablen zu erreichen, werden sie wie gerade angedeutet, durch einen Punkt '.' von der Hauptvariablen getrennt. Die Zeile ..
z.i = 0x1234;
.. füllt die Untervariable i der Hauptvariablen z mit dem hexadezimalen Wert 1234. Dementsprechend wird die Fliesskomma-Untervariable x, wie folgt mit einem Wert beschrieben.
z.x = 1234.57;

Sollen die Variablen wieder gelesen werden gilt gleiches. Eine Variable r vom typ double kann mit dem Inhalt von z.x im Verbund mit einer Berechnung, beispielsweise durch folgende Zeile gefüllt werden.
r = z.x – 26.36;

Bei Strukturen ist es (anders als bei Varianten) möglich, jeder Untervariablen gleichzeitig einen eigenen Wert einzuschreiben. Das nachfolgende Programm zeigt diese Eigenschaften aus der Warte des Programmierers.


Ein Beispielprogramm

Es deklariert die bisher besprochene struct zahl und leitet in main() aus dieser eine Variable mit der Bezeichnung z ab. Diese besteht aus den Untervariablen z.i, z.x und z.c, die mit ihrer Deklaration auch gleich definiert und mit 0 initialisiert werden. Jede dieser Variablen wird folgend und nacheinander mit einem neuen Wert beschrieben, der dann in Form eines Speicherbyte- Listings ausgegeben wird. Um den Einfüllablauf der Werte in den Speicherraum der Struktur im Detail zu zeigen, wurden nach jeder Setzung eines Wertes, alle Speicherzellen des Speicherraums ausgegeben.

Das Programm besteht aus zwei Teilen, wobei die Funktion scan_struct() grundsätzlich alle 11 Bytes der Variablen z ausliest und hexadezimal darstellt. Je zwei hexadezimale Ziffern stellen ja bekannterweise den Inhalt eines Bytes dar, und so werden von scan_struct() 22 Ziffern für die 11 Bytes erzeugt. scan_struct stellt den elf gelesen Bytes die Adresse des ersten Bytes voraus. Mehr braucht man von dieser Funktion nicht zu wissen, aber es soll niemand davon abgehalten werden, über ihre Arbeitsweise weiter nachzudenken.

scan_struct() gibt also nach jedem Aufruf den Inhalt aller 11 Bytes der Variablen z wieder und nennt auch die Ablageadresse des ersten Bytes im Speicher. Dabei ist es interessant zu sehen, wie mit jeder Zuordnung eines Wertes der Speicherraum der Struktur weiter aufgefüllt wird.

main() gibt die Adresse von z auch noch einmal aus, darüberhinaus aber auch die der Untervariablen. Interessant ist es, dass alle Variablen adressmässig hintereinander folgen. Zudem wird deutlich, dass sich die oben genannte Berechnung des benötigten Speicherraums tatsächlich, aus der Summe der benötigten Einzelbytes, ergibt.

In der Folge wird jeder Untervariablen ein Wert zugewiesen, und mit scan_struct() die daraus resultierende Speicherbelegung ermittelt. Im Ausgabebild des Programms wird deutlich, wie nacheinander jede Untervariable ihren Platz im Gesamtspeicher der Hauptvariablen erhält.

 

 

 

 

// struct.cpp
// ------------  Version 17.06.2007
//               Borland IDE C++ 3.1 oder 5.02
//               Model:  SMALL
//               Char:   unsigned
//               bearbeitet: www.GoBlack.de, D.Schwarzer

// Hardware:     jeder DOS-PC

#include <stdio>    // für printf()
#include <conio>    // für getch()

// typedef struct {int i; double x; char c;} zahl; // Alternative
struct zahl {int i; double x; char c;};


// Dieser Programmteil liest die 11 Bytes der übergebenen Variablen
// z vom Typ (structur) zahl und gibt sie samt Anfangsadresse
// aufeinanderfolgend zum Bildschirm aus.

void scan_struct(zahl z)
{
  int n=0;
  char* m;

  m = (char*)&z;
  printf ("\n Adr von z: %X - Inhalt: ", &z);
  for (n=sizeof(z)-1; n>=0; n--){printf("%02X", *(m+n));}
}

// das Beispielprogramm ermittelt für die Variable z und
// jede ihrer Untervariablen z.i, z.x, z.c die Anzahl der
// belegten Bytes, sowie deren Anfangsadressen im Speicher
// dann wird jeder der drei Untervariablen ein legaler
// Wert zugewiesen und beobachtet zu welchem Resultat das
// bei der Füllung der Struktur und ihrer Untervariablen
// führt.

void main (void)
{
  // Deklaration und Definition einer Variablen z vom neuen Typ
  // (struktur)zahl
  struct zahl z={0,0,0};

  // Anzahl der belegten Bytes im Speicher und ..
  // Anfangsadresse der Variablen
  printf ("\nbelegte Bytes im Speicher / AnfangsAdresse");
  printf ("\nz   = %2d   Adr: %04Xh", sizeof(z),   &z);
  printf ("\nz.i = %2d   Adr: %04Xh", sizeof(z.i), &z.i);
  printf ("\nz.x = %2d   Adr: %04Xh", sizeof(z.x), &z.x);
  printf ("\nz.c = %2d   Adr: %04Xh", sizeof(z.c), &z.c);
  printf ("\n-----");

  // Wert für z.i => noch keine Werte für z.x und z.c
  z.i = 0x1234;
  scan_struct(z);
  printf ("\n Wert fuer IntegerVariable z.i");
  printf ("\nInhalt hex   z.i = %04Xh",  z.i);
  printf ("\nInhalt float z.x = %g",     z.x);
  printf ("\nInhalt hex   z.c = %02Xh",  z.c);
  printf ("\n-----");

  // Wert für z.x => noch kein Wert für z.c
  z.x = 1234.57;
  scan_struct(z);
  printf ("\n Wert fuer FliesskommaVariable z.x");
  printf ("\nInhalt hex   z.i = %04Xh",  z.i);
  printf ("\nInhalt float z.x = %g",     z.x);
  printf ("\nInhalt hex   z.c = %02Xh",  z.c);
  printf ("\n-----");

  // Wert für z.c => Struktur ist gefüllt
  z.c = 0xC3;
  scan_struct(z);
  printf ("\n Wert fuer CharacterVariable z.c");
  printf ("\nInhalt hex   z.i = %04Xh",  z.i);
  printf ("\nInhalt float z.x = %g",     z.x);
  printf ("\nInhalt hex   z.c = %02Xh",  z.c);

  getch();
}

 

 

 

 

 

 

 

Der Ausgabebildschirm des obigen Programms. Zu beachten ist, dass die Zuweisung von Adressen der Variablen zur Laufzeit erfolgt, .. sie können also bei jedem Lauf des Programms andere Werte annehmen.


An diesem Beispiel kann auch der Unterschied zwischen der Codierung von Fliesskommazahlen im Gegensatz zu der von Integerzahlen betrachtet werden. Setzen Sie mal beide Werte gleich, also z.i=1234 und z.x=1234. Während die Integerzahl mit 1234 im Listing gut auszumachen ist, ergibt sich für die Fliesskommazahl 4093480000000000. Aus diese Darstellung würde man nicht unmittelbar darauf schliessen das hier die Zahl 1234 steht.

 

 

 

 

 

Hinweis:
Eine Zeile, die nur der Hauptvariablen z einen Wert zuweist, (z.B. z = 1234) führt zu einer Fehlermeldung des Compilers.
Dagegen erscheint nur eine Warnung, wenn der Wert von z ausgelesen wird. Da im obigen Beispiel die Adresse von z mit der von z.i übereinstimmt, wird der Intergerwert von z.i ausgegeben. Man sollte die Warnung also ernst nehmen.

 

 
 
 
www..de