Eine Struktur (in anderen Sprachen oft als record, Verbund, Datensatz bezeichnet) ist als Aggregat ein komplexer Datentyp, der aus einer Anreihung von einer oder mehreren Komponenten (members) oft auch verschiedenen Typs besteht, um diese so zusammengefassten Daten dann als Einheit behandeln zu können.
Eine Struktur wird definiert mit dem Schlüsselwort struct, gefolgt von einem Block mit den Deklarationen der Komponenten. Beispiel:
struct person { int num; char name[64]; char email[64]; char telefon[32]; char level; };
Hier werden mit dem Schlüsselwort struct und dem Bezeichner person, dem sog. Etikett (structure tag), zusammengehörige Daten in einer Struktur zusammen-gefasst: Es wird ein neuer, benutzerdefinierter Datentyp namens struct person geschaffen.
Die Namen der in dem Strukturblock deklarierten Komponenten befinden sich in einem eigenen Namensraum und können nicht mit anderen (äußeren) Namen oder Namen von Komponenten in anderen Strukturen kollidieren. Es wird hierbei auch noch kein Speicherplatz reserviert, sondern lediglich der Typ bekannt gemacht, seine Form beschrieben, also ein Bauplan zur Beschaffenheit dieses Typs und seiner Struktur vorgelegt.
Speicherplatz kann reserviert und somit Variablen dieses Typs erzeugt werden, indem man zwischen der beendenden geschweiften Klammer des Strukturblocks und dem abschließenden Semikolon eine Liste von Variablennamen einfügt. Übersichtlicher ist wohl aber meist, die Beschreibung der Form von der Speicherplatzreservierung zu trennen. Variablen dieses Typs werden dann z.B. so vereinbart:
struct person hugo, pp; /* 1 Variable und ein Zeiger */
Man kann natürlich auch gleich ganze Arrays von diesem neuen Typ erzeugen:
struct person ap[100]; /* Array von 100 struct person */
Der Compiler sorgt dafür, dass die Komponenten der Strukturen in der Reihenfolge ihrer Deklaration mit der korrekten Ausrichtung angelegt werden und dass die Gesamtheit der Struktur so gestaltet ist, dass sich mehrere davon als Elemente eines Arrays anreihen lassen. Je nach Gestalt der Struktur, abhängig von Maschinenarchitektur und Compiler können dabei zwischen den Komponenten und am Ende der Struktur auch Lücken entstehen, so dass die Gesamtgröße einer Struktur (zu ermitteln mithilfe des sizeof-Operators) unter Umständen größer ist als die Summe der Größen ihrer Komponenten. Der Speicherinhalt der so entstandenen Lücken bleibt dabei undefiniert.
Auf die Komponenten zugegriffen wird direkt mit dem .-Operator:
hugo.num = 4711; /* Schreibzugriff auf Komp. num von hugo */
Der indirekte Zugriff (über Zeiger) geschieht mithilfe des ->-Operators:
pp = &hugo; pp->level = 12; /* Zugriff auf Komponente level von hugo */
Oder entsprechend bei Zugriff auf ein Element eines Arrays:
ap[5].num = 4712; printf( „%d“, (ap+5)->num );
Strukturen können selbst auch wieder (andere) Strukturen als Komponenten enthalten. Erlaubt ist auch die Definition von Strukturen innerhalb des Strukturdefinitionsblocks – dieser Typ ist dann allerdings auch im Sichtbarkeitsbereich der ein-bettenden Struktur bekannt, daher sollte dies besser vermieden werden. Wenn die Definition des Strukturblocks nicht erfolgt oder noch nicht abgeschlossen ist, spricht man von einem unvollständigen (incomplete) Datentyp. Davon lassen sich dann zwar keine Variablen erzeugen – Speicherplatzverbrauch und Gestalt sind ja noch unbekannt, es lassen sich aber schon Zeiger auf diesen Typ erstellen. Auf diese Weise können Strukturen Zeiger auf ihren eigenen Typ enthalten, eine Konstruktion, die oft zur Erzeugung von verketteten Listen verwandt wird. Beispiel:
Strukturen können (an Variablen gleichen Typs) zugewiesen werden, als Argumente an Funktionen übergeben und als Rückgabetyp von Funktionen deklariert werden. Die Zuweisung ist dabei als komponentenweise Kopie definiert. Bei größeren Strukturen empfiehlt sich bei den beiden letzteren Aktionen allerdings, lieber mit Zeigern zu arbeiten, da sonst intern immer über temporäre Kopien gearbeitet wird, was sowohl zeit- wie speicherplatzaufwendig wäre. Strukturvariablen lassen sich ähnlich wie Arrays mit Initialisierungslisten initialisieren.
Syntaktisch ähnlich einer Struktur ist die Variante oder Union (union), mit dem Unterschied, dass die verschiedenen Komponenten nicht nacheinander angeordnet sind, sondern alle an der gleichen Adresse liegend abgebildet werden. Vereinbart werden sie mit dem Schlüsselwort union, gefolgt von einem optionalen Etikett, gefolgt von einem Definitionsblock mit den Definitionen der Komponenten, gefolgt von einem Semikolon. Sie werden benutzt, um Daten unterschiedlichen Typs am gleichen Speicherplatz unterbringen zu können (natürlich immer nur einen Typ zur gleichen Zeit!), oder um den Speicherplatz anders zu interpretieren
Der Compiler sorgt dafür, dass die Größe der Union, ihre Ausrichtung inklusive etwaiger Auffüllung den Anforderungen der Maschine entsprechen, daher ist die Größe einer Unionsvariablen immer mindestens so groß wie die Größe ihrer größten Komponente.
Bitfelder
Als mögliche Komponenten von struct oder union können Bitfelder vereinbart werden. Ein Bitfeld dient zur Zusammenfassung von Information auf kleinstem Raum (nur erlaubt innerhalb struct oder union). Es gibt drei Formen von Bitfeldern:
Stand: 08.12.2025
Es ist für uns eine Selbstverständlichkeit, dass wir verantwortungsvoll mit Ihren personenbezogenen Daten umgehen. Sofern wir personenbezogene Daten von Ihnen erheben, verarbeiten wir diese unter Beachtung der geltenden Datenschutzvorschriften. Detaillierte Informationen finden Sie in unserer Datenschutzerklärung.
Einwilligung in die Verwendung von Daten zu Werbezwecken
Ich bin damit einverstanden, dass die Vogel Communications Group GmbH & Co. KG, Max-Planckstr. 7-9, 97082 Würzburg einschließlich aller mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen (im weiteren: Vogel Communications Group) meine E-Mail-Adresse für die Zusendung von redaktionellen Newslettern nutzt. Auflistungen der jeweils zugehörigen Unternehmen können hier abgerufen werden.
Der Newsletterinhalt erstreckt sich dabei auf Produkte und Dienstleistungen aller zuvor genannten Unternehmen, darunter beispielsweise Fachzeitschriften und Fachbücher, Veranstaltungen und Messen sowie veranstaltungsbezogene Produkte und Dienstleistungen, Print- und Digital-Mediaangebote und Services wie weitere (redaktionelle) Newsletter, Gewinnspiele, Lead-Kampagnen, Marktforschung im Online- und Offline-Bereich, fachspezifische Webportale und E-Learning-Angebote. Wenn auch meine persönliche Telefonnummer erhoben wurde, darf diese für die Unterbreitung von Angeboten der vorgenannten Produkte und Dienstleistungen der vorgenannten Unternehmen und Marktforschung genutzt werden.
Meine Einwilligung umfasst zudem die Verarbeitung meiner E-Mail-Adresse und Telefonnummer für den Datenabgleich zu Marketingzwecken mit ausgewählten Werbepartnern wie z.B. LinkedIN, Google und Meta. Hierfür darf die Vogel Communications Group die genannten Daten gehasht an Werbepartner übermitteln, die diese Daten dann nutzen, um feststellen zu können, ob ich ebenfalls Mitglied auf den besagten Werbepartnerportalen bin. Die Vogel Communications Group nutzt diese Funktion zu Zwecken des Retargeting (Upselling, Crossselling und Kundenbindung), der Generierung von sog. Lookalike Audiences zur Neukundengewinnung und als Ausschlussgrundlage für laufende Werbekampagnen. Weitere Informationen kann ich dem Abschnitt „Datenabgleich zu Marketingzwecken“ in der Datenschutzerklärung entnehmen.
Falls ich im Internet auf Portalen der Vogel Communications Group einschließlich deren mit ihr im Sinne der §§ 15 ff. AktG verbundenen Unternehmen geschützte Inhalte abrufe, muss ich mich mit weiteren Daten für den Zugang zu diesen Inhalten registrieren. Im Gegenzug für diesen gebührenlosen Zugang zu redaktionellen Inhalten dürfen meine Daten im Sinne dieser Einwilligung für die hier genannten Zwecke verwendet werden. Dies gilt nicht für den Datenabgleich zu Marketingzwecken.
Recht auf Widerruf
Mir ist bewusst, dass ich diese Einwilligung jederzeit für die Zukunft widerrufen kann. Durch meinen Widerruf wird die Rechtmäßigkeit der aufgrund meiner Einwilligung bis zum Widerruf erfolgten Verarbeitung nicht berührt. Um meinen Widerruf zu erklären, kann ich als eine Möglichkeit das unter https://contact.vogel.de abrufbare Kontaktformular nutzen. Sofern ich einzelne von mir abonnierte Newsletter nicht mehr erhalten möchte, kann ich darüber hinaus auch den am Ende eines Newsletters eingebundenen Abmeldelink anklicken. Weitere Informationen zu meinem Widerrufsrecht und dessen Ausübung sowie zu den Folgen meines Widerrufs finde ich in der Datenschutzerklärung, Abschnitt Redaktionelle Newsletter.
normale Bitfelder (plain bitfields ) – deklariert als int
vorzeichenbehaftete (signed bitfields ) – deklariert als signed int
nicht vorzeichenbehaftete (unsigned bitfields ) – deklariert als unsigned int
Ein Bitfeld belegt eine gewisse, aufeinander folgende Anzahl von Bit in einem Integer. Es ist nicht möglich, eine größere Anzahl von Bit zu vereinbaren, als in der Speichergröße des Typs int Platz haben. Es darf auch unbenannte Bitfelder geben, auf die man dann natürlich nicht zugreifen kann, dies dient meist der Abbildung der Belegung bestimmter Register oder Ports. Hier die Syntax:
Nach dem Doppelpunkt steht die Anzahl der Bit, die das Feld belegt. Wie der Compiler die Bitfelder anlegt, wie er sie ausrichtet und wie groß er die sie enthaltenden Integraltypen macht, ist völlig implementationsabhängig. Wenn man sie überhaupt je verwenden will, wird empfohlen, sie jedenfalls als unsigned int zu deklarieren.
Aufzählungstypen in C
Aufzählungstypen – Schlüsselwort enum – sind benannte Ganzzahlkonstanten (enumeration constants), deren Vereinbarungssyntax der von Strukturen ähnelt. Im Gegensatz zu mit #define vereinbarten Konstanten, die der C-Präprozessor verarbeitet, werden die enum-Konstanten vom C-Compiler selbst bearbeitet. Auf den C-Präprozessor werden wir im nächsten Artikel näher eingehen.
Sie sind kompatibel zum Typ, den der Compiler dafür wählt – einen Typ, aufwärts-kompatibel zum Typ int: Es könnte also auch char oder short sein, aber nicht long, das ist implementationsabhängig – und lassen sich ohne weiteres in diesen überführen und umgekehrt, ohne dass der Compiler prüft, ob der Wert auch im passenden Bereich liegt. Hier einige Beispiele zur Deklaration, bzw. Definition:
Bei aller semantischen Nähe zum Typ int sind enum-Konstanten oft der beste Weg, um mittels benannter Konstanten das Programm übersichtlicher zu machen und „magische“ Zahlen (magic numbers) zu vermeiden, besser oft als die übliche Methode der #define-Makros und daher für diesen Zweck sehr zu empfehlen. Diese Art der Verwendung funktioniert natürlich nur für Ganzzahlkonstanten, die den Wertebereich eines int nicht überschreiten.
Typdefinitionen
Das Schlüsselwort ist typedef. Der Name lässt es zwar vermuten, aber typedef dient nicht zur Definition neuer Datentypen, er erzeugt syntaktisch nur andere Namen (Synonyme, Aliasse) für schon bekannte Typen. Das kann, richtig angewandt, zur erhöhten Lesbarkeit des Quelltextes genutzt werden. Einerseits wird typedef dazu benutzt, komplizierte oder umständliche Deklarationen zu vereinfachen, andererseits kann durch geschickten Einsatz die Portabilität von Programmcode auf unterschiedliche Umgebungen erhöht werden. Der so erzeugte „neue“ Typ ist mit seinem Ursprungstyp voll kompatibel und syntaktisch quasi-identisch. Die Syntax ist:
typedef bekannter-Typ neuer-Typname ;
Ein Beispiel:
typedef int int32; typedef short int16; typedef signed char int8;
Bei einigen Elementen sind bereits die Begriffe "Präprozessor" oder "Bibliothek" erwähnt. Dabei handelt es sich nicht mehr länger um die "C-Grammatik", vielmehr stellen sie eigene, essentielle Bestandteile der Programmiersprache dar. Im nächsten Kapitel werden wir daher näher darauf eingehen, was es mit der dem C-Präprozessor und der Standardbibliothek von C auf sich hat.