Ohne Datentypen funktioniert in C nichts. Elemente wie Arrays und Zeiger bzw Pointer oder Strukturen zählen zu den wichtigsten Bausteinen der Programmiersprache. Zum Abschluss der syntaktischen Elemente von C wollen wir daher auf diese Dateitypen näher eingehen - und auch den Präprozessor zur Code-Vorverarbeitung kurz erklären.
Syntaktische Elemente wie Arrays (bzw Vektoren) oder Pointer (bzw. Zeiger) sind beim Programmieren mit C essentiell.
Vektoren (meist Arrays, deutsch zuweilen auch Felder genannt) sind als Aggregate komplexe Datentypen, die aus einer Anreihung gleicher Elemente bestehen. Diese Elemente werden aufeinander folgend in Richtung aufsteigender Adressen im Speicher abgelegt, sie können einfache oder selbst auch wieder komplexe Datentypen darstellen. Die Adresse des Arrays ist identisch mit der Adresse des Elements mit der Nummer 0, denn in C werden die Elemente beginnend mit 0 durchnumeriert.
Ein Array wird deklariert mit dem Operator [ ], in dem die Dimensionsangabe, d.h. die Anzahl der Elemente steht. Die angegebene Anzahl muss eine vorzeichenlose integrale Konstante sein, eine Anzahl 0 ist nicht erlaubt. Der Bezeichner für ein Array ist fest mit seinem Typ verbunden, stellt aber kein Objekt im Speicher dar.
In Zusammenhang mit dem sizeof-Operator wird die Größe des Arrays als Anzahl von Einheiten des Typs char geliefert. Der Compiler sorgt für eine korrekte Ausrichtung im Speicher. Ein Beispiel:
int iv[10]; /* Ein Array iv von zehn Elementen vom Typ int */
Mehr Dimensionen sind möglich. Im Gegensatz zu einigen anderen Programmiersprachen gibt es in C jedoch keine echten mehrdimensionalen Arrays. Essentiell gibt es hier nur Arrays von Arrays (mit beliebiger – vielleicht von der Implementation oder verfügbarem Speicherplatz begrenzter – Anzahl der Dimensionen). Ein Beispiel für die Deklaration eines zweidimensionalen Arrays:
double dvv[5][20]; /* Array von 5 Arrays von je 20 doubles */
Die Ablage im Speicher erfolgt hierbei zeilenweise (bei zwei Dimensionen), d.h. der der rechte Index (der Spaltenindex bei zwei Dimensionen) variiert am schnellsten, wenn die Elemente gemäß ihrer Reihenfolge im Speicher angesprochen werden. Auf die Elemente zugegriffen wird mit dem Operator [ ], in dem nun der Index steht, für ein Array mit n Elementen reicht der erlaubte Indexbereich von 0 bis n-1. Ein Beispiel:
abc = iv[3]; /* Zuweisung des 4. Elements von iv an abc */ xyz = dvv[i][j]; /* Zuweisung aus dvv, i und j sind Laufvariablen */
Die Schrittweite des Index ist so bemessen, dass immer das jeweils nächste – oder vorherige – Element erfasst wird. Es ist erlaubt, negative Indizes oder solche größer als n-1 zu verwenden. Was dann jedoch beim Zugriff außerhalb des erlaubten Bereichs des so indizierten Arrays passiert, ist implementationsabhängig (und generell nicht zu empfehlen!).
Zeiger bzw. Pointer
Zeiger, auch englisch Pointer genannt, sind komplexe Datentypen. Sie beinhalten sowohl die Adresse des Typs, auf den sie zeigen, als auch die Eigenschaften eben dieses Typs, insbesondere, wichtig für die mit ihnen verwendete Adressarithmetik, seine Speichergröße. Zeiger werden deklariert mittels des Operators *.
int *ip; /* ip ist ein Zeiger auf Typ int */
Pointer müssen initialisiert werden, bevor sie zum Zugriff auf die mit ihnen bezeigten Objekte benutzt werden können:
ip = &abc; /* ip wird auf die Adresse von abc gesetzt */
Zeiger können nur mit Zeigern gleichen Typs initialisiert werden, oder mit Zeigern auf void, (also auf nichts bestimmtes). Zum Initialisieren von Zeigern wird meist der Adressoperator & verwendet, der einen Zeiger auf seinen Operanden erzeugt. In einem Zuweisungszusammenhang gilt der Name eines Arrays als Zeiger auf das erste Element (das mit dem Index 0) des Arrays, d.h. wenn wie im Beispiel weiter oben iv ein Array vom Typ int ist:
ip = iv; /* gleichwertig mit ip = &iv[0] */
Der Zugriff auf das vom Zeiger referenzierte Objekt, (die sog. Dereferenzierung), geschieht mittels des Operators *:
if(*ip) /* Test des Inhalts der Variable, auf die ip zeigt */
Wenn ip auf iv zeigt, dann ist *ip identisch mit iv[0], man hätte auch schreiben können ip[0] oder *iv. Hier zeigt sich nun der grundlegende Zusammenhang zwischen Array- und Zeigernotation in der Sprache C. Es gilt: a[n] ist identisch mit *(a+n)
Zu beachten ist hierbei lediglich, dass Arraynamen in einem Zugriffskontext feste Zeiger sind (Adressen), sie stellen kein Objekt im Speicher dar und können somit auch nicht verändert werden, wohingegen Zeigervariablen Objekte sind. Ein Pointer kann inkrementiert und dekrementiert werden, d.h. integrale Größen können addiert oder subtrahiert werden. Der Zeiger zeigt dann auf ein dem Vielfachen seiner Schrittweite entsprechend entferntes Objekt.
Zeiger gleichen Typs dürfen miteinander verglichen oder voneinander subtrahiert werden. Wenn sie in den gleichen Bereich (z.B. ein entsprechend deklariertes Array) zeigen, ergibt sich eine integrale Größe, die den Indexabstand der so bezeigten Elemente bedeutet. Wenn das nicht der Fall ist, ist diese Operation nicht sinnvoll. Erlaubt (und häufig angewandt) ist auch das Testen des Wertes eines Zeigers.
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.
Die Zuweisung integraler Werte an einen Zeiger hat die Bedeutung einer Adresse des vom Zeiger bezeigten Typs. Wenn die Bedingungen der Ausrichtung dieses Typs (z.B. ganzzahlige Vielfache einer bestimmten Größe) nicht erfüllt sind oder der Zugriff auf diese Adresse in der entsprechenden Typgröße nicht erlaubt sein sollte, kann dies zu Laufzeitfehlern führen. Der Wert 0 eines Zeigers hat die Bedeutung, dass dieser Zeiger ungültig ist, ein sogenannter Nullzeiger (null pointer). Der Zugriff auf die Adresse 0 ist in einem C-System, gleich ob lesend oder schreibend, allgemein nicht gestattet.
Zeiger auf den Typ void (also auf nichts bestimmtes) dienen als generische Pointer lediglich zur Zwischenspeicherung von Zeigern auf Objekte bestimmten Typs. Man kann sonst nichts sinnvolles mit ihnen anfangen, auch keine Adressberechnungen. Sie dürfen ohne weiteres allen Typen von Zeigern zugewiesen werden und umgekehrt.
Initialisierung von Arrays
Wenn erwünscht, können Arrays durch Angabe einer Initialisierungsliste mit konstanten Werten initialisiert werden, hierbei darf dann die Dimensionsangabe fehlen, man spricht dann von einem unvollständigen Arraytyp (incomplete array type), und der Compiler errechnet sie selbsttätig aus der Anzahl der angegebenen Elemente der Liste (und komplettiert damit den Typ!):
int magic[] = {4711, 815, 7, 42, 3}; /* magic hat 5 Elem. */
Ist die Dimension angegeben, werden die Elemente beginnend mit dem Index 0 mit den Werten aus der Liste initialisiert und der Rest, so vorhanden, wird auf 0 gesetzt:
long prim[100] = {2, 3, 5, 7, 11}; /* ab Index 5 alles 0 */
Die Dimensionsangabe darf nicht geringer als die Anzahl der Elemente der Initialisierungsliste sein:
Hier – oder im obigen Beispiel – hätte man die inneren geschweiften Klammern auch weglassen können, denn der Compiler füllt bei jeder Dimension beginnend mit dem Index 0 auf, wobei der rechte Index am schnellsten variiert. Oft besteht jedoch die Gefahr der Mehrdeutigkeit. Hilfreiche Compiler warnen hier!
Bei der Initialisierung von char-Arrays mit konstanten Zeichenketten darf man die geschweiften Klammern weglassen. Also:
char mword[] = „Abrakadabra“; /* mword hat 12 Elemente */
Auch hier zählt der Compiler wieder die Anzahl der Elemente ab (inklusive der terminierenden Null) und dimensioniert das Array selbsttätig. Eine eventuell vorhandene Dimensionsangabe muss mindestens der erforderlichen Anzahl entsprechen. Überzählige Elemente werden auch hier mit 0 aufgefüllt:
char name[64] = „Heiner Mueller“; /* Ab Index 14 alles 0 */
Man beachte folgenden wichtigen Unterschied:
char xword[] = „Hokuspokus“; /* xword hat 11 Elemente */ char xptr = „Hokuspokus“; /* xptr zeigt auf Zeichenkettenkonstante */
Im ersten Fall handelt es sich um ein Array namens xword von 11 Elementen in Form eines C-Strings (mit terminierender Null), im zweiten Fall haben wir mit xptr einen Zeiger, der auf einen an anderer Stelle (möglicherweise im Nur-Lesebereich) gespeicherten C-String (jetzt als namenloses Array vom Typ char) zeigt.