Namespaces
Variants

Pointer declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Deklariert eine Variable eines Zeiger- oder Zeiger-auf-Member-Typs.

Inhaltsverzeichnis

Syntax

Eine Zeigerdeklaration ist jede einfache Deklaration, deren Deklarator die Form hat

* attr  (optional) cv  (optional) declarator (1)
nested-name-specifier * attr  (optional) cv  (optional) declarator (2)
1) Zeigerdeklarator : die Deklaration S * D ; deklariert D als einen Zeiger auf den Typ, der durch die Deklarationsspezifizierer-Sequenz S bestimmt wird.
2) Deklarator für Zeiger auf Mitglied : die Deklaration S C :: * D ; deklariert D als einen Zeiger auf ein nicht-statisches Mitglied von C vom Typ, bestimmt durch die Deklarationsspezifizierer-Sequenz S .
nested-name-specifier - eine Folge von Namen und Bereichsauflösungsoperatoren ::
attr - (since C++11) eine Liste von Attributen
cv - const/volatile-Qualifizierung, die auf den Zeiger angewendet wird, der deklariert wird (nicht auf den gezeigten Typ, dessen Qualifizierungen Teil der Deklarationsspezifizierer-Sequenz sind)
declarator - ein beliebiger Deklarator

Es gibt keine Zeiger auf references und es gibt keine Zeiger auf bit-fields . Typischerweise schließen Erwähnungen von "Zeigern" ohne nähere Spezifikation keine Zeiger auf (nicht-statische) Member ein.

Zeiger

Jeder Wert vom Zeigertyp ist einer der Folgenden:

  • ein Zeiger auf ein Objekt oder eine Funktion (in diesem Fall sagt man, der Zeiger zeigt auf das Objekt oder die Funktion), oder
  • ein Zeiger hinter das Ende eines Objekts , oder
  • der Nullzeigerwert für diesen Typ, oder
  • ein ungültiger Zeigerwert .

Ein Zeiger, der auf ein Objekt zeigt, repräsentiert die Adresse des ersten Bytes im Speicher, das vom Objekt belegt wird. Ein Zeiger hinter dem Ende eines Objekts repräsentiert die Adresse des ersten Bytes im Speicher nach dem Ende des vom Objekt belegten Speicherbereichs.

Beachten Sie, dass zwei Zeiger, die dieselbe Adresse repräsentieren, dennoch unterschiedliche Werte haben können.

struct C
{
    int x, y;
} c;
int* px = &c.x;   // Wert von px ist "Zeiger auf c.x"
int* pxe= px + 1; // Wert von pxe ist "Zeiger hinter das Ende von c.x"
int* py = &c.y;   // Wert von py ist "Zeiger auf c.y"
assert(pxe == py); // == prüft, ob zwei Zeiger dieselbe Adresse repräsentieren
                   // kann ausgelöst werden oder nicht
*pxe = 1; // undefiniertes Verhalten, selbst wenn die Assertion nicht ausgelöst wird

Die Indirektion über einen ungültigen Zeigerwert und das Übergeben eines ungültigen Zeigerwerts an eine Freigabefunktion haben undefiniertes Verhalten. Jede andere Verwendung eines ungültigen Zeigerwerts hat implementierungsdefiniertes Verhalten. Einige Implementierungen könnten definieren, dass das Kopieren eines ungültigen Zeigerwerts einen systemgenerierten Laufzeitfehler verursacht.

Zeiger auf Objekte

Ein Zeiger auf ein Objekt kann mit dem Rückgabewert des Adressoperators initialisiert werden, angewendet auf jeden Ausdruck vom Objekttyp, einschließlich eines anderen Zeigertyps:

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 s = {1};
int* sp = &s.n;        // Zeiger auf den int, der ein Member von s ist

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

int n;
int* p = &n;     // Zeiger auf n
int& r = *p;     // Referenz ist an den Lvalue-Ausdruck gebunden, der n identifiziert
r = 7;           // speichert den int-Wert 7 in n
std::cout << *p; // Lvalue-zu-Rvalue implizite Konvertierung liest den Wert aus n

Zeiger auf Klassenobjekte können auch als linke Operanden der Elementzugriffsoperatoren operator-> und operator->* 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* p1 = a; // Zeiger auf das erste Element a[0] (ein int) des Arrays a
int b[6][3][8];
int (*p2)[3][8] = b; // Zeiger auf das erste Element b[0] des Arrays b,
                     // welches ein Array von 3 Arrays mit 8 ints ist

Aufgrund der abgeleitet-zu-basis impliziten Konvertierung für Zeiger kann ein Zeiger auf eine Basisklasse mit der Adresse einer abgeleiteten Klasse initialisiert werden:

struct Base {};
struct Derived : Base {};
Derived d;
Base* p = &d;
**Erklärung:** - Die HTML-Tags und Attribute wurden unverändert beibehalten - Der C++ Code innerhalb der `
` Tags wurde nicht übersetzt
- C++ spezifische Begriffe wie `struct`, `Base`, `Derived` etc. wurden nicht übersetzt
- Die Formatierung und Struktur des Originalcodes bleibt vollständig erhalten
Der Code demonstriert eine einfache Vererbung in C++, bei der eine `Derived`-Klasse von einer `Base`-Klasse erbt und ein Zeiger auf die Basisklasse auf ein Objekt der abgeleiteten Klasse zeigt.

Wenn Derived polymorphe ist, kann ein solcher Zeiger verwendet werden, um virtuelle Funktionsaufrufe durchzuführen.

Bestimmte Additions-, Subtraktions- , Inkrement- und Dekrement- Operatoren sind für Zeiger auf Array-Elemente definiert: Solche Zeiger erfüllen die LegacyRandomAccessIterator Anforderungen und ermöglichen es den C++- Algorithmusbibliotheken , mit Roh-Arrays zu arbeiten.

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 den Array-Indizes dieser Elemente, und Zeiger auf nicht-statische Datenelemente mit demselben Member-Zugriff vergleichen in der Reihenfolge der Deklaration dieser Member.

Viele Implementierungen bieten auch eine strikte Totalordnung von Zeigern beliebiger Herkunft, z.B. wenn sie als Adressen innerhalb eines kontinuierlichen virtuellen Adressraums implementiert sind. Jene Implementierungen, die dies nicht tun (z.B. wenn nicht alle Bits des Zeigers Teil einer Speicheradresse sind und für Vergleiche ignoriert werden müssen, oder eine zusätzliche Berechnung erforderlich ist oder Zeiger und Ganzzahl keine 1-zu-1-Beziehung haben), bieten eine Spezialisierung von std::less für Zeiger an, die diese Garantie bietet. Dies ermöglicht die Verwendung aller Zeiger beliebiger Herkunft als Schlüssel in standardmäßigen assoziativen Containern wie std::set oder std::map .

Zeiger auf void

Zeiger auf Objekte beliebigen Typs können implizit konvertiert werden in Zeiger auf (möglicherweise cv-qualifiziertes ) void ; der Zeigerwert bleibt unverändert. Die umgekehrte Konvertierung, die static_cast oder explizite Konvertierung erfordert, liefert den ursprünglichen Zeigerwert:

int n = 1;
int* p1 = &n;
void* pv = p1;
int* p2 = static_cast<int*>(pv);
std::cout << *p2 << '\n'; // gibt 1 aus

Wenn der ursprüngliche Zeiger auf ein Basisklassen-Subobjekt innerhalb eines Objekts eines polymorphen Typs zeigt, dynamic_cast kann verwendet werden, um einen void * zu erhalten, der auf das vollständige Objekt des am meisten abgeleiteten Typs zeigt.

Zeiger auf void haben die gleiche Größe, Darstellung und Ausrichtung wie Zeiger auf char .

Zeiger auf void werden verwendet, um Objekte unbekannten Typs zu übergeben, was in C-Schnittstellen üblich ist: std::malloc gibt void * zurück, std::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 umzuwandeln.

Zeiger auf Funktionen

Ein Zeiger auf Funktion kann mit der Adresse einer Nicht-Mitgliedsfunktion oder einer statischen Mitgliedsfunktion initialisiert werden. Wegen der Funktion-zu-Zeiger impliziten Konvertierung ist der Address-of-Operator optional:

void f(int);
void (*p1)(int) = &f;
void (*p2)(int) = f; // identisch mit &f

Im Gegensatz zu Funktionen oder Referenzen auf Funktionen sind Zeiger auf Funktionen Objekte und können daher in Arrays gespeichert, kopiert, zugewiesen usw. werden.

void (a[10])(int);  // Fehler: Array von Funktionen
void (&a[10])(int); // Fehler: Array von Referenzen
void (*a[10])(int); // OK: Array von Funktionszeigern

Hinweis: Deklarationen mit Zeigern auf Funktionen können oft durch Typaliase vereinfacht werden:

using F = void(int); // benannter Typalias zur Vereinfachung von Deklarationen
F a[10];  // Fehler: Array von Funktionen
F& a[10]; // Fehler: Array von Referenzen
F* a[10]; // OK: Array von Zeigern auf Funktionen

Ein Zeiger auf Funktion kann als linker Operand des Funktionsaufrufoperators verwendet werden, dies ruft die gezeigte Funktion auf:

int f(int n)
{
    std::cout << n << '\n';
    return n * n;
}
int main()
{
    int (*p)(int) = f;
    int x = p(7);
}

Das Dereferenzieren eines Funktionszeigers ergibt den L-Wert, der die gezeigte Funktion identifiziert:

int f();
int (*p)() = f;  // Zeiger p zeigt auf f
int (&r)() = *p; // der L-Wert, der f identifiziert, ist an eine Referenz gebunden
r();             // Funktion f wird über L-Wert-Referenz aufgerufen
(*p)();          // Funktion f wird über den Funktions-L-Wert aufgerufen
p();             // Funktion f wird direkt über den Zeiger aufgerufen

Ein Zeiger auf eine Funktion kann von einem Überladungssatz initialisiert werden, der Funktionen, Funktions-Template-Spezialisierungen und Funktions-Templates enthalten kann, falls nur eine Überladung zum Typ des Zeigers passt (siehe Adresse einer überladenen Funktion für weitere Details):

template<typename T>
T f(T n) { return n; }
double f(double n) { return n; }
int main()
{
    int (*p)(int) = f; // instanziiert und wählt f<int> aus
}

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

Zeiger auf Member

Zeiger auf Datenelemente

Ein Zeiger auf ein nicht-statisches Mitgliedsobjekt m , das Mitglied der Klasse C ist, kann exakt mit dem Ausdruck & C :: m initialisiert werden. Ausdrücke wie & ( C :: m ) oder & m innerhalb der Mitgliedsfunktion von C bilden keine Zeiger auf Mitglieder.

Ein solcher Zeiger kann als rechter Operand der Zeiger-auf-Member-Zugriffsoperatoren operator. * und operator - > * verwendet werden:

struct C { int m; };
int main()
{
    int C::* p = &C::m;          // Zeiger auf Datenelement m der Klasse C
    C c = {7};
    std::cout << c.*p << '\n';   // gibt 7 aus
    C* cp = &c;
    cp->m = 10;
    std::cout << cp->*p << '\n'; // gibt 10 aus
}

Zeiger auf ein Datenmitglied einer zugänglichen eindeutigen nicht-virtuellen Basisklasse kann implizit konvertiert werden zu einem Zeiger auf dasselbe Datenmitglied einer abgeleiteten Klasse:

struct Base { int m; };
struct Derived : Base {};
int main()
{
    int Base::* bp = &Base::m;
    int Derived::* dp = bp;
    Derived d;
    d.m = 1;
    std::cout << d.*dp << ' ' << d.*bp << '\n'; // gibt 1 1 aus
}

Die Umwandlung in die entgegengesetzte Richtung, von einem Zeiger auf ein Datenelement einer abgeleiteten Klasse zu einem Zeiger auf ein Datenelement einer eindeutigen nicht-virtuellen Basisklasse, ist erlaubt mit static_cast und explicit cast , selbst wenn die Basisklasse dieses Element nicht besitzt (aber die am stärksten abgeleitete Klasse es besitzt, wenn der Zeiger für den Zugriff verwendet wird):

struct Base {};
struct Derived : Base { int m; };
int main()
{
    int Derived::* dp = &Derived::m;
    int Base::* bp = static_cast<int Base::*>(dp);
    Derived d;
    d.m = 7;
    std::cout << d.*bp << '\n'; // okay: gibt 7 aus
    Base b;
    std::cout << b.*bp << '\n'; // undefiniertes Verhalten
}

Der gezeigte Typ eines Zeigers-auf-Mitglied kann selbst ein Zeiger-auf-Mitglied sein: Zeiger auf Mitglieder können mehrstufig sein und können auf jeder Ebene unterschiedlich cv-qualifiziert sein. Gemischte mehrstufige Kombinationen von Zeigern und Zeigern-auf-Mitglieder sind ebenfalls erlaubt:

struct A
{
    int m;
    // konstanter Zeiger auf nicht-konstantes Member
    int A::* const p;
};
int main()
{
    // nicht-konstanter Zeiger auf Datenmember, der ein konstanter Zeiger auf nicht-konstantes Member ist
    int A::* const A::* p1 = &A::p;
    const A a = {1, &A::m};
    std::cout << a.*(a.*p1) << '\n'; // gibt 1 aus
    // regulärer nicht-konstanter Zeiger auf einen konstanten Zeiger-auf-Member
    int A::* const* p2 = &a.p;
    std::cout << a.**p2 << '\n'; // gibt 1 aus
}
**Übersetzungsdetails:** - HTML-Tags und Attribute wurden unverändert belassen - C++ Code innerhalb der `
` und `` Tags wurde nicht übersetzt
- C++ spezifische Begriffe wie "struct", "int", "const", "cout" etc. wurden beibehalten
- Kommentare wurden präzise ins Deutsche übersetzt
- "prints" wurde als "gibt aus" übersetzt (übliche deutsche Programmierterminologie)
- Zeiger-Konzepte wurden fachgerecht übersetzt ("pointer" → "Zeiger", "member" → "Member")

Zeiger auf Memberfunktionen

Ein Zeiger auf eine nicht-statische Member-Funktion f die eine Member-Klasse C ist, kann exakt mit dem Ausdruck & C :: f initialisiert werden. Ausdrücke wie & ( C :: f ) oder & f innerhalb von C 's Member-Funktion bilden keine Zeiger auf Member-Funktionen.

Ein solcher Zeiger kann als rechter Operand der Zeiger-auf-Member-Zugriffsoperatoren operator. * und operator - > * verwendet werden. Der resultierende Ausdruck kann nur als linker Operand eines Funktionsaufrufoperators verwendet werden:

struct C
{
    void f(int n) { std::cout << n << '\n'; }
};
int main()
{
    void (C::* p)(int) = &C::f; // Zeiger auf Memberfunktion f der Klasse C
    C c;
    (c.*p)(1);                  // gibt 1 aus
    C* cp = &c;
    (cp->*p)(2);                // gibt 2 aus
}


Zeiger auf Memberfunktion einer Basisklasse können implizit konvertiert werden zu Zeigern auf dieselbe Memberfunktion einer abgeleiteten Klasse:

struct Base
{
    void f(int n) { std::cout << n << '\n'; }
};
struct Derived : Base {};
int main()
{
    void (Base::* bp)(int) = &Base::f;
    void (Derived::* dp)(int) = bp;
    Derived d;
    (d.*dp)(1);
    (d.*bp)(2);
}

Die Umwandlung in die entgegengesetzte Richtung, von einem Zeiger auf eine Memberfunktion einer abgeleiteten Klasse zu einem Zeiger auf eine Memberfunktion einer eindeutigen nicht-virtuellen Basisklasse, ist erlaubt mit static_cast und explicit cast , selbst wenn die Basisklasse diese Memberfunktion nicht besitzt (aber die am stärksten abgeleitete Klasse besitzt sie, wenn der Zeiger für den Zugriff verwendet wird):

struct Base {};
struct Derived : Base
{
    void f(int n) { std::cout << n << '\n'; }
};
int main()
{
    void (Derived::* dp)(int) = &Derived::f;
    void (Base::* bp)(int) = static_cast<void (Base::*)(int)>(dp);
    Derived d;
    (d.*bp)(1); // in Ordnung: gibt 1 aus
    Base b;
    (b.*bp)(2); // undefiniertes Verhalten
}

Zeiger auf Memberfunktionen können als Callbacks oder als Funktionsobjekte verwendet werden, oft nach Anwendung von std::mem_fn oder std::bind :

#include <algorithm>
#include <cstddef>
#include <functional>
#include <iostream>
#include <string>
int main()
{
    std::vector<std::string> v = {"a", "ab", "abc"};
    std::vector<std::size_t> l;
    transform(v.begin(), v.end(), std::back_inserter(l),
              std::mem_fn(&std::string::size));
    for (std::size_t n : l)
        std::cout << n << ' ';
    std::cout << '\n';
}

Ausgabe:

1 2 3

Null-Zeiger

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

Ein null pointer constant kann verwendet werden, um einen Zeiger auf null zu initialisieren oder den null-Wert einem bestehenden Zeiger zuzuweisen. Es handelt sich um einen der folgenden Werte:

  • Ein Integer-Literal mit dem Wert null.
(seit C++11)

Das Makro NULL kann ebenfalls verwendet werden, es expandiert zu einer implementierungsdefinierten Nullzeigerkonstante.

Zero-Initialisierung und Wert-Initialisierung initialisieren ebenfalls Zeiger auf ihre Nullwerte.

Nullzeiger können verwendet werden, um das Fehlen eines Objekts anzuzeigen (z.B. std::function::target() ), oder als andere Fehlerzustandsindikatoren (z.B. dynamic_cast ). Allgemein muss eine Funktion, die einen Zeigerparameter empfängt, fast immer prüfen, ob der Wert null ist und diesen Fall anders behandeln (beispielsweise tut der delete-Ausdruck nichts, wenn ein Nullzeiger übergeben wird).

Ungültige Zeiger

Ein Zeigerwert p ist gültig im Kontext von einer Auswertung e wenn eine der folgenden Bedingungen erfüllt ist:

  • p ist ein Nullzeigerwert.
  • p ist ein Zeiger auf eine Funktion.
  • p ist ein Zeiger auf oder hinter das Ende eines Objekts o , und e befindet sich in der Speicherdauer des Speicherbereichs für o .

Wenn ein Zeigerwert p in einer Auswertung e verwendet wird und p im Kontext von e nicht gültig ist, dann:

  • Wenn e eine Indirektion oder ein Aufruf einer Freigabefunktion ist, ist das Verhalten undefiniert.
  • Andernfalls ist das Verhalten implementierungsdefiniert.
int* f()
{
    int obj;
    int* local_ptr = new (&obj) int;
    *local_ptr = 1; // OK, die Auswertung „*local_ptr“ befindet sich
                    // in der Speicherdauer von „obj“
    return local_ptr;
}
int* ptr = f();  // die Speicherdauer von „obj“ ist abgelaufen,
                 // daher ist „ptr“ in den folgenden Kontexten ein ungültiger Zeiger
int* copy = ptr; // implementierungsdefiniertes Verhalten
*ptr = 2;        // undefiniertes Verhalten: Dereferenzierung eines ungültigen Zeigers
delete ptr;      // undefiniertes Verhalten: Freigabe von Speicher über einen ungültigen Zeiger

Konstanz

  • Wenn cv vor * in der Pointer-Deklaration erscheint, ist es Teil der Deklarationsspezifizierer-Sequenz und bezieht sich auf das Objekt, auf das gezeigt wird.
  • Wenn cv nach * in der Pointer-Deklaration erscheint, ist es Teil des Deklarators und bezieht sich auf den Pointer, der deklariert wird.
Syntax Bedeutung
const T * Zeiger auf konstantes Objekt
T const * Zeiger auf konstantes Objekt
T * const Konstanter Zeiger auf Objekt
const T * const Konstanter Zeiger auf konstantes Objekt
T const * const Konstanter Zeiger auf konstantes Objekt
// pc ist ein nicht-konstanter Zeiger auf konstantes int
// cpc ist ein konstanter Zeiger auf konstantes int
// ppc ist ein nicht-konstanter Zeiger auf nicht-konstanten Zeiger auf konstantes int
const int ci = 10, *pc = &ci, *const cpc = pc, **ppc;
// p ist ein nicht-konstanter Zeiger auf nicht-konstantes int
// cp ist ein konstanter Zeiger auf nicht-konstantes int
int i, *p, *const cp = &i;
i = ci;    // okay: Wert des konstanten int wird in nicht-konstantes int kopiert
*cp = ci;  // okay: nicht-konstantes int (gezeigt durch konstanten Zeiger) kann geändert werden
pc++;      // okay: nicht-konstanter Zeiger (auf konstantes int) kann geändert werden
pc = cpc;  // okay: nicht-konstanter Zeiger (auf konstantes int) kann geändert werden
pc = p;    // okay: nicht-konstanter Zeiger (auf konstantes int) kann geändert werden
ppc = &pc; // okay: Adresse eines Zeigers auf konstantes int ist Zeiger auf Zeiger auf konstantes int
ci = 1;    // Fehler: konstantes int kann nicht geändert werden
ci++;      // Fehler: konstantes int kann nicht geändert werden
*pc = 2;   // Fehler: gezeigtes konstantes int kann nicht geändert werden
cp = &ci;  // Fehler: konstanter Zeiger (auf nicht-konstantes int) kann nicht geändert werden
cpc++;     // Fehler: konstanter Zeiger (auf konstantes int) kann nicht geändert werden
p = pc;    // Fehler: Zeiger auf nicht-konstantes int kann nicht auf konstantes int zeigen
ppc = &p;  // Fehler: Zeiger auf Zeiger auf konstantes int kann nicht auf
           // Zeiger auf nicht-konstantes int zeigen

Im Allgemeinen folgt die implizite Konvertierung von einem mehrstufigen Zeiger zu einem anderen den Regeln, die in Qualifikationskonvertierungen beschrieben sind.

Zusammengesetzter Zeigertyp

Wenn ein Operand eines Vergleichsoperators oder einer der zweiten und dritten Operanden eines bedingten Operators ein Zeiger oder Zeiger-auf-Mitglied ist, wird ein zusammengesetzter Zeigertyp als gemeinsamer Typ dieser Operanden bestimmt.

Gegeben zwei Operanden p1 und p2 mit den Typen T1 und T2 jeweils, können p1 und p2 nur dann einen zusammengesetzten Zeigertyp haben, wenn eine der folgenden Bedingungen erfüllt ist:

  • p1 und p2 sind beide Zeiger.
  • Einer von p1 und p2 ist ein Zeiger und der andere Operand ist eine Nullzeiger-Konstante.
  • p1 und p2 sind beide Nullzeiger-Konstanten, und mindestens einer von T1 und T2 ist ein Nicht-Integraltyp.
(seit C++11)
(bis C++14)
  • Mindestens einer von T1 und T2 ist ein Zeigertyp, Zeiger-auf-Member-Typ oder std::nullptr_t .
(seit C++14)

Der zusammengesetzte Zeigertyp C von p1 und p2 wird wie folgt bestimmt:

  • Falls p1 eine Nullzeiger-Konstante ist, ist C gleich T2 .
  • Andernfalls, falls p2 eine Nullzeiger-Konstante ist, ist C gleich T1 .
(bis C++11)
  • Falls p1 und p2 beide Nullzeiger-Konstanten sind, ist C gleich std::nullptr_t .
  • Andernfalls, falls p1 eine Nullzeiger-Konstante ist, ist C gleich T2 .
  • Andernfalls, falls p2 eine Nullzeiger-Konstante ist, ist C gleich T1 .
(seit C++11)
  • Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:
  • T1 oder T2 ist "Zeiger auf cv1 void ".
  • Der andere Typ ist "Zeiger auf cv2 T ", wobei T ein Objekttyp oder void ist.
C ist "Zeiger auf cv12 void ", wobei cv12 die Vereinigung von cv1 und cv2 ist.
  • Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:
  • T1 oder T2 ist "Zeiger auf Funktionstyp F1 ".
  • Der andere Typ ist "Zeiger auf noexcept-Funktionstyp F2 ".
  • F1 und F2 sind bis auf noexcept identisch.
C ist "Zeiger auf F1 ".
(seit C++17)
  • Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:
  • T1 ist "Zeiger auf C1 ".
  • T2 ist "Zeiger auf C2 ".
  • Einer der beiden Typen C1 und C2 ist referenzverwandt mit dem anderen.
C ist
  • der qualifikationskombinierte Typ von T1 und T2 , falls C1 referenzverwandt mit C2 ist, oder
  • der qualifikationskombinierte Typ von T2 und T1 , falls C2 referenzverwandt mit C1 ist.
  • Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:
  • T1 oder T2 ist "Zeiger auf Member von C1 vom Funktionstyp F1 ".
  • Der andere Typ ist "Zeiger auf Member von C2 vom noexcept-Funktionstyp F2 ".
  • Einer von C1 und C2 ist referenzverwandt mit dem anderen.
  • F1 und F2 sind bis auf noexcept identisch.
C ist
  • "Zeiger auf Member von C2 vom Typ F1 ", wenn C1 referenzverwandt mit C2 ist, oder
  • "Zeiger auf Member von C1 vom Typ F1 ", wenn C2 referenzverwandt mit C1 ist.
(seit C++17)
  • Andernfalls, wenn alle folgenden Bedingungen erfüllt sind:
  • T1 ist "Zeiger auf Element von C1 vom Nicht-Funktionstyp M1 ".
  • T2 ist "Zeiger auf Element von C2 vom Nicht-Funktionstyp M2 "
  • M1 und M2 sind identisch bis auf Top-Level CV-Qualifizierungen.
  • Einer von C1 und C2 ist referenzverwandt mit dem anderen.
C ist
  • der qualifikationskombinierte Typ von T2 und T1 , wenn C1 referenzverwandt zu C2 ist, oder
  • der qualifikationskombinierte Typ von T1 und T2 , wenn C2 referenzverwandt zu C1 ist.
  • Andernfalls, falls T1 und T2 ähnliche Typen sind, ist C der qualifikationskombinierte Typ von T1 und T2 .
  • Andernfalls haben p1 und p2 keinen zusammengesetzten Zeigertyp, und ein Programm, das die Bestimmung von C als solchen Typ erfordert, ist fehlerhaft.
using p = void*;
using q = const int*;
// Die Bestimmung des zusammengesetzten Zeigertyps von „p“ und „q“
// fällt unter den Fall [„Zeiger auf cv1 void“ und „Zeiger auf cv2 T“]:
// cv1 = leer, cv2 = const, cv12 = const
// Ersetze „cv12 = const“ in „Zeiger auf cv12 void“:
// der zusammengesetzte Zeigertyp ist „const void*“
using pi = int**;
using pci = const int**;
// Die Bestimmung des zusammengesetzten Zeigertyps von „pi“ und „pci“
// fällt unter den Fall [Zeiger auf ähnliche Typen „C1“ und „C2“]:
// C1 = int*, C2 = const int*
// es sind referenzverwandte Typen (in beide Richtungen), da sie ähnlich sind
// der zusammengesetzte Zeigertyp ist der qualifikationskombinierte Typ
// von „p1“ und „pc1“ (oder der von „pci“ und „pi“): „const int**“

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
CWG 73 C++98 ein Zeiger auf ein Objekt verglich sich nie gleich
mit einem Zeiger auf das Ende eines Arrays
für nicht-null und nicht-Funktionszeiger,
vergleiche die Adressen, die sie repräsentieren
CWG 903 C++98 jeder integrale konstante Ausdruck, der
zu 0 ausgewertet wurde, war ein Nullzeigerkonstant
beschränkt auf Integer-
Literale mit Wert 0
CWG 1438 C++98 das Verhalten bei Verwendung eines ungültigen Zeiger-
werts auf irgendeine Weise war undefiniert
Verhalten außer Dereferenzierung und
Übergabe an Freigabefunktionen
sind implementierungsdefiniert
CWG 1512
( N3624 )
C++98 die Regel für zusammengesetzte Zeigertypen war unvollständig und erlaubte daher
keinen Vergleich zwischen int ** und const int **
wurde vervollständigt
CWG 2206 C++98 ein Zeiger auf void und ein Zeiger auf
Funktion hatten einen zusammengesetzten Zeigertyp
sie haben keinen solchen Typ
CWG 2381 C++17 Funktionszeigerkonvertierungen waren nicht erlaubt
bei der Bestimmung des zusammengesetzten Zeigertyps
erlaubt
CWG 2822 C++98 das Erreichen des Endes der Dauer eines Speicher-
bereichs könnte Zeigerwerte ungültig machen
Zeigergültigkeit basiert
auf dem Auswertungskontext
CWG 2933 C++98 Zeiger auf Funktionen waren immer ungültig sie sind immer gültig

Siehe auch

C-Dokumentation für Pointer-Deklaration