C# Language
puntatori
Ricerca…
Osservazioni
Puntatori e unsafe
A causa della loro natura, i puntatori producono codice non verificabile. Pertanto, l'utilizzo di qualsiasi tipo di puntatore richiede un contesto unsafe
.
Il tipo System.IntPtr
è un wrapper sicuro attorno a un void*
. È inteso come un'alternativa più conveniente a void*
quando non è richiesto altrimenti un contesto non sicuro per eseguire l'attività a portata di mano.
Comportamento non definito
Come in C e C ++, l'uso scorretto di puntatori può richiamare un comportamento indefinito, con possibili effetti collaterali come il danneggiamento della memoria e l'esecuzione di codice non intenzionale. A causa della natura non verificabile della maggior parte delle operazioni del puntatore, l'uso corretto dei puntatori è interamente una responsabilità del programmatore.
Tipi che supportano i puntatori
A differenza di C e C ++, non tutti i tipi di C # hanno tipi di puntatore corrispondenti. Un tipo T
può avere un tipo di puntatore corrispondente se si applicano entrambi i seguenti criteri:
-
T
è un tipo di struttura o un tipo di puntatore. -
T
contiene solo membri che soddisfano entrambi questi criteri in modo ricorsivo.
Puntatori per l'accesso alla matrice
Questo esempio dimostra come i puntatori possono essere utilizzati per l'accesso di tipo C agli array C #.
unsafe
{
var buffer = new int[1024];
fixed (int* p = &buffer[0])
{
for (var i = 0; i < buffer.Length; i++)
{
*(p + i) = i;
}
}
}
La parola chiave unsafe
è necessaria perché l'accesso al puntatore non emetterà alcun controllo sui limiti normalmente emesso quando si accede agli array C # in modo regolare.
La parola chiave fixed
dice al compilatore C # di emettere istruzioni per bloccare l'oggetto in un modo sicuro. Il blocco è necessario per garantire che il garbage collector non sposterà la matrice in memoria, in quanto ciò invaliderebbe qualsiasi puntatore che punta all'interno dell'array.
Aritmetica del puntatore
L'addizione e la sottrazione nei puntatori funzionano in modo diverso dai numeri interi. Quando un puntatore viene incrementato o decrementato, l'indirizzo a cui punta viene aumentato o diminuito dalla dimensione del tipo di referente.
Ad esempio, il tipo int
(alias per System.Int32
) ha una dimensione di 4. Se un int
può essere memorizzato nell'indirizzo 0, il successivo int
può essere memorizzato nell'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
Analogamente, il tipo long
(alias per System.Int64
) ha una dimensione di 8. Se un long
può essere memorizzato nell'indirizzo 0, il long
successivo può essere memorizzato nell'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 anche i puntatori void
sono speciali e vengono utilizzati come indicatori catch-all quando il tipo non è noto o non ha importanza. A causa della loro natura agnostica, i puntatori di void
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 fa parte dell'espressione dichiarata. In C #, l'asterisco nella dichiarazione fa parte del tipo .
In C, C ++ e C #, il seguente snippet dichiara un puntatore int
:
int* a;
In C e C ++, il seguente snippet dichiara un puntatore int
e una variabile int
. In C #, dichiara due puntatori int
:
int* a, b;
In C e C ++, il seguente snippet dichiara due puntatori int
. In C #, non è valido:
int *a, *b;
void *
C # eredita da C e C ++ l'uso di void*
come puntatore agnostico di tipo e puntatore agnostico.
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 usando ->
C # eredita da C e C ++ l'uso 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'uso 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 (vedere Note ) non possono essere espressi 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
}