Namespaces
Variants

Pointer declaration

From cppreference.net

Ein Zeiger ist eine Art von Objekt, das auf eine Funktion oder ein Objekt eines anderen Typs verweist, möglicherweise mit hinzugefügten Qualifizierern. Ein Zeiger kann auch auf nichts verweisen, was durch den speziellen Nullzeigerwert angezeigt wird.

Inhaltsverzeichnis

Syntax

In der Deklarationsgrammatik einer Zeigerdeklaration bezeichnet die type-specifier -Sequenz den gezeigten Typ (der ein Funktions- oder Objekttyp sein kann und unvollständig sein darf), 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 hinweisen würde):

float *p, **pp; // p ist ein Zeiger auf float
                // pp ist ein Zeiger auf einen Zeiger auf float
int (*fp)(int); // fp ist ein Zeiger auf eine Funktion mit Typ int(int)

Die Qualifizierer , die zwischen * und dem Bezeichner (oder einem anderen geschachtelten Deklarator) erscheinen, qualifizieren den Typ des Zeigers, der deklariert wird:

int n;
const int * pc = &n; // pc ist ein nicht-konstanter Zeiger auf einen konstanten int
// *pc = 2; // Fehler: n kann nicht über pc ohne Cast geändert werden
pc = NULL; // OK: pc selbst kann geändert werden
int * const cp = &n; // cp ist ein konstanter Zeiger auf einen nicht-konstanten int
*cp = 2; // OK, n über cp zu ändern
// cp = NULL; // Fehler: cp selbst kann nicht geändert werden
int * const * pcp = &cp; // nicht-konstanter Zeiger auf konstanten Zeiger auf nicht-konstanten int

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

Erklärung

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

Zeiger auf Objekte

Ein Zeiger auf ein Objekt kann mit dem Ergebnis des Adressoperators initialisiert werden, angewendet auf einen Ausdruck vom Objekttyp (der unvollständig sein kann):

int n;
int *np = &n; // Zeiger auf int
int *const *npp = &np; // nicht-konstanter Zeiger auf konstanten Zeiger auf nicht-konstanten int
int a[2];
int (*ap)[2] = &a; // Zeiger auf Array von int
struct S { int n; } s = {1}
int* sp = &s.n; // Zeiger auf den int, der ein Member von s ist

Zeiger können als Operanden für den Dereferenzierungsoperator (unäres * ) erscheinen, der den Lvalue zurückgibt, der das referenzierte Objekt identifiziert:

int n;
int* p = &n; // Zeiger p zeigt auf n
*p = 7; // speichert 7 in n
printf("%d\n", *p); // Lvalue-zu-Rvalue-Konvertierung liest den Wert aus n

Zeiger auf Objekte vom Typ struct und union können ebenfalls als linke Operanden des Memberzugriffs durch Zeiger -Operators -> auftreten.

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

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

Bestimmte Additions-, Subtraktions- , zusammengesetzte Zuweisungs- , Inkrement- und Dekrement- Operatoren sind für Zeiger auf Array-Elemente definiert.

Vergleichsoperatoren sind in einigen Situationen für Zeiger auf Objekte definiert: zwei Zeiger, die dieselbe Adresse repräsentieren, vergleichen gleich, zwei Nullzeigerwerte vergleichen gleich, Zeiger auf Elemente desselben Arrays vergleichen gleich wie die Array-Indizes dieser Elemente, und Zeiger auf Strukturelemente vergleichen in der Reihenfolge der Deklaration dieser Elemente.

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

Zeiger auf Funktionen

Ein Zeiger auf Funktion kann mit der Adresse einer Funktion initialisiert werden. Wegen der Funktion-zu-Zeiger -Konvertierung ist der Adressoperator optional:

void f(int);
void (*pf1)(int) = &f;
void (*pf2)(int) = f; // dasselbe wie &f

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

Ein Zeiger auf Funktion kann auf der linken Seite des Funktionsaufrufoperators verwendet werden; dies ruft die gezeigte 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 Funktionsbezeichner für die gezeigte Funktion:

int f();
int (*p)() = f;    // Zeiger p zeigt auf f
(*p)(); // Funktion f wird über den Funktionsbezeichner aufgerufen
p();    // Funktion f wird direkt über den Zeiger aufgerufen

Gleichheitsvergleichsoperatoren sind für Zeiger auf Funktionen definiert (sie vergleichen gleich, wenn sie auf dieselbe Funktion zeigen).

Da Kompatibilität von Funktionstypen Top-Level-Qualifizierer der Funktionsparameter ignoriert, sind Zeiger auf Funktionen, 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

Zeiger auf void

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

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

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 benutzerdefinierten Callback, der zwei const void * Argumente akzeptiert. pthread_create erwartet einen benutzerdefinierten Callback, der void * akzeptiert und zurückgibt. In allen Fällen ist es die Verantwortung des Aufrufers, den Zeiger vor der Verwendung in den korrekten Typ zu konvertieren.

Null-Zeiger

Zeiger jedes Typs haben einen speziellen Wert, der als null pointer value dieses Typs bekannt ist. Ein Zeiger mit dem Wert null zeigt nicht auf ein Objekt oder eine Funktion (das Dereferenzieren eines null-Zeigers ist undefiniertes Verhalten) und vergleicht gleich mit allen Zeigern desselben Typs, deren Wert ebenfalls null ist.

Um einen Zeiger auf null zu initialisieren oder den Nullwert einem bestehenden Zeiger zuzuweisen, kann eine Nullzeiger-Konstante ( NULL , oder jede andere Integer-Konstante mit dem Wert null) verwendet werden. Statische Initialisierung initialisiert Zeiger ebenfalls auf ihre Nullwerte.

Nullzeiger können das Fehlen eines Objekts anzeigen oder können verwendet werden, um andere Arten von Fehlerzuständen 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 (zum Beispiel, free tut nichts, wenn ein Nullzeiger übergeben wird).

Hinweise

Obwohl jeder Zeiger auf ein Objekt umgewandelt werden kann in einen Zeiger auf ein Objekt eines anderen Typs, ist das Dereferenzieren eines Zeigers auf einen Typ, der sich vom deklarierten Typ des Objekts unterscheidet, fast immer undefiniertes Verhalten. Siehe strict aliasing für Details.

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

(since C99)

Lvalue-Ausdrücke vom Array-Typ erfahren in den meisten Kontexten eine implizite Konvertierung zum Zeiger auf das erste Element des Arrays. Weitere Details finden Sie unter Array .

char *str = "abc"; // "abc" ist ein char[4]-Array, str ist ein Zeiger auf 'a'

Zeiger auf char werden oft verwendet, um Strings darzustellen . Um einen gültigen Byte-String zu repräsentieren, 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 geben, der größer oder gleich dem Index des durch den Zeiger referenzierten Elements ist.

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 Pointer-Deklaratoren (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

Siehe auch

C++ Dokumentation für Pointer-Deklaration