Puntatori

Puntatori

# Puntatori per l'accesso all'array

Questo esempio mostra come utilizzare i puntatori per l'accesso di tipo C alle matrici C#.

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

Il unsafe è richiesta perché l'accesso al puntatore non emetterà alcun controllo dei limiti normalmente emesso quando si accede agli array C# in modo regolare.

Il fixed La parola chiave indica al compilatore C# di emettere istruzioni per bloccare l'oggetto in modo sicuro dalle eccezioni. Il blocco è necessario per garantire che il Garbage Collector non sposti l'array in memoria, poiché ciò invaliderebbe tutti i puntatori che puntano all'interno dell'array.

# Aritmetica del puntatore

L'addizione e la sottrazione nei puntatori funzionano in modo diverso dagli interi. Quando un puntatore viene incrementato o decrementato, l'indirizzo a cui punta viene aumentato o diminuito della dimensione del tipo di riferimento.

Ad esempio, il tipo int (alias per System.Int32 ) ha una dimensione di 4. Se un int può essere memorizzato all'indirizzo 0, il successivo int può essere memorizzato all'indirizzo 4 e così via. Nel codice:

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

Allo stesso modo, il tipo long (alias per System.Int64 ) ha una dimensione di 8. Se un long può essere memorizzato all'indirizzo 0, il successivo long può essere memorizzato all'indirizzo 8 e così via. Nel codice:

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

Il tipo void è speciale e void anche i puntatori sono speciali e vengono usati come puntatori catch-all quando il tipo non è noto o non ha importanza. A causa della loro natura indipendente dalle dimensioni, void i puntatori non possono essere incrementati o decrementati:

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));

# L'asterisco fa parte del tipo

In C e C++, l'asterisco nella dichiarazione di una variabile puntatore è parte dell'espressione essere dichiarato. In C#, l'asterisco nella dichiarazione è parte del tipo .

In C, C++ e C#, il frammento di codice seguente dichiara un int puntatore:

int* a;

In C e C++, il frammento di codice seguente dichiara un int puntatore e un int variabile. In C#, dichiara due int puntatori:

int* a, b; 

In C e C++, lo snippet seguente dichiara due int puntatori. In C# non è valido:

int *a, *b;

# vuoto*

C# eredita da C e C++ l'utilizzo di void* come puntatore indipendente dal tipo e dalla dimensione.

void* ptr;

Qualsiasi tipo di puntatore può essere assegnato a void* utilizzando una conversione implicita:

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

Il contrario richiede una conversione esplicita:

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

# Accesso membri tramite ->

C# eredita da C e C++ l'utilizzo del simbolo -> come mezzo per accedere ai membri di un'istanza tramite un puntatore digitato.

Considera la seguente struttura:

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

Questo è un esempio dell'utilizzo di -> per accedere ai suoi membri:

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

# Puntatori generici

I criteri che un tipo deve soddisfare per supportare i puntatori (vedi Osservazioni ) non può essere espresso in termini di vincoli generici. Pertanto, qualsiasi tentativo di dichiarare un puntatore a un tipo fornito tramite un parametro di tipo generico avrà esito negativo.

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

# Osservazioni

# Puntatori e unsafe

Per loro natura, i puntatori producono codice non verificabile. Pertanto, l'utilizzo di qualsiasi tipo di puntatore richiede un unsafe contesto.

Il tipo System.IntPtr è un wrapper sicuro attorno a un void* . È inteso come un'alternativa più conveniente a void* quando un contesto non sicuro non è altrimenti richiesto per eseguire l'attività in questione.

# Comportamento non definito

Come in C e C++, l'uso non corretto dei puntatori può invocare un comportamento non definito, con possibili effetti collaterali come il danneggiamento della memoria e l'esecuzione di codice non previsto. A causa della natura non verificabile della maggior parte delle operazioni con i puntatori, il corretto utilizzo dei puntatori è interamente responsabilità del programmatore.

# Tipi che supportano i puntatori

A differenza di C e C++, non tutti i tipi C# hanno tipi di puntatori corrispondenti. Un tipo T può avere un tipo di puntatore corrispondente se si applicano entrambi i criteri seguenti:

  • T è un tipo struct o un tipo puntatore.
  • T contiene solo membri che soddisfano entrambi questi criteri in modo ricorsivo.