Zeigerbezogene Operatoren: nehmen die Adressen von Variablen, dereferenzieren Speicherorte und greifen auf Arbeitsspeicherorte zu

Mit den Zeigeroperatoren können Sie die Adresse einer Variablen (&) nehmen, einen Zeiger dereferenzieren (*), Zeigerwerte vergleichen sowie Zeiger und ganze Zahlen hinzufügen oder subtrahieren.

Sie verwenden die folgenden Operatoren für Zeiger:

Informationen zu Zeigertypen finden Sie unter Zeigertypen.

Hinweis

Für alle Operationen mit Zeigern ist ein Kontext des Typs unsafe erforderlich. Code, in dem unsichere Blöcke enthalten sind, muss mit der Compileroption AllowUnsafeBlocks kompiliert werden.

address-of-Operator &

Der unäre Operator & gibt die Adresse seines Operanden zurück:

unsafe
{
    int number = 27;
    int* pointerToNumber = &number;

    Console.WriteLine($"Value of the variable: {number}");
    Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}
// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4

Der Operand des Operators & muss eine feste Variable sein. Feste Variablen befinden sich an Speicherorten, auf die sich Garbage Collector-Operationen nicht auswirken. Im vorherigen Beispiel ist die lokale Variable number eine feste Variable, da sie im Stapel angeordnet ist. Variablen an Speicherorten, auf die sich der Garbage Collector auswirken kann (z. B. durch eine Verschiebung), werden als bewegliche Variablen bezeichnet. Objektfelder und Arrayelemente sind Beispiele für bewegliche Variablen. Sie können die Adresse einer beweglichen Variablen erhalten, wenn Sie sie mit einer fixed-Anweisung „fixieren“ bzw. „anheften“. Die erhaltene Adresse ist nur innerhalb des Blocks einer fixed-Anweisung gültig. Im folgenden Beispiel wird veranschaulicht, wie Sie eine fixed-Anweisung und den &-Operator verwenden:

unsafe
{
    byte[] bytes = { 1, 2, 3 };
    fixed (byte* pointerToFirst = &bytes[0])
    {
        // The address stored in pointerToFirst
        // is valid only inside this fixed statement block.
    }
}

Sie können nicht die Adresse einer Konstanten oder eines Werts abrufen.

Weitere Informationen zu festen und beweglichen Variablen finden Sie im Abschnitt Feste und verschiebbare Variablen der Spezifikation für die Sprache C#.

Mit dem binären Operator & wird der Wert für logisches UND der booleschen Operanden oder der Wert für bitweise logisches UND für die integralen Operanden berechnet.

Zeigerdereferenzierungsoperator *

Mit dem unären Zeigerdereferenzierungsoperator * wird die Variable abgerufen, auf die der Operand verweist. Er wird auch kurz als Dereferenzierungsoperator bezeichnet. Der Operand des Operators * muss einen Zeigertyp aufweisen.

unsafe
{
    char letter = 'A';
    char* pointerToLetter = &letter;
    Console.WriteLine($"Value of the `letter` variable: {letter}");
    Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");

    *pointerToLetter = 'Z';
    Console.WriteLine($"Value of the `letter` variable after update: {letter}");
}
// Output is similar to:
// Value of the `letter` variable: A
// Address of the `letter` variable: DCB977DDF4
// Value of the `letter` variable after update: Z

Sie können den Operator * nicht auf einen Ausdruck vom Typ void* anwenden.

Mit dem binären Operator * wird das Produkt seiner numerischen Operanden berechnet.

Zeigermember-Zugriffsoperator ->

Mit dem Operator -> werden die Zeigerdereferenzierung und der Memberzugriff kombiniert. Wenn x ein Zeiger des Typs T* und y ein Member des Typs T ist, auf den zugegriffen werden kann, ist ein Ausdruck der Form

x->y

für die folgende Syntax:

(*x).y

Im folgenden Beispiel wird die Verwendung des ->-Operators veranschaulicht:

public struct Coords
{
    public int X;
    public int Y;
    public override string ToString() => $"({X}, {Y})";
}

public class PointerMemberAccessExample
{
    public static unsafe void Main()
    {
        Coords coords;
        Coords* p = &coords;
        p->X = 3;
        p->Y = 4;
        Console.WriteLine(p->ToString());  // output: (3, 4)
    }
}

Sie können den Operator -> nicht auf einen Ausdruck vom Typ void* anwenden.

Zeigerelementzugriff-Operator []

Für einen Ausdruck p mit einem Zeigertyp wird ein Zeigerelementzugriff der Form p[n] als *(p + n) ausgewertet. Hierbei muss n einen Typ aufweisen, der implizit in int, uint, long oder ulong konvertiert werden kann. Informationen zum Verhalten des Operators + mit Zeigern finden Sie im Abschnitt Addieren oder Subtrahieren eines Integralwerts zu bzw. von einem Zeiger.

Im folgenden Beispiel wird veranschaulicht, wie Sie mit einem Zeiger und dem Operator [] auf Arrayelemente zugreifen:

unsafe
{
    char* pointerToChars = stackalloc char[123];

    for (int i = 65; i < 123; i++)
    {
        pointerToChars[i] = (char)i;
    }

    Console.Write("Uppercase letters: ");
    for (int i = 65; i < 91; i++)
    {
        Console.Write(pointerToChars[i]);
    }
}
// Output:
// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ

Im vorangegangenen Beispiel ordnet ein stackalloc-Ausdruck einen Speicherblock im Stapel zu.

Hinweis

Der Zeigerelementzugriff-Operator führt keine Überprüfung auf Fehler vom Typ „Außerhalb des gültigen Bereichs“ durch.

Sie können [] nicht für den Zeigerelementzugriff mit einem Ausdruck vom Typ void* verwenden.

Sie können auch den Operator [] für den Arrayelement- oder Indexerzugriff nutzen.

Arithmetische Zeigeroperatoren

Sie können mit Zeigern die folgenden arithmetischen Operationen durchführen:

  • Addieren oder Subtrahieren eines Integralwerts zu bzw. von einem Zeiger
  • Subtrahieren von zwei Zeigern
  • Inkrementieren oder Dekrementieren eines Zeigers

Sie können diese Operationen nicht mit Zeigern vom Typ void* durchführen.

Weitere Informationen zu unterstützten arithmetischen Operationen mit numerischen Typen finden Sie unter Arithmetische Operatoren.

Addieren oder Subtrahieren eines Integralwerts zu bzw. von einem Zeiger

Für einen Zeiger p vom Typ T* und einen Ausdruck n eines Typs, der implizit in int, uint, long oder ulong konvertiert werden kann, sind die Addition und die Subtraktion wie folgt definiert:

  • Für die Ausdrücke p + n und n + p wird jeweils ein Zeiger vom Typ T* erzeugt, der sich aus dem Addieren von n * sizeof(T) zur Adresse von p ergibt.
  • Für den Ausdruck p - n wird ein Zeiger vom Typ T* erzeugt, der sich ergibt, indem n * sizeof(T) von der Adresse von p subtrahiert wird.

Mit dem Operator sizeof wird die Größe eines Typs in Byte abgerufen.

Im folgenden Beispiel wird die Verwendung des Operators + mit einem Zeiger veranschaulicht:

unsafe
{
    const int Count = 3;
    int[] numbers = new int[Count] { 10, 20, 30 };
    fixed (int* pointerToFirst = &numbers[0])
    {
        int* pointerToLast = pointerToFirst + (Count - 1);

        Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");
        Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");
    }
}
// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144

Zeigersubtraktion

Für die beiden Zeiger p1 und p2 vom Typ T* ergibt der Ausdruck p1 - p2 den Unterschied zwischen den Adressen von p1 und p2 dividiert durch sizeof(T). Das Ergebnis hat den Typ long. p1 - p2 wird also als ((long)(p1) - (long)(p2)) / sizeof(T) berechnet.

Im folgenden Beispiel ist die Zeigersubtraktion dargestellt:

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };
    int* p1 = &numbers[1];
    int* p2 = &numbers[5];
    Console.WriteLine(p2 - p1);  // output: 4
}

Inkrementieren und Dekrementieren von Zeigern

Mit dem Inkrementoperator ++ wird 1 zum Zeigeroperanden addiert. Mit dem Dekrementoperator -- wird 1 vom Zeigeroperanden subtrahiert.

Für beide Operatoren werden zwei Formen unterstützt: Postfix (p++ und p--) und Präfix (++p und --p). Das Ergebnis von p++ und p-- ist der Wert von pvor der Operation. Das Ergebnis von ++p und --p ist der Wert von pnach der Operation.

Im folgenden Beispiel wird das Verhalten von Postfix- und Präfix-Inkrementoperatoren veranschaulicht:

unsafe
{
    int* numbers = stackalloc int[] { 0, 1, 2 };
    int* p1 = &numbers[0];
    int* p2 = p1;
    Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");
    Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");
    Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");
    Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");
}
// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516

Vergleichsoperatoren für Zeiger

Sie können die Operatoren ==, !=, <, >, <= und >= verwenden, um Operanden jedes Zeigertyps zu vergleichen, z. B. void*. Mit diesen Operatoren werden die Adressen der zwei Operanden so verglichen, als ob es sich um ganze Zahlen ohne Vorzeichen handeln würde.

Informationen zum Verhalten dieser Operatoren für Operanden anderer Typen finden Sie in den Artikeln zu Gleichheitsoperatoren und Vergleichsoperatoren.

Operatorrangfolge

In der folgenden Liste sind die Zeigeroperatoren absteigend nach der Rangfolge sortiert:

  • Inkrementierungs- und Dekrementierungsoperatoren in Postfixnotation (x++ und x--) und die Operatoren -> und []
  • Inkrementierungs- und Dekrementierungsoperatoren in Präfixnotation (++x und --x) und die Operatoren & und *
  • Additive Operatoren + und -
  • Vergleichsoperatoren <, >, <= und >=
  • Gleichheitsoperatoren == und !=

Verwenden Sie Klammern (), wenn Sie die Reihenfolge der Auswertung ändern möchten, die durch die Operatorrangfolge festgelegt ist.

Die vollständige Liste der nach Rangfolgenebene sortierten C#-Operatoren finden Sie im Abschnitt Operatorrangfolge im Artikel C#-Operatoren.

Operatorüberladbarkeit

Mit einem benutzerdefinierten Typ können die Zeigeroperatoren &, *, -> und [] nicht überladen werden.

C#-Sprachspezifikation

Weitere Informationen finden Sie in den folgenden Abschnitten der C#-Sprachspezifikation:

Weitere Informationen