C# Language
Zeiger
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
}