Namensräume
Varianten
Aktionen

Zeigerdeklaration

Von cppreference.com
< c‎ | Sprache

Ein Zeiger ist ein Objekttyp, der auf eine Funktion oder ein Objekt eines anderen Typs verweist, möglicherweise unter Hinzufügung von Qualifizierern. Ein Zeiger kann auch auf nichts verweisen, was durch den speziellen Nullzeigerwert angezeigt wird.

Inhalt

[bearbeiten] Syntax

In der Deklarationsgrammatik einer Zeigerdeklaration bezeichnet die type-specifier Sequenz den referenzierten Typ (der ein Funktions- oder Objekttyp sein kann und unvollständig sein kann), und der declarator hat die Form

* attr-spec-seq (optional) qualifiers (optional) declarator

wobei declarator der Bezeichner sein kann, der den zu deklarierenden Zeiger benennt, einschließlich eines weiteren Zeigerdeklarators (was auf einen Zeiger auf einen Zeiger hindeuten würde)

float *p, **pp; // p is a pointer to float
                // pp is a pointer to a pointer to float
int (*fp)(int); // fp is a pointer to function with type int(int)

Die qualifiers, die zwischen * und dem Bezeichner (oder einem anderen verschachtelten Deklarator) erscheinen, qualifizieren den Typ des zu deklarierenden Zeigers

int n;
const int * pc = &n; // pc is a non-const pointer to a const int
// *pc = 2; // Error: n cannot be changed through pc without a cast
pc = NULL; // OK: pc itself can be changed
 
int * const cp = &n; // cp is a const pointer to a non-const int
*cp = 2; // OK to change n through cp
// cp = NULL; // Error: cp itself cannot be changed
 
int * const * pcp = &cp; // non-const pointer to const pointer to non-const int

Die attr-spec-seq(C23) ist eine optionale Liste von Attributen, die auf den deklarierten Zeiger angewendet werden.

[bearbeiten] Erklärung

Zeiger werden für Indirektion verwendet, eine allgegenwärtige Programmiertechnik; sie können verwendet werden, um Referenzsemantik zu implementieren (Pass-by-reference), auf Objekte mit dynamischer Speicherdauer zuzugreifen, "optionale" Typen zu implementieren (unter Verwendung des Nullzeigerwerts), Aggregationsbeziehungen zwischen Strukturen zu implementieren, Callbacks (unter Verwendung von Funktionszeigern), generische Schnittstellen (unter Verwendung von Zeigern auf void) und vieles mehr.

[bearbeiten] Zeiger auf Objekte

Ein Zeiger auf ein Objekt kann mit dem Ergebnis des Adressoperator (address-of operator) initialisiert werden, der auf einen Ausdruck vom Objekttyp (der unvollständig sein kann) angewendet wird

int n;
int *np = &n; // pointer to int
int *const *npp = &np; // non-const pointer to const pointer to non-const int
 
int a[2];
int (*ap)[2] = &a; // pointer to array of int
 
struct S { int n; } s = {1}
int* sp = &s.n; // pointer to the int that is a member of s

Zeiger können als Operanden des Indirektionsoperators (unäres *) erscheinen, der das lvalue zurückgibt, das das referenzierte Objekt identifiziert

int n;
int* p = &n; // pointer p is pointing to n
*p = 7; // stores 7 in n
printf("%d\n", *p); // lvalue-to-rvalue conversion reads the value from n

Zeiger auf Objekte vom Typ struct und union können auch als linke Operanden des Member-Access-Operators über Zeiger -> erscheinen.

Aufgrund der impliziten Konvertierung von Arrays zu Zeigern kann ein Zeiger auf das erste Element eines Arrays mit einem Ausdruck vom Array-Typ initialisiert werden

int a[2];
int *p = a; // pointer to a[0]
 
int b[3][3];
int (*row)[3] = b; // pointer to b[0]

Bestimmte Additions-, Subtraktions-, kombinierte Zuweisungs-, Inkrement- und Dekrementoperatoren sind für Zeiger auf Array-Elemente definiert.

Vergleichsoperatoren sind für Zeiger auf Objekte in einigen Situationen definiert: Zwei Zeiger, die dieselbe Adresse darstellen, vergleichen sich als gleich, zwei Nullzeigerwerte vergleichen sich als gleich, Zeiger auf Elemente desselben Arrays vergleichen sich genauso wie die Array-Indizes dieser Elemente, und Zeiger auf Strukturmitglieder vergleichen sich in der Reihenfolge der Deklaration dieser Mitglieder.

Viele Implementierungen bieten auch eine strikte totale Ordnung von Zeigern beliebigen Ursprungs, z. B. wenn sie als Adressen innerhalb eines kontinuierlichen ("flachen") virtuellen Adressraums implementiert sind.

[bearbeiten] Zeiger auf Funktionen

Ein Funktionszeiger kann mit der Adresse einer Funktion initialisiert werden. Aufgrund der Konvertierung von Funktionen zu Zeigern ist der Adressoperator optional

void f(int);
void (*pf1)(int) = &f;
void (*pf2)(int) = f; // same as &f

Im Gegensatz zu Funktionen sind Funktionszeiger Objekte und können daher in Arrays gespeichert, kopiert, zugewiesen, als Argumente an andere Funktionen übergeben usw. werden.

Ein Funktionszeiger kann auf der linken Seite des Funktionsaufrufoperators verwendet werden; dies ruft die referenzierte Funktion auf

#include <stdio.h>
 
int f(int n)
{
    printf("%d\n", n);
    return n * n;
}
 
int main(void)
{
    int (*p)(int) = f;
    int x = p(7);
}

Das Dereferenzieren eines Funktionszeigers ergibt den Funktionsdeskriptor für die referenzierte Funktion

int f();
int (*p)() = f;    // pointer p is pointing to f
(*p)(); // function f invoked through the function designator
p();    // function f invoked directly through the pointer

Gleichheitsvergleichsoperatoren sind für Funktionszeiger definiert (sie vergleichen sich als gleich, wenn sie auf dieselbe Funktion zeigen).

Da die Kompatibilität von Funktionstypen Top-Level-Qualifizierer von Funktionsparametern ignoriert, sind Funktionszeiger, deren Parameter sich nur in ihren Top-Level-Qualifizierern unterscheiden, austauschbar

int f(int), fc(const int);
int (*pc)(const int) = f; // OK
int (*p)(int) = fc;       // OK
pc = p;                   // OK

[bearbeiten] Zeiger auf void

Zeiger auf Objekte beliebigen Typs können implizit in Zeiger auf void (optional const oder volatile-qualifiziert) konvertiert werden, und umgekehrt

int n=1, *p=&n;
void* pv = p; // int* to void*
int* p2 = pv; // void* to int*
printf("%d\n", *p2); // prints 1

Zeiger auf void werden verwendet, um Objekte unbekannten Typs zu übergeben, was in generischen Schnittstellen üblich ist: malloc gibt void* zurück, qsort erwartet einen vom Benutzer bereitgestellten Callback, der zwei const void* Argumente akzeptiert. pthread_create erwartet einen vom Benutzer bereitgestellten Callback, der void* Argumente akzeptiert und zurückgibt. In allen Fällen liegt es in der Verantwortung des Aufrufers, den Zeiger vor der Verwendung in den richtigen Typ zu konvertieren.

[bearbeiten] Nullzeiger

Zeiger jedes Typs haben einen speziellen Wert, der als Nullzeigerwert dieses Typs bekannt ist. Ein Zeiger, dessen Wert null ist, verweist nicht auf ein Objekt oder eine Funktion (das Dereferenzieren eines Nullzeigers ist undefiniertes Verhalten) und vergleicht sich mit allen Zeigern desselben Typs, deren Wert ebenfalls null ist.

Um einen Zeiger auf null zu initialisieren oder den Nullwert einem vorhandenen Zeiger zuzuweisen, kann eine Nullzeigerkonstante (NULL oder eine beliebige andere ganzzahlige Konstante mit dem Wert null) verwendet werden. Statische Initialisierung initialisiert Zeiger ebenfalls mit ihren Nullwerten.

Nullzeiger können die Abwesenheit eines Objekts anzeigen oder verwendet werden, um andere Arten von Fehlerbedingungen anzuzeigen. Im Allgemeinen muss eine Funktion, die ein Zeigerargument empfängt, fast immer prüfen, ob der Wert null ist, und diesen Fall anders behandeln (z. B. tut free nichts, wenn ein Nullzeiger übergeben wird).

[bearbeiten] Hinweise

Obwohl jeder Zeiger auf ein Objekt in einen Zeiger auf ein Objekt eines anderen Typs umgewandelt werden kann, ist das Dereferenzieren eines Zeigers auf einen anderen Typ als den deklarierten Typ des Objekts fast immer undefiniertes Verhalten. Siehe Strict Aliasing für Details.

Es ist möglich, einer Funktion, die über Zeiger auf Objekte zugreift, anzuzeigen, dass diese Zeiger nicht aliasen. Siehe restrict für Details.

(seit C99)

lvalue-Ausdrücke vom Array-Typ werden, wenn sie in den meisten Kontexten verwendet werden, einer impliziten Konvertierung zu einem Zeiger auf das erste Element des Arrays unterzogen. Siehe Array für Details.

char *str = "abc"; // "abc" is a char[4] array, str is a pointer to 'a'

Zeiger auf char werden oft verwendet, um Zeichenketten darzustellen. Um eine gültige Byte-Zeichenkette darzustellen, muss ein Zeiger auf ein char zeigen, das ein Element eines char-Arrays ist, und es muss ein char mit dem Wert null an einem Index vorhanden sein, der größer oder gleich dem Index des vom Zeiger referenzierten Elements ist.

[bearbeiten] Referenzen

  • C23-Standard (ISO/IEC 9899:2024)
  • 6.7.6.1 Zeigerdeklaratoren (S. TBD)
  • C17-Standard (ISO/IEC 9899:2018)
  • 6.7.6.1 Zeigerdeklaratoren (S. 93-94)
  • C11-Standard (ISO/IEC 9899:2011)
  • 6.7.6.1 Zeigerdeklaratoren (S. 130)
  • C99-Standard (ISO/IEC 9899:1999)
  • 6.7.5.1 Zeigerdeklaratoren (S. 115-116)
  • C89/C90-Standard (ISO/IEC 9899:1990)
  • 3.5.4.1 Zeigerdeklaratoren

[bearbeiten] Siehe auch

C++ Dokumentation für Zeigerdeklaration