C# Language
pointers
Zoeken…
Opmerkingen
Aanwijzingen en unsafe
Vanwege hun aard produceren wijzers niet-verifieerbare code. Het gebruik van elk type aanwijzer vereist dus een unsafe
context.
Het type System.IntPtr
is een veilige verpakking rond een void*
. Het is bedoeld als een handiger alternatief voor void*
wanneer een onveilige context anders niet vereist is om de taak uit te voeren.
Ongedefinieerd gedrag
Net als in C en C ++ kan onjuist gebruik van pointers ongedefinieerd gedrag oproepen, met mogelijke bijwerkingen als geheugenbeschadiging en uitvoering van onbedoelde code. Vanwege het niet-verifieerbare karakter van de meeste aanwijzerbewerkingen, is correct gebruik van aanwijzers volledig een verantwoordelijkheid van de programmeur.
Typen die aanwijzers ondersteunen
In tegenstelling tot C en C ++ hebben niet alle C # -typen overeenkomstige pointertypen. Een type T
kan een bijbehorend pointertype hebben als beide volgende criteria van toepassing zijn:
-
T
is een struct type of een pointer type. -
T
bevat alleen leden die recursief aan beide criteria voldoen.
Aanwijzingen voor toegang tot de array
Dit voorbeeld laat zien hoe pointers kunnen worden gebruikt voor C-achtige toegang tot C # -matrices.
unsafe
{
var buffer = new int[1024];
fixed (int* p = &buffer[0])
{
for (var i = 0; i < buffer.Length; i++)
{
*(p + i) = i;
}
}
}
Het unsafe
sleutelwoord is vereist omdat pointer toegang geen limietcontroles zal uitzenden die normaal worden uitgezonden bij toegang tot C # arrays op de reguliere manier.
Het fixed
sleutelwoord vertelt de C # compiler om instructies te geven om het object op een uitzonderingsveilige manier vast te zetten. Pinning is vereist om ervoor te zorgen dat de vuilnisman de array niet in het geheugen verplaatst, omdat dit alle aanwijzers die binnen de array wijzen ongeldig zou maken.
Wijzer rekenen
Optellen en aftrekken in aanwijzers werkt anders dan gehele getallen. Wanneer een aanwijzer wordt verhoogd of verlaagd, wordt het adres waarnaar deze verwijst verhoogd of verlaagd met de grootte van het referentietype.
Het type int
(alias voor System.Int32
) heeft bijvoorbeeld een grootte van 4. Als een int
kan worden opgeslagen op adres 0, kan de volgende int
worden opgeslagen op adres 4, enzovoort. 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
Op dezelfde manier heeft het type long
(alias voor System.Int64
) een grootte van 8. Als een long
kan worden opgeslagen in adres 0, kan de volgende long
worden opgeslagen in adres 8, enzovoort. 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
Het type void
is speciaal en void
pointers zijn ook speciaal en ze worden gebruikt als catch-all pointers wanneer het type niet bekend is of er niet toe doet. Vanwege hun grootte-agnostische aard kunnen void
wijzers niet worden verhoogd of verlaagd:
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));
De asterisk is onderdeel van het type
In C en C ++ maakt het sterretje in de declaratie van een pointervariabele deel uit van de uitdrukking die wordt gedeclareerd. In C # maakt het sterretje in de aangifte deel uit van het type .
In C, C ++ en C # verklaart het volgende fragment een int
pointer:
int* a;
In C en C ++ verklaart het volgende fragment een int
pointer en een int
variabele. In C # verklaart het twee int
verwijzingen:
int* a, b;
In C en C ++ declareert het volgende fragment twee int
verwijzingen. In C # is het ongeldig:
int *a, *b;
leegte *
C # neemt van C en C ++ het gebruik van void*
als een type-agnostische en grootte-agnostische aanwijzer.
void* ptr;
Elk pointertype kan worden toegewezen aan void*
met behulp van een impliciete conversie:
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
Het omgekeerde vereist een expliciete conversie:
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;
Lidstoegang met ->
C # neemt van C en C ++ het gebruik van het symbool ->
als een manier om toegang te krijgen tot de leden van een instantie via een getypte aanwijzer.
Overweeg de volgende struct:
struct Vector2
{
public int X;
public int Y;
}
Dit is een voorbeeld van het gebruik van ->
om toegang te krijgen tot zijn leden:
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
Algemene aanwijzingen
De criteria waaraan een type moet voldoen om pointers te ondersteunen (zie Opmerkingen ) kunnen niet worden uitgedrukt in generieke beperkingen. Daarom mislukt elke poging om een pointer te declareren naar een type dat wordt aangeboden via een generieke parameter type.
void P<T>(T obj)
where T : struct
{
T* ptr = &obj; // compile-time error
}