EMZETT.
Login

Kurz: Eine struct fasst zusammengehörige Variablen verschiedener Typen unter einem Namen zusammen:

Teil des Kurses C

Kapitel 10 von 15 im Kurs C (Abschnitt „Eigene Datentypen“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

struct: Daten bündeln

Eine struct fasst zusammengehörige Variablen verschiedener Typen unter einem Namen zusammen:

#include <stdio.h>
#include <string.h>
 
struct Person {
    char name[32];
    int alter;
    double gewicht;
};
 
int main(void) {
    struct Person mia = { "Mia", 17, 58.5 };
    struct Person tom = { .name = "Tom", .alter = 19 };      // benannte Initialisierung, Rest = 0
    mia.alter++;
    strcpy(tom.name, "Tom K.");
    printf("%s %d %.1f\n", mia.name, mia.alter, mia.gewicht);
    printf("%s %d %.1f\n", tom.name, tom.alter, tom.gewicht);
    struct Person kopie = mia;                                // Struktur komplett kopieren
    kopie.alter = 99;
    printf("%d %d\n", mia.alter, kopie.alter);
    printf("Größe: %zu\n", sizeof(struct Person));
    return 0;
}

Ausgabe:

Mia 18 58.5
Tom K. 19 0.0
18 99
Größe: 48

Mit typedef bekommt der Typ einen kürzeren Namen:

#include <stdio.h>
#include <math.h>
 
typedef struct {
    double x, y;
} Punkt;
 
typedef struct {
    Punkt links_oben;
    double breite, hoehe;
} Rechteck;
 
double abstand(Punkt a, Punkt b) { return hypot(a.x - b.x, a.y - b.y); }
double flaeche(const Rechteck *r) { return r->breite * r->hoehe; }     // -> bei Zeigern
 
int main(void) {
    Punkt a = {0, 0}, b = {3, 4};
    Rechteck r = { {1, 2}, 5, 3 };
    printf("%.1f %.1f %.1f\n", abstand(a, b), flaeche(&r), r.links_oben.y);
    return 0;
}

Ausgabe:

5.0 15.0 2.0

Große Strukturen übergibst du als const Struktur * (kein Kopieren); p->feld ist die Kurzform von (*p).feld. (Zum Linken von math.h kann -lm nötig sein.)

Arrays von Strukturen

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef struct { char name[16]; int punkte; } Spieler;
 
static int nach_punkten(const void *a, const void *b) {
    return ((const Spieler *) b)->punkte - ((const Spieler *) a)->punkte;     // absteigend
}
 
int main(void) {
    Spieler liste[] = { {"Mia", 120}, {"Tom", 95}, {"Zoe", 210} };
    int n = sizeof liste / sizeof liste[0];
    qsort(liste, n, sizeof liste[0], nach_punkten);
    for (int i = 0; i < n; i++) printf("%d. %-4s %3d\n", i + 1, liste[i].name, liste[i].punkte);
    return 0;
}

Ausgabe:

1. Zoe  210
2. Mia  120
3. Tom   95

enum: benannte Konstanten

#include <stdio.h>
 
enum Ampel { ROT, GELB, GRUEN };                   // 0, 1, 2
typedef enum { MO = 1, DI, MI, DO, FR } Tag;      // 1, 2, 3, ...
 
const char *name(enum Ampel a) {
    switch (a) {
        case ROT:   return "rot";
        case GELB:  return "gelb";
        case GRUEN: return "grün";
    }
    return "?";
}
 
int main(void) {
    enum Ampel a = GELB;
    Tag t = MI;
    printf("%s %d %d %d\n", name(a), a, t, FR);
    return 0;
}

Ausgabe:

gelb 1 3 5

union: ein Speicherplatz, mehrere Sichten

Alle Felder einer union liegen am selben Speicherplatz. Es ist nur eines davon gleichzeitig gültig. Üblich ist die Kombination mit einem Kennzeichen (tagged union):

#include <stdio.h>
 
typedef struct {
    enum { ZAHL, TEXT } art;
    union { int zahl; const char *text; } wert;
} Wert;
 
void zeige(Wert w) {
    if (w.art == ZAHL) printf("Zahl %d\n", w.wert.zahl);
    else printf("Text %s\n", w.wert.text);
}
 
int main(void) {
    Wert a = { ZAHL, .wert.zahl = 42 };
    Wert b = { TEXT, .wert.text = "Hallo" };
    zeige(a); zeige(b);
    printf("%zu\n", sizeof(Wert));
    return 0;
}

Ausgabe:

Zahl 42
Text Hallo
16

Bitfelder und Ausrichtung

Der Compiler fügt zwischen Feldern oft Füllbytes (Padding) ein, damit Felder an passenden Adressen liegen. Die Reihenfolge der Felder beeinflusst die Größe:

#include <stdio.h>
#include <stddef.h>
 
struct Schlecht { char a; int b; char c; };     // a, 3 Füllbytes, b, c, 3 Füllbytes
struct Besser   { int b; char a; char c; };
 
int main(void) {
    printf("%zu %zu\n", sizeof(struct Schlecht), sizeof(struct Besser));
    printf("%zu %zu\n", offsetof(struct Schlecht, b), offsetof(struct Schlecht, c));
    return 0;
}

Ausgabe:

12 8
4 8

Verkettete Liste: Struktur mit Zeiger auf sich selbst

#include <stdio.h>
#include <stdlib.h>
 
typedef struct Knoten {
    int wert;
    struct Knoten *weiter;
} Knoten;
 
Knoten *vorne(Knoten *kopf, int wert) {
    Knoten *k = malloc(sizeof *k);
    if (!k) return kopf;
    k->wert = wert;
    k->weiter = kopf;
    return k;
}
 
int main(void) {
    Knoten *liste = NULL;
    for (int i = 1; i <= 4; i++) liste = vorne(liste, i * 10);
    for (Knoten *k = liste; k; k = k->weiter) printf("%d -> ", k->wert);
    printf("NULL\n");
    while (liste) { Knoten *n = liste->weiter; free(liste); liste = n; }     // aufräumen
    return 0;
}

Ausgabe:

40 -> 30 -> 20 -> 10 -> NULL

Merke

  • struct bündelt Felder; Zugriff mit . bzw. -> bei Zeigern
  • typedef gibt Typen kürzere Namen; Strukturen lassen sich kopieren und per Zeiger übergeben
  • enum für benannte Ganzzahl-Konstanten; union teilt sich einen Speicherplatz
  • Füllbytes machen Strukturen oft größer, als die Felder vermuten lassen
  • Verkettete Strukturen entstehen mit Zeigern auf den eigenen Typ

Übungsaufgabe

Lege eine Struktur Buch (Titel, Autor, Jahr) an, speichere drei Bücher in einem Array und sortiere sie nach Jahr.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Zeiger

Weiter: Dynamischer Speicher

Alle Kapitel: C im Überblick