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
}


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow