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
}


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow