Suche…


Bemerkungen

Hinweise und unsafe

Zeiger erzeugen naturgemäß nicht überprüfbaren Code. Daher erfordert die Verwendung eines beliebigen Zeigertyps einen unsafe Kontext.

Der Typ System.IntPtr ist ein sicherer Wrapper um eine void* . Es ist eine bequemere Alternative zu void* wenn ansonsten ein unsicherer Kontext nicht erforderlich ist, um die vorliegende Aufgabe auszuführen.

Undefiniertes Verhalten

Wie in C und C ++ kann eine falsche Verwendung von Zeigern undefiniertes Verhalten verursachen, wobei mögliche Nebeneffekte Speicherbeschädigung und die Ausführung von nicht beabsichtigtem Code sein können. Aufgrund der nicht nachweisbaren Natur der meisten Zeigeroperationen liegt die korrekte Verwendung von Zeigern vollständig in der Verantwortung des Programmierers.

Typen, die Zeiger unterstützen

Im Gegensatz zu C und C ++ haben nicht alle C # -Typen entsprechende Zeigertypen. Ein Typ T kann einen entsprechenden Zeigertyp haben, wenn beide der folgenden Kriterien zutreffen:

  • T ist ein Strukturtyp oder ein Zeigertyp.
  • T enthält nur Member, die beide dieser Kriterien rekursiv erfüllen.

Zeiger für den Array-Zugriff

Dieses Beispiel zeigt, wie Zeiger für den C-ähnlichen Zugriff auf C # -Arrays verwendet werden können.

unsafe
{
    var buffer = new int[1024];
    fixed (int* p = &buffer[0])
    {
        for (var i = 0; i < buffer.Length; i++)
        {
            *(p + i) = i;
        }
    }
}

Das unsafe Schlüsselwort ist erforderlich, da der Zeigerzugriff keine Begrenzungsprüfungen auslöst, die normalerweise beim normalen Zugriff auf C # -Anordnungen ausgegeben werden.

Das fixed Schlüsselwort weist den C # -Compiler an, Anweisungen zu senden, um das Objekt auf eine ausnahmesichere Weise zu fixed . Es ist ein Pinning erforderlich, um sicherzustellen, dass der Garbage Collector das Array nicht in den Arbeitsspeicher versetzt, da dadurch alle Zeiger ungültig werden, die auf das Array zeigen.

Zeigerarithmetik

Addition und Subtraktion in Zeigern funktionieren anders als ganze Zahlen. Wenn ein Zeiger inkrementiert oder dekrementiert wird, wird die Adresse, auf die er zeigt, um die Größe des Referenztyps erhöht oder verringert.

Der Typ int (Alias ​​für System.Int32 ) hat beispielsweise eine Größe von 4. Wenn ein int in Adresse 0 gespeichert werden kann, kann das nachfolgende int in Adresse 4 usw. gespeichert werden. In Code:

var ptr = (int*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 4
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8

In ähnlicher Weise hat der Typ long (Alias ​​für System.Int64 ) eine Größe von 8. Wenn long in Adresse 0 gespeichert werden kann, kann der nachfolgende long in Adresse 8 usw. gespeichert werden. In Code:

var ptr = (long*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 16

Der Typ void ist speziell und void Zeiger sind auch speziell und sie werden als Catch-All-Zeiger verwendet, wenn der Typ nicht bekannt ist oder keine Rolle spielt. void Zeiger können aufgrund ihrer größenunabhängigen Natur nicht inkrementiert oder dekrementiert werden:

var ptr = (void*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));

Das Sternchen ist Teil des Typs

In C und C ++ ist der Stern in der Deklaration einer Zeigervariable Teil des zu deklarierenden Ausdrucks . In C # ist der Stern in der Deklaration Teil des Typs .

In C, C ++ und C # deklariert der folgende Ausschnitt einen int Zeiger:

int* a;

In C und C ++ deklariert das folgende Snippet einen int Zeiger und eine int Variable. In C # werden zwei int Zeiger deklariert:

int* a, b; 

In C und C ++ deklariert das folgende Snippet zwei int Zeiger. In c # ist es ungültig:

int *a, *b;

Leere*

C # erbt von C und C ++ die Verwendung von void* als typunabhängiger und größenagnostischer Zeiger.

void* ptr;

Jeder Zeigertyp kann mithilfe einer impliziten Konvertierung void* zugewiesen werden:

int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;

Das Gegenteil erfordert eine explizite Konvertierung:

int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;

Mitgliederzugang mit ->

C # erbt von C und C ++ die Verwendung des Symbols -> als Mittel zum Zugriff auf die Mitglieder einer Instanz über einen typisierten Zeiger.

Betrachten Sie die folgende Struktur:

struct Vector2
{
    public int X;
    public int Y;
}

Dies ist ein Beispiel für die Verwendung von -> um auf seine Mitglieder zuzugreifen:

Vector2 v;
v.X = 5;
v.Y = 10;

Vector2* ptr = &v;
int x = ptr->X;
int y = ptr->Y;
string s = ptr->ToString();

Console.WriteLine(x); // prints 5
Console.WriteLine(y); // prints 10
Console.WriteLine(s); // prints Vector2

Generische Zeiger

Die Kriterien, die ein Typ erfüllen muss, um Zeiger zu unterstützen (siehe Anmerkungen ), können nicht in Form allgemeiner Einschränkungen ausgedrückt werden. Daher schlägt jeder Versuch fehl, einen Zeiger auf einen durch einen generischen Typparameter bereitgestellten Typ zu deklarieren.

void P<T>(T obj) 
    where T : struct
{
    T* ptr = &obj; // compile-time error
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow