Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Операторы указателя позволяют принимать адрес переменной (), разыменовывать указатель (&*), сравнивать значения указателя и добавлять или вычитать указатели и целые числа.
Для работы с указателями используются следующие операторы:
- Унарный оператор
&(address-of): для получения адреса переменной - Унарный оператор
*(косвенное обращение к указателю): для получения переменной, на которую указывает указатель - Операторы
->(доступ к членам) и[](доступ к элементам) - Арифметические операторы
+,-,++и-- - Операторы сравнения
==,!=,<,>,<=и>=
Сведения о типах указателей см. в разделе Типы указателей.
Примечание.
Для всех операций с указателями требуется небезопасный контекст. Код, содержащий небезопасные блоки, должен компилироваться с параметром компилятора AllowUnsafeBlocks.
Оператор address-of >
Унарный оператор & возвращает адрес своего операнда:
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
Операнд оператора & должен быть фиксированной переменной.
Фиксированные переменные — это переменные в местах хранения, на которые не распространяется сборка мусора. В приведенном выше примере локальная переменная number — это фиксированная переменная, так как она находится в стеке. Переменные, которые находятся в местах хранения, обслуживаемых сборщиком мусора (например, перемещение), называются перемещаемыми переменными. Поля объекта и элементы массива являются примерами перемещаемых переменных. Вы можете получить адрес перемещаемой переменной, если зафиксируете или закрепите ее с помощью инструкции fixed. Полученный адрес действителен только в пределах блока инструкции fixed. В следующем примере показано, как использовать инструкцию fixed и оператор &:
unsafe
{
byte[] bytes = { 1, 2, 3 };
fixed (byte* pointerToFirst = &bytes[0])
{
// The address stored in pointerToFirst
// is valid only inside this fixed statement block.
}
}
Получить адрес константы или значения нельзя.
Дополнительные сведения о фиксированных и перемещаемых переменных см. в разделе Фиксированные и перемещаемые переменные в спецификации языка C#.
Бинарный оператор & вычисляет логическое И своих логических операндов или побитовое логическое И своих целочисленных операндов.
Оператор косвенного обращения указателя *
Унарный оператор косвенного обращения указателя * получает переменную, на которую указывает операнд. Он также называется оператором разыменования. Операнд оператора * должен иметь тип указателя.
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
Оператор нельзя применить * к выражению типа void*.
Бинарный оператор * вычисляет продукт своих числовых операндов.
Оператор доступа к элементу указателя —>
Оператор -> объединяет косвенное обращение к указателю и доступ к члену. То есть если x — это указатель типа T*, а y является доступным членом типа T, выражение формы
x->y
эквивалентно правилу
(*x).y
В следующем примере иллюстрируется использование оператора ->.
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)
}
}
Оператор нельзя применить -> к выражению типа void*.
Оператор доступа к элементу указателя []
Для выражения p типа указателя доступ к элементу указателя формы p[n] вычисляется как *(p + n), где n должен иметь тип, неявно преобразуемый в int, uint, long или ulong. Сведения о поведении оператора + с указателями см. в разделе Сложение целочисленного значения с указателем или его вычитание из указателя.
Следующий пример демонстрирует доступ к элементам массива с указателем и оператором []:
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
В предыдущем примере выражение stackalloc выделяет блок памяти в стеке.
Примечание.
Оператор доступа к элементу указателя не проверяет ошибки за пределами области.
Невозможно использовать [] для доступа к элементу указателя с выражением типа void*.
Можно также использовать оператор [] для доступа к элементу массива или индексатору.
Арифметические операторы указателя
Вы можете выполнить следующие арифметические операции с указателями:
- Сложение целочисленного значения с указателем или его вычитание из указателя
- Вычитание двух указателей
- Инкремент или декремент указателя
Эти операции нельзя выполнять с указателями типа void*.
Сведения о поддерживаемых арифметических операциях с числовыми типами см. в разделе Арифметические операторы.
Сложение целочисленного значения с указателем или его вычитание из указателя
Для указателя p типа T* и выражения n типа, неявно преобразуемого в int, uint, long или ulong, сложение и вычитание определяются следующим образом:
- Выражения
p + nиn + pсоздают указатель типаT*, полученный добавлениемn * sizeof(T)к адресу, предоставленномуp. - Выражение
p - nсоздает указатель типаT*, полученный вычитаниемn * sizeof(T)из адреса, предоставленногоp.
Оператор sizeof получает размер типа в байтах.
В следующем примере показано использование оператора + с указателем:
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
Вычитание указателей
Для двух указателей p1 и p2 типа T* выражение p1 - p2 находит разность между адресами, предоставленными p1 и p2, деленную на sizeof(T). Результат имеет тип long. То есть p1 - p2 вычисляется как ((long)(p1) - (long)(p2)) / sizeof(T).
В следующем примере показано вычитание указателей:
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
}
Инкремент и декремент указателя
Оператор инкремента ++добавляет 1 к своему операнду указателя. Оператор декремента --вычитает 1 из своего операнда указателя.
Оба оператора поддерживаются в двух формах: постфикс (p++ и p--) и префикс (++p и --p). Результат p++ и p-- — это значение pперед операцией. Результат ++p и --p — это значение pпосле операции.
В следующем примере показано поведение постфиксных и префиксных операторов инкремента:
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
Операторы сравнения указателей
Вы можете использовать операторы ==, !=, <, >, <= и >= для сравнения операндов любого типа указателя, включая void*. Эти операторы сравнивают адреса, заданные двумя операндами, как если бы они не были назначены целыми числами.
Сведения о поведении этих операторов для операндов других типов см. в статьях Операторы равенства и Операторы сравнения.
Приоритет операторов
В следующем списке операторы, связанные с указателями, упорядочены от самого высокого до самого низкого приоритета:
- Постфиксные операторы инкремента
x++и декрементаx--и операторы->и[] - Префиксные операторы инкремента
++xи декремента--xи операторы&и* - Аддитивные операторы
+и- - Операторы сравнения
<,>,<=и>= - Операторы равенства
==и!=
Используйте скобки (), чтобы изменить порядок вычисления, накладываемый приоритетом операторов.
Полный список операторов C#, упорядоченный по уровню приоритета, можно найти в разделе Приоритет операторов статьи Операторы C#.
Возможность перегрузки оператора
Определяемый пользователем тип не может перегружать связанные операторы &указателя , *и ->[].
Спецификация языка C#
Дополнительные сведения см. в следующих разделах статьи Спецификация языка C#:
- Фиксированные и перемещаемые переменные
- Оператор address-of
- Косвенное обращение к указателю
- Доступ к членам указателей
- Доступ к элементам указателей
- Расчеты указателей
- Инкремент и декремент указателя
- Сравнение указателей