Operatori correlati al puntatore: accettare l'indirizzo delle variabili, dereferenziare le posizioni di archiviazione e accedere alle posizioni di memoria
Gli operatori puntatore consentono di accettare l'indirizzo di una variabile (&
), dereferenziare un puntatore (*
), confrontare i valori del puntatore e aggiungere o sottrarre puntatori e interi.
È possibile usare gli operatori seguenti con i puntatori:
- Operatore
&
(address-of) unario: per ottenere l'indirizzo di una variabile - Operatore
*
(riferimento indiretto al puntatore): per ottenere la variabile indicata da un puntatore - Operatori
->
(accesso ai membri) e[]
(accesso agli elementi) - Operatori aritmetici
+
,-
,++
e--
- Operatori di confronto
==
,!=
,<
,>
,<=
e>=
Per informazioni sui tipi di puntatori, vedere Tipi di puntatori.
Nota
Qualsiasi operazione con i puntatori richiede un contesto unsafe. Il codice che contiene blocchi non sicuri deve essere compilato con l'opzione AllowUnsafeBlocks del compilatore.
Operatore address-of &
L'operatore &
unario restituisce l'indirizzo del relativo operando:
unsafe
{
int number = 27;
int* pointerToNumber = &number;
Console.WriteLine($"Value of the variable: {number}");
Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}
// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4
L'operando dell'operatore &
deve essere una variabile fissa. Le variabili fisse sono variabili che si trovano in posizioni di archiviazione non interessate dall'operazione di Garbage Collector. Nell'esempio precedente la variabile locale number
è una variabile fissa perché si trova nello stack. Le variabili che si trovano in posizioni di archiviazione che possono essere influenzate da Garbage Collector (ad esempio rilocate) sono chiamate variabili mobili. I campi degli oggetti e gli elementi delle matrici sono esempi di variabili mobili. È possibile ottenere l'indirizzo di una variabile mobile se si "fissa" o si "blocca" la variabile con una istruzione fixed
. L'indirizzo ottenuto è valido solo all'interno del blocco di un'istruzione fixed
. L'esempio seguente mostra come usare un’istruzione fixed
e l'operatore &
:
unsafe
{
byte[] bytes = { 1, 2, 3 };
fixed (byte* pointerToFirst = &bytes[0])
{
// The address stored in pointerToFirst
// is valid only inside this fixed statement block.
}
}
Non è possibile ottenere l'indirizzo di una costante o di un valore.
Per altre informazioni sulle variabili fisse e mobili, vedere la sezione Variabili fisse e mobili dell'articolo relativo alla specifica del linguaggio C#.
L'operatore &
binario calcola l'AND logico dei relativi operandi booleani o l'AND logico bit per bit dei relativi operandi integrali.
Operatore * (riferimento indiretto a puntatore)
L'operatore di riferimento indiretto a puntatore unario *
ottiene la variabile a cui punta il relativo operando. L'operatore è chiamato anche operatore di dereferenziazione. L'operando dell'operatore *
deve essere un tipo di puntatore.
unsafe
{
char letter = 'A';
char* pointerToLetter = &letter;
Console.WriteLine($"Value of the `letter` variable: {letter}");
Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");
*pointerToLetter = 'Z';
Console.WriteLine($"Value of the `letter` variable after update: {letter}");
}
// Output is similar to:
// Value of the `letter` variable: A
// Address of the `letter` variable: DCB977DDF4
// Value of the `letter` variable after update: Z
Non è possibile applicare l'operatore *
a un'espressione di tipo void*
.
L'operatore *
binario calcola il prodotto dei relativi operandi numerici.
Operatore di accesso al membro del puntatore - >
L'operatore ->
unisce il riferimento indiretto al puntatore e l'accesso ai membri. Ovvero, se x
è un puntatore di tipo T*
e y
è un membro accessibile di tipo T
, un'espressione nella forma
x->y
equivale a
(*x).y
Nell'esempio seguente viene illustrato l'uso dell'operatore ->
:
public struct Coords
{
public int X;
public int Y;
public override string ToString() => $"({X}, {Y})";
}
public class PointerMemberAccessExample
{
public static unsafe void Main()
{
Coords coords;
Coords* p = &coords;
p->X = 3;
p->Y = 4;
Console.WriteLine(p->ToString()); // output: (3, 4)
}
}
Non è possibile applicare l'operatore ->
a un'espressione di tipo void*
.
Operatore [] (accesso agli elementi del puntatore)
Per un'espressione p
di un tipo di puntatore, l'accesso a un elemento del puntatore nella forma p[n]
viene calcolato come *(p + n)
, dove n
deve essere un tipo convertibile in modo implicito in int
, uint
, long
o ulong
. Per informazioni sul comportamento dell'operatore +
con i puntatori, vedere la sezione Addizione o sottrazione di un valore integrale da un puntatore.
L'esempio seguente illustra come accedere agli elementi della matrice con un puntatore e l'operatore []
:
unsafe
{
char* pointerToChars = stackalloc char[123];
for (int i = 65; i < 123; i++)
{
pointerToChars[i] = (char)i;
}
Console.Write("Uppercase letters: ");
for (int i = 65; i < 91; i++)
{
Console.Write(pointerToChars[i]);
}
}
// Output:
// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Nell'esempio precedente, un'espressione stackalloc
alloca un blocco di memoria nello stack.
Nota
L'operatore di accesso agli elementi del puntatore non ricerca gli errori relativi a valori non compresi nell'intervallo.
Non è possibile usare []
per l'accesso all’elemento del puntatore con un'espressione di tipo void*
.
È anche possibile usare l'operatore []
per l'accesso agli elementi di una matrice o all'indicizzatore.
Operatori aritmetici dei puntatori
È possibile eseguire le operazioni aritmetiche seguenti con i puntatori:
- Aggiungere o sottrarre un valore integrale da un puntatore
- Sottrarre due puntatori
- Incrementare o decrementare un puntatore
Non è possibile eseguire queste operazioni con puntatori di tipo void*
.
Per informazioni sulle operazioni aritmetiche supportate con i tipi numerici, vedere Operatori aritmetici.
Addizione o sottrazione di un valore integrale da un puntatore
Per un puntatore p
di tipo T*
e un'espressione n
di tipo implicitamente convertibile in int
, uint
, long
o ulong
, l'addizione e la sottrazione sono definite come segue:
- Entrambe le espressioni
p + n
en + p
producono un puntatore di tipoT*
risultante dalla somma din * sizeof(T)
all'indirizzo specificato dap
. - L'espressione
p - n
produce un puntatore di tipoT*
risultante dalla sottrazione din * sizeof(T)
dall'indirizzo specificato dap
.
L'operatore sizeof
ottiene la dimensione di un tipo in byte.
L'esempio seguente illustra l'uso dell'operatore +
con un puntatore:
unsafe
{
const int Count = 3;
int[] numbers = new int[Count] { 10, 20, 30 };
fixed (int* pointerToFirst = &numbers[0])
{
int* pointerToLast = pointerToFirst + (Count - 1);
Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");
Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");
}
}
// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144
Sottrazione di puntatori
Per i due puntatori p1
e p2
di tipo T*
, l'espressione p1 - p2
produce la differenza tra gli indirizzi specificati da p1
e p2
divisi per sizeof(T)
. Il tipo del risultato è long
. Ovvero, p1 - p2
viene calcolato come ((long)(p1) - (long)(p2)) / sizeof(T)
.
L'esempio seguente illustra la sottrazione del puntatore:
unsafe
{
int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };
int* p1 = &numbers[1];
int* p2 = &numbers[5];
Console.WriteLine(p2 - p1); // output: 4
}
Incremento e decremento dei puntatori
L'operatore di incremento ++
somma 1 al relativo operando puntatore. L'operatore di decremento --
sottrae 1 dal relativo operando puntatore.
Entrambi gli operatori sono supportati in due forme: come suffisso (p++
e p--
) e come prefisso (++p
e --p
). Il risultato di p++
e p--
è il valore di p
prima dell'operazione. Il risultato di ++p
e --p
è il valore di p
dopo l'operazione.
L'esempio seguente illustra il comportamento di entrambi gli operatori di incremento suffisso e prefisso:
unsafe
{
int* numbers = stackalloc int[] { 0, 1, 2 };
int* p1 = &numbers[0];
int* p2 = p1;
Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");
Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");
Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");
Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");
}
// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516
Operatori di confronto dei puntatori
È possibile usare gli operatori ==
, !=
, <
, >
, <=
e >=
per confrontare gli operandi di qualsiasi tipo di puntatore, incluso void*
. Questi operatori confrontano gli indirizzi specificati dai due operandi come se fossero interi senza segno.
Per informazioni sul comportamento di questi operatori per gli operandi di altri tipi, vedere gli articoli Operatori di uguaglianza e Operatori di confronto.
Precedenza tra gli operatori
Nell'elenco seguente gli operatori correlati ai puntatori sono ordinati dalla precedenza più elevata a quella più bassa:
- Operatori di incremento
x++
e decrementox--
suffisso e operatori->
e[]
- Operatori di incremento
++x
e decremento--x
prefisso e operatori&
e*
- Operatori di addizione
+
e-
- Operatori di confronto
<
,>
,<=
e>=
- Operatori di uguaglianza
==
e!=
Usare le parentesi, ()
, per cambiare l'ordine di valutazione imposto dalla precedenza tra gli operatori.
Per l'elenco completo degli operatori C# ordinati per livello di precedenza, vedere la sezione precedenza dell'operatore nell'articolo operatori C#.
Overload degli operatori
Un tipo definito dall'utente non può eseguire l'overload degli operatori correlati ai puntatori &
, *
, ->
e []
.
Specifiche del linguaggio C#
Per altre informazioni, vedere le sezioni seguenti delle specifiche del linguaggio C#:
- Variabili fisse e mobili
- Operatore address-of
- Riferimento indiretto a puntatore
- Accesso ai membri del puntatore
- Accesso agli elementi del puntatore
- Puntatore aritmetico
- Incremento e decremento dei puntatori
- Confronto tra puntatori