peace software
  Berndt Wischnewski  Richard-Wagner-Str. 49  10585 Berlin 
  Tel.: 030 - 3429075  FAX : 030 34704037  email: webmaster@peacesoftware.de  Ust-ID: DE135577379 
<= Previous

 

C Kurs - man müßte jetzt mal etwas struct in die Sache bringen...

 

1. Deklaration einer struct

Das typische Beispiel für den Einsatz von Strukturen sind Personen bezogene Daten. Eine Person hat einen Namen, einen Vornamen ein Geschlecht ein Geburtsdatum, eine Adresse, ein Gehalt, eine Kreditkartennummer usw. Es ist dann natürlich toll wenn C uns die Möglichkeit bieten würde, diese ganzen verschiedenen Daten zusammenzufassen, tut es natürlich, dass Zauberwort heißt struct. Jetzt zuerst einmal ein Beispiel für die Deklaration einer Struktur:

struct adresse
{
  char strasse[100];
  char stadt[100];
  int plz;
}; // man vergißt schnell mal das Semikolon am Ende

Die Definition einer struct ist ein statement und wird deshalb mit einem Semikolon abgeschlossen. Was darf in eine Struktur eingeschlossen werden? Antwort: alles was es so gibt, Variablen aller Grundtypen, Arrays, andere Strukturen, Arrays von Strukturen, Pointer usw.

Bis jetzt wurde noch keine Variable deklariert, die man im Programm benutzen kann, es kommt auch die Frage auf, wo im Programm macht man diese Deklaration? Die Antwort ergibt sich, wenn man weiss wie oft man die Struktur braucht. Braucht man sie nur ein zwei mal an einer bestimmten Stelle, dann kann man das ganze so lösen:

#include <stdio.h>

int main (void)
{
  struct adresse
  {
    char strasse[100];
    char ort[100];
    int plz;
  } willis_adresse, gabis_adresse;

  // tue irgend etwas sinvolles ...

  printf("bye babes\n");
  return 0;
}

In diesem, eher selten vorkommenden, Beispiel wurde jetzt mitten im Programm die Struktur definiert und gleich zwei Variablen dieser Struktur erzeugt, willis_adresse und gabis_adresse, die Sie jetzt sofort benutzen können. Braucht man die Datenstruktur öfters an verschiedenen Stellen im Programm könnte das ganze jetzt so aussehen:

#include <stdio.h>

struct Adresse
{
  char strasse[100];
  char ort[100];
  int plz;
};

int main (void)
{
  struct Adresse adressen[100]; // lokaler Array

  // do something useful

  printf("Hello, World!\n");
  return 0;
}


void someFunction(void)
{
  struct Adresse willis_adresse; // lokale Variable

  // bearbeite willis Adresse
}

Die Deklaration der Adresse-Struktur liegt jetzt nicht mehr innerhalb einer Funktion sonder ist global verfügbar, sinvoll wäre es so etwas in ein header-file zu stecken. Wenn man die Struktur in ganz vielen über mehrere Sourcode files verteilten Stellen im Programm braucht dann gibt es die Möglichkeit sich seine Struktur als eigenen Datentyp mit der typedef Anwesung zu deklarieren:

 

2. typedef Anweisung

Die typedef Anweisung hat erstmal gar nichts mit Strukturen zu tun, wird jetzt aber trotzdem nebenbei mal erklärt. Mit typedef hat man in C die Möglichkeit, eigene Datentypen zu definieren, von denen man sich dann beliebige Variablen deklarieren kann. Prinzipiell funktioniert die Definition eines neuen Datentypes so:

typedef datentyp neuer_name_des_datentyp;

Wenn Sie mal Sourcode aus dem Intenet laden und da steht dann sowas wie:

INT32 variableX;

dann hat der Programmierer in irgend einem header file

typedef int INT32;

deklariert. Warum? um sicher zu stellen, dass variableX ein 32 bit integer ist. Am Anfang des Kurses wurde mal erwähnt, dass verschiedene C Compiler unter verschiedenen Betriebssystemen sich da unterschiedlich verhalten können. Die Deklaration eines eigenen Datentyps unserer Adressenstruktur sieht dann in unserem kleinen Programm folgendermaßen aus:

#include <stdio.h>

// hier wird die struct Adresse als neuer Datentyp definiert:
typedef struct
{
  char strasse[100];
  char ort[100];
  int plz;
} Adresse;

int main (void)
{
  Adresse adressen[100]; // ein array mit 100 Elementen vom neuen Datentyp Adresse wird erzeugt

  // do something useful

  printf("Hello, World!\n");
  return 0;
}


void someFunction(void)
{
  Adresse willis_adresse; // eine Variable vom neuen Datentyp Adresse wird erzeugt

  // bearbeite willis Adresse
}

Ist doch sehr elegant oder? Ich deklariere meine Strukturen meistens mit typedef, schon weil man sich im weiteren spart immer wieder struct... einzutippen.

 

3. Zugriff auf die Elemente einer Struktur

Wie schreibt und liest man nun die inneren Variablen eine struct? mit der . (Punkt) Notation. Zuerst kommt der Name der Variablen der struct, dann ein Punkt und dann die innere Variable der Struktur;

#include <stdio.h>

typedef struct
{
  char strasse[100];
  char ort[100];
  int plz;
} Adresse;

int main (void)
{
  Adresse gabisAdresse;
  int einePLZ;

  sprintf(gabisAdresse.strasse, "Am Friseursalon 42");
  sprintf(gabisAdresse.ort, "Berlin");
  gabisAdresse.plz = 12345;

  einePLZ = gabisAdresse.plz;

  return 0;
}

Und jetzt das ganze mal mit einen array-of-struct zuerst kommt der Name der Arrayvariablen, dann der Index in eckigen Klammern, dann der Punkt und zuletzt die innere Variable der Struktur:

#include <stdio.h>
typedef struct
{
  char strasse[100];
  char ort[100];
  int plz;
} Adresse;

int main (void)
{
  Adresse meineAdressen[100];
  int einePLZ

  // das erste Element des Arrays meineAdressen wird hier als Beispiel mal mit Daten gefüllt
  sprintf(meineAdressen[0].strasse, "Richard-Wagner-Str 49");
  sprintf(meineAdressen[0].ort, "Berlin");
  meineAdressen[0].plz = 10585;

  einePLZ = meineAdressen[0].plz

  return 0;
}

 

Structs können recht komplex werden, im wirklichen Leben genauso wie im folgenden Beispiel aus der Computergraphic:

#include <stdio.h>

// die Definition der struct für die Farbe
typedef struct
{
  int red, green, blue;
} Color;

// die Definition der struct eines Punktes
typedef struct
{
  int x, y; // Koordinaten des Punktes
} Point;

// die Definition der struct eines Rechtecks
typedef struct
{
  Point edges[4]; // Ecken des Rechtecks
  int width; // Breite des Rechtecks
  int height; // Höhe des Rechteckes
  Color colorcode; // Farbe des Rechtecks
} Rectangle;

// die Definition einer Gitter Struktur
typedef struct
{
  Point startPoint;
  Rectangle leftRects[10], centerRects[10], rightRects[10];
} Grid;


int main (void)
{
  Grid theGrid;
  int i;

  // der Startpunkt des Grid wird gesetzt
  theGrid.startPoint.x = 20;
  theGrid.startPoint.y = 40;

  for(i = 0; i < 10; i++)
  {
    // die Farben der Rechtecke des Grids werden gesetzt
    // die leftRects werden grün
    theGrid.leftRects[i].colorcode.red = 0;
    theGrid.leftRects[i].colorcode.green = 255;
    theGrid.leftRects[i].colorcode.blue = 0;
    // die centerRects sollen rot sein
    theGrid.centerRects[i].colorcode.red = 255;
    theGrid.centerRects[i].colorcode.green = 0;
    theGrid.centerRects[i].colorcode.blue = 0;
    // und die rightRects schwarz
    theGrid.rightRects[i].colorcode.red = 0;
    theGrid.rightRects[i].colorcode.green = 0;
    theGrid.rightRects[i].colorcode.blue = 0;

    // Breite und Höhe der Rechtecke setzen
    theGrid.leftRects[i].width = 100;
    theGrid.leftRects[i].height = 50;
    theGrid.centerRects[i].width = 200;
    theGrid.centerRects[i].height = 50;
    theGrid.rightRects[i].width = 100;
    theGrid.rightRects[i].height = 7;

    // obere, linke Ecke
    theGrid.leftRects[i].edges[0].x = theGrid.startPoint.x + (i * 20);
    theGrid.leftRects[i].edges[0].y = theGrid.startPoint.y + 20;
    theGrid.centerRects[i].edges[0].x = theGrid.startPoint.x + (i * 10);
    theGrid.centerRects[i].edges[0].y = theGrid.startPoint.y + 320;
    theGrid.rightRects[i].edges[0].x = theGrid.startPoint.x + (i * 50);
    theGrid.rightRects[i].edges[0].y = theGrid.startPoint.y + 620;
    // obere, rechte Ecke
    theGrid.leftRects[i].edges[1].x = theGrid.leftRects[i].edges[0].x + theGrid.leftRects[i].width;
    theGrid.leftRects[i].edges[1].y = theGrid.leftRects[i].edges[0].y;
    theGrid.centerRects[i].edges[1].x = theGrid.centerRects[i].edges[0].x + theGrid.centerRects[i].width;
    theGrid.centerRects[i].edges[1].y = theGrid.centerRects[i].edges[0].y;
    theGrid.rightRects[i].edges[1].x = theGrid.rightRects[i].edges[0].x + theGrid.rightRects[i].width;
    theGrid.rightRects[i].edges[1].y = theGrid.rightRects[i].edges[0].y;
    // linke, untere Ecke
    theGrid.leftRects[i].edges[2].x = theGrid.leftRects[i].edges[0].x;
    theGrid.leftRects[i].edges[2].y = theGrid.leftRects[i].edges[0].y + theGrid.leftRects[i].height;
    theGrid.centerRects[i].edges[2].x = theGrid.centerRects[i].edges[0].x;
    theGrid.centerRects[i].edges[2].y = theGrid.centerRects[i].edges[0].y+ theGrid.centerRects[i].height;
    theGrid.rightRects[i].edges[2].x = theGrid.rightRects[i].edges[0].x;
    theGrid.rightRects[i].edges[2].y = theGrid.rightRects[i].edges[0].y+ theGrid.rightRects[i].height;
    // rechte, untere Ecke
    theGrid.leftRects[i].edges[3].x = theGrid.leftRects[i].edges[0].x + theGrid.leftRects[i].width;
    theGrid.leftRects[i].edges[3].y = theGrid.leftRects[i].edges[0].y + theGrid.leftRects[i].height;
    theGrid.centerRects[i].edges[3].x = theGrid.centerRects[i].edges[0].x + theGrid.centerRects[i].width;
    theGrid.centerRects[i].edges[3].y = theGrid.centerRects[i].edges[0].y + theGrid.centerRects[i].height;
    theGrid.rightRects[i].edges[3].x = theGrid.rightRects[i].edges[0].x + theGrid.leftRects[i].width;
    theGrid.rightRects[i].edges[3].y = theGrid.rightRects[i].edges[0].y + theGrid.rightRects[i].height;
  }

  return 0;
}

 

4. wieviel Speicherplatz belegt eine Struktur eigentlich? - sizeof

Das Grid aus dem Beispiel oben sieht ganz schön komplex aus, wie bekommt man nun raus, wieviel Speicherplatz ein Variable der Struktur Grid belegt? Man kann natürlich selber nachzählen, die Struktur Color besteht z.B. aus 3 integer * 4 Byte pro integer = 12 byte. Wenn es zu komplex zum Kopfrechnen wird, dann gibt es in C die sizeof Anweisung. sizeof funktioniert wie folgt:

int_variable = sizeof(Datentyp);

oder

int_variable = sizeof(variablenname);

in beiden Fällen erhält man die Größe der Variable oder des Datentypes der Variablen in Byte.
Für Integer

int theSize = sizeof(int);

erhält man 4 Bytes und für unsere obige Grid Struktur

int theSize = sizeof(theGrid)

erhält man eine Größe von 1568 Bytes.

 

5. Strukturen und Pointer

structs können natürlich als Variable an andere Funktionen übergeben werde, aber sollten sie das wirklich? Schauen wir uns folgendes Beispiel ohne Pointer an:

#include <stdio.h>

typedef struct
{
  char strasse[100];
  char ort[100];
  int plz;
} Adresse;

typedef struct
{
  char name[100];
  char vorname[100];
  int sex;
  Adresse adresse;
} Person;

// function, an welche eine Variable der Struktur Adresse übergeben wird
void PersonAusgeben(Person aPerson)
{
  printf("Name: %s\n", aPerson.name);
  printf("Vorname: %s\n", aPerson.vorname);
  printf("Straße: %s\n", aPerson.adresse.strasse);
  printf("Ortße: %s\n", aPerson.adresse.ort);
  printf("PLZ: %d\n", aPerson.adresse.plz);
  if(aPerson.sex == 1)
    printf("Anrede: Herr\n");
  else if(aPerson.sex == 2)
    printf("Anrede: Frau\n");
  else
    printf("Anrede: ?\n");
}

int main (void)
{
  Person hansi;

  // jetzt wird die Struktur der Person hansi gefüllt
  sprintf(hansi.vorname, "Hans");
  sprintf(hansi.name, "Meyer");
  hansi.sex = 1;
  sprintf(hansi.adresse.strasse, "Spandauer Damm 42");
  sprintf(hansi.adresse.ort, "Berlin");
  hansi.adresse.plz = 12345;

  // uebergeben wir eine Variable der Struktur Person an eine Funktion:
  PersonAusgeben(hansi);

  // wie groß ist die Struktur eigentlich? Ergebnis 408 Bytes
  int size = sizeof(Person);
  printf("Strukturgröße: %d\n", size);

  return 0;
}

Im Kapitel über Pointer haben wir gelernt, das der C Compiler beim übergeben einer Variable an eine Funktion, eine Kopie der Variablen anlegt. In diesen hier wird die gesamte Struktur der Variablen hansi neu im Speicher Ihres Rechners anglegt und der Inhalt von Hansi dort hineinkopiert. In der Ausgabe des Programms wird die Größe der Struktur mit 408 Bytes angegeben. Wenn man nun statt der Struktur selber, nur einen Pointer auf die Struktur übergibt, braucht der Computer nur die 8 Byte Adresse der Struktur übergeben. Das Programm wird also an diese Stelle um den Faktor 50 schneller, beachtlich oder?

Wie dereferenziert man in C nun einen Pointer auf eine Struktur? Nun an Stelle des Punktes benutzt man die Pfeil Nomenklatur ->, minus-größer, auch um darzustellen, dass auf etwas gezeigt wird:

  pointer_to_struct->inner_variable

Hier jetzt das Beispiel von vorhin, realisiert mit Pointern:

#include <stdio.h><

typedef struct
{
  char strasse[100];
  char ort[100];
  int plz;
} Adresse;

typedef struct
{
  char name[100];
  char vorname[100];
  int sex;
  Adresse adresse;
} Person;

// function, an welche ein Pointer auf die Struktur Adresse übergeben wird
void PersonAusgeben(Person *personPtr)
{
  printf("Name: %s\n", personPtr->name);
  printf("Vorname: %s\n", personPtr->vorname);
  printf("Straße: %s\n", personPtr->adresse.strasse);
  printf("Ortße: %s\n", personPtr->adresse.ort);
  printf("PLZ: %d\n", personPtr->adresse.plz);
  if(personPtr->sex == 1)
    printf("Anrede: Herr\n");
  else if(personPtr->sex == 2)
    printf("Anrede: Frau\n");
  else
    printf("Anrede: ?\n");
}


int main (void)
{
  Person hansi;

  // jetzt wird die Struktur der Person hansi gefüllt
  sprintf(hansi.vorname, "Hans");
  sprintf(hansi.name, "Meyer");
  hansi.sex = 1;
  sprintf(hansi.adresse.strasse, "Spandauer Damm 42");
  sprintf(hansi.adresse.ort, "Berlin");
  hansi.adresse.plz = 12345;

  // uebergeben wir die Adresse der Variablen der Struktur Person an eine Funktion:
  PersonAusgeben(&hansi);

  // wieviel bytes werden denn jetzt an die Funktion übergeben? Ergebnis: 8 bytes
  int size = sizeof(&hansi);
  printf("Bytes: %d\n", size);

  return 0;
}

Der C Compiler -weiß- wie groß die Strukturen sind deshalb funktioniert die Pointerarithmetik auch mit Strukturen, siehe vorheriges Kapitel, Sie müssen natürlich selber aufpassen, dass der Pointer noch auf etwas sinnvolles zeigt. Hier mal ein Beispiel mit einigen Berliner Straßen, Sie dürfen raten in welchem Berliner Bezirk ich aufgewachsen bin und immer noch wohne:

#include <stdio.h>

typedef struct
{
  char strasse[100];
  char ort[100];
  int plz;
} Adresse;

void StrasseAusgeben(Adresse *adressenPtr)
{
  printf("Strasse: %s\n", adressenPtr->strasse);

  // diese Operation kann schiefgehen,
  // wenn der Pointer schon auf das letzte Element des Arrays zeigt, dann zeigt er danach in den Wald
  adressenPtr = adressenPtr + 3; // springe im Adressen-Array 3 Elemente vorwärts
  printf("Strasse: %s\n", adressenPtr->strasse);

  adressenPtr--; // springe im Adressen-Array ein Element rückwärts
  printf("Strasse: %s\n", adressenPtr->strasse);
}


int main (void)
{
  Adresse meineAdressen[10];

  sprintf(meineAdressen[0].strasse, "Spandauer Damm");
  sprintf(meineAdressen[1].strasse, "Richard-Wagner-Straße");
  sprintf(meineAdressen[2].strasse, "Wilmersdorfer Straße");
  sprintf(meineAdressen[3].strasse, "Kaiser-Friedrich-Straße");
  sprintf(meineAdressen[4].strasse, "Schloßstraße");
  sprintf(meineAdressen[5].strasse, "Kurfürstendamm");
  sprintf(meineAdressen[6].strasse, "Otto-Suhr-Allee");
  sprintf(meineAdressen[7].strasse, "Ernst-Reuter-Platz");
  sprintf(meineAdressen[8].strasse, "Hardenbergstraße");
  sprintf(meineAdressen[9].strasse, "Uhlandstraße");

  StrasseAusgeben(&meineAdressen[4]); // übergebe Speicheradresse des 5. Elements des Adressen-Arrays

   return 0;
}

 

<= Previous











C++ Literatur: