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:

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, , uintlonglub 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, longlub ulong, dodawanie i odejmowanie są definiowane T* w następujący sposób:

  • Oba p + n wyrażenia i n + p generują wskaźnik typu T* , który wynika z dodawania n * sizeof(T) do adresu podanego przez pelement .
  • Wyrażenie p - n generuje wskaźnik typu T* , który wynika z odejmowania n * sizeof(T) z adresu podanego przez p.

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ą pprzed operacją. Wynik ++p i --p jest wartością ppo 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#:

Zobacz też