Operatory powiązane ze wskaźnikiem — przyjmują adres zmiennych, lokalizacje magazynu wyłuszczenia i uzyskują dostęp do lokalizacji pamięci
Operatory wskaźnika umożliwiają odbieranie adresu zmiennej (&
), wyłuszczanie wskaźnika (*
), porównywanie wartości wskaźnika oraz dodawanie lub odejmowanie wskaźników i liczb całkowitych.
Do pracy ze wskaźnikami służą następujące operatory:
- Operator jednoargumentowy
&
(adres-of): aby uzyskać adres zmiennej - Operator jednoargumentowy
*
(pośredni wskaźnik): aby uzyskać zmienną wskazywaną przez wskaźnik - Operatory
->
(dostęp do składowych) i[]
(dostęp do elementów) - Operatory
+
arytmetyczne ,-
,++
i--
- Operatory
==
porównania, ,!=
<
,>
,<=
i>=
Aby uzyskać informacje o typach wskaźników, zobacz Typy wskaźników.
Uwaga
Każda operacja ze wskaźnikami wymaga niebezpiecznego kontekstu. Kod zawierający niebezpieczne bloki należy skompilować za pomocą opcji kompilatora AllowUnsafeBlocks .
Operator adresowy i
Operator jednoargumentowy &
zwraca adres operandu:
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
Operand &
operatora musi być zmienną stałą. Zmienne stałe to zmienne, które znajdują się w lokalizacjach przechowywania, które nie mają wpływu na działanie modułu odśmiecywania pamięci. W poprzednim przykładzie zmienna lokalna jest zmienną number
stałą, ponieważ znajduje się na stosie. Zmienne, które znajdują się w lokalizacjach przechowywania, których może dotyczyć moduł odśmiecający pamięci (na przykład przeniesiony) są nazywane zmiennymi wymiennymi . Pola obiektów i elementy tablicy to przykłady zmiennych wymiennych. Adres zmiennej wymiennej można uzyskać, jeśli "naprawisz" lub "przypnij", będzie ona zawierać instrukcję fixed
. Uzyskany adres jest prawidłowy tylko wewnątrz bloku instrukcji fixed
. W poniższym przykładzie pokazano, jak używać fixed
instrukcji i &
operatora:
unsafe
{
byte[] bytes = { 1, 2, 3 };
fixed (byte* pointerToFirst = &bytes[0])
{
// The address stored in pointerToFirst
// is valid only inside this fixed statement block.
}
}
Nie można uzyskać adresu stałej ani wartości.
Aby uzyskać więcej informacji na temat zmiennych stałych i wymiennych, zobacz sekcję Zmienne stałe i ruchome specyfikacji języka C#.
Operator binarny &
oblicza logiczne AND jego operandów logicznych lub bitowych operatorów logicznych AND z operandów całkowitych.
Operator pośredni wskaźnika *
Operator *
jednoargumentowego wskaźnika pośredniego uzyskuje zmienną, do której wskazuje operand. Jest on również znany jako operator wyłudzenia. Operand *
operatora musi być typu wskaźnika.
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
Nie można zastosować *
operatora do wyrażenia typu void*
.
Operator binarny *
oblicza iloczyn operandów liczbowych.
Operator dostępu do elementu członkowskiego wskaźnika —>
Operator ->
łączy wskaźnik pośredni i dostęp do składowych. Oznacza to, że jeśli x
jest wskaźnikiem typu T*
i y
jest dostępnym elementem członkowskim typu T
, wyrażenie formularza
x->y
jest równoważny
(*x).y
W poniższym przykładzie pokazano użycie ->
operatora:
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)
}
}
Nie można zastosować ->
operatora do wyrażenia typu void*
.
Operator dostępu do elementu wskaźnika []
W przypadku wyrażenia p
typu wskaźnika dostęp do elementu wskaźnika formularza p[n]
jest obliczany jako *(p + n)
, gdzie n
musi być typu niejawnie konwertowany na int
, , uint
long
lub ulong
. Aby uzyskać informacje o zachowaniu +
operatora ze wskaźnikami, zobacz Dodawanie lub odejmowanie wartości całkowitej do lub z sekcji wskaźnika .
W poniższym przykładzie pokazano, jak uzyskać dostęp do elementów tablicy za pomocą wskaźnika i []
operatora:
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
W poprzednim przykładzie stackalloc
wyrażenie przydziela blok pamięci na stosie.
Uwaga
Operator dostępu elementu wskaźnika nie sprawdza błędów braku ograniczeń.
Nie można użyć []
do uzyskiwania dostępu do elementu wskaźnika z wyrażeniem typu void*
.
Możesz również użyć operatora do uzyskiwania []
dostępu do elementu tablicy lub indeksatora.
Operatory arytmetyczne wskaźnika
Następujące operacje arytmetyczne można wykonać za pomocą wskaźników:
- Dodawanie lub odejmowanie wartości całkowitej do lub ze wskaźnika
- Odejmowanie dwóch wskaźników
- Inkrementacja lub dekrementacja wskaźnika
Nie można wykonać tych operacji za pomocą wskaźników typu void*
.
Aby uzyskać informacje na temat obsługiwanych operacji arytmetycznych z typami liczbowymi, zobacz Operatory arytmetyczne.
Dodawanie lub odejmowanie wartości całkowitej do lub ze wskaźnika
Dla wskaźnika p
typu i wyrażenia n
typu niejawnie konwertowanego na int
, uint
, long
lub ulong
, dodawanie i odejmowanie są definiowane T*
w następujący sposób:
- Oba
p + n
wyrażenia in + p
generują wskaźnik typuT*
, który wynika z dodawanian * sizeof(T)
do adresu podanego przezp
element . - Wyrażenie
p - n
generuje wskaźnik typuT*
, który wynika z odejmowanian * sizeof(T)
z adresu podanego przezp
.
Operator sizeof
uzyskuje rozmiar typu w bajtach.
W poniższym przykładzie pokazano użycie +
operatora ze wskaźnikiem:
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
Odejmowanie wskaźnika
W przypadku dwóch wskaźników p1
i p2
typu T*
wyrażenie p1 - p2
generuje różnicę między adresami podanymi przez p1
i p2
podzielonymi przez sizeof(T)
. Typ wyniku to long
. Oznacza to, p1 - p2
że jest obliczany jako ((long)(p1) - (long)(p2)) / sizeof(T)
.
W poniższym przykładzie pokazano odejmowanie wskaźnika:
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
}
Inkrementacja i dekrementacja wskaźnika
Operator ++
inkrementacji dodaje wartość 1 do operandu wskaźnika. --
Operator dekrementacji odejmuje 1 od operandu wskaźnika.
Oba operatory są obsługiwane w dwóch formach: postfiks (p++
i ) i p--
prefiks (++p
i --p
). Wynik p++
i p--
jest wartością p
przed operacją. Wynik ++p
i --p
jest wartością p
po operacji.
W poniższym przykładzie pokazano zachowanie operatorów przyrostka postfiksu i prefiksu:
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
Operatory porównania wskaźników
Operatory ==
, , !=
, <
>
, <=
i umożliwiają >=
porównywanie operandów dowolnego typu wskaźnika, w tym void*
. Te operatory porównują adresy podane przez dwa operandy tak, jakby były niepodpisane liczby całkowite.
Aby uzyskać informacje o zachowaniu tych operatorów dla operandów innych typów, zobacz artykuły Operatory równości i Operatory porównania.
Kolejność wykonywania działań
Następujące operatory powiązane ze wskaźnikiem orders zaczynają się od najwyższego pierwszeństwa do najniższego:
- Operatory przyrostkowe i dekrementacji
x++
x--
oraz->
operatory i[]
- Operatory inkrementacji i dekrementacji
++x
--x
prefiksu&
oraz operatory i*
- Operatory i
-
addytywne+
- Porównanie
<
operatorów , ,>
<=
i>=
- Równość
==
i!=
operatory
Użyj nawiasów, ()
, aby zmienić kolejność oceny narzuconą przez pierwszeństwo operatora.
Aby uzyskać pełną listę operatorów języka C# uporządkowanych według poziomu pierwszeństwa, zobacz sekcję Pierwszeństwo operatora w artykule Operatory języka C#.
Przeciążenie operatora
Typ zdefiniowany przez użytkownika nie może przeciążyć operatorów &
powiązanych ze wskaźnikiem , *
, ->
i []
.
specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz następujące sekcje specyfikacji języka C#:
- Zmienne stałe i przenoszone
- Operator address-of
- Pośredni wskaźnik
- Dostęp do elementu członkowskiego wskaźnika
- Dostęp do elementu wskaźnika
- Arytmetyka wskaźnika
- Inkrementacja i dekrementacja wskaźnika
- Porównanie wskaźników