Операторы, связанные с указателем: получение адреса переменных, расположения хранилища разыменовки и доступ к расположениям памяти
Статья
Операторы указателя позволяют принимать адрес переменной (), разыменовывать указатель (&*), сравнивать значения указателя и добавлять или вычитать указатели и целые числа.
Для работы с указателями используются следующие операторы:
Унарный оператор & (address-of): для получения адреса переменной
Для всех операций с указателями требуется небезопасный контекст. Код, содержащий небезопасные блоки, должен компилироваться с параметром компилятора AllowUnsafeBlocks.
Оператор address-of >
Унарный оператор & возвращает адрес своего операнда:
C#
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 и оператор &:
C#
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*.
Бинарный оператор * вычисляет продукт своих числовых операндов.
Оператор нельзя применить -> к выражению типа void*.
Оператор доступа к элементу указателя []
Для выражения p типа указателя доступ к элементу указателя формы p[n] вычисляется как *(p + n), где n должен иметь тип, неявно преобразуемый в int, uint, long или ulong. Сведения о поведении оператора + с указателями см. в разделе Сложение целочисленного значения с указателем или его вычитание из указателя.
Следующий пример демонстрирует доступ к элементам массива с указателем и оператором []:
C#
unsafe
{
char* pointerToChars = stackallocchar[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
В следующем примере показано использование оператора + с указателем:
C#
unsafe
{
constint Count = 3;
int[] numbers = newint[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).
В следующем примере показано вычитание указателей:
Оператор инкремента ++добавляет 1 к своему операнду указателя. Оператор декремента --вычитает 1 из своего операнда указателя.
Оба оператора поддерживаются в двух формах: постфикс (p++ и p--) и префикс (++p и --p). Результат p++ и p-- — это значение pперед операцией. Результат ++p и --p — это значение pпосле операции.
В следующем примере показано поведение постфиксных и префиксных операторов инкремента:
C#
unsafe
{
int* numbers = stackallocint[] { 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*. Эти операторы сравнивают адреса, заданные двумя операндами, как если бы они не были назначены целыми числами.
Источник этого содержимого можно найти на GitHub, где также можно создавать и просматривать проблемы и запросы на вытягивание. Дополнительные сведения см. в нашем руководстве для участников.
Отзыв о .NET
.NET — это проект с открытым исходным кодом. Выберите ссылку, чтобы оставить отзыв:
Присоединитесь к серии встреч для создания масштабируемых решений искусственного интеллекта на основе реальных вариантов использования с другими разработчиками и экспертами.