Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Операторы указателя позволяют принимать адрес переменной (), разыменовывать указатель (&*), сравнивать значения указателя и добавлять или вычитать указатели и целые числа.
Для работы с указателями используются следующие операторы:
- Унарный оператор
&(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
- Косвенное обращение к указателю
- Доступ к членам указателей
- Доступ к элементам указателей
- Расчеты указателей
- Инкремент и декремент указателя
- Сравнение указателей