Указатели

Последнее обновление: 19.02.2022

Если вы программировали на С/С++, то, возможно, вы знакомы с таким понятием как указатели. Указатели позволяют получить доступ к определенной ячейке памяти и произвести определенные манипуляции со значением, хранящимся в этой ячейке.

В языке C# указатели редко используются, однако в некоторых случаях можно прибегать к ним для оптимизации приложений. Код, применяющий указатели, еще называют небезопасным (unsafe) кодом. Однако это не значит, что он представляет какую-то опасность. Просто при работе с ним все действия по использованию памяти, в том числе по ее очистке, ложится целиком на нас, а не на среду CLR. И с точки зрения CLR такой код не безопасен, так как среда не может проверить данный код, поэтому повышается вероятность различного рода ошибок.

Чтобы использовать небезопасный код в C#, надо первым делом указать проекту, что он будет работать с небезопасным кодом. Для этого надо установить в настройках проекта соответствующий флаг - в меню Project (Проект) найти Свойства проекта. Затем в меню Build установить флажок Unsafe code (Небезопасный код):

установка флага unsafe в c#

Теперь мы можем приступать к работе с небезопасным кодом и указателями.

Ключевое слово unsafe

Блок кода или метод, в котором используются указатели, помечается ключевым словом unsafe:

// блок кода, использующий указатели
unsafe 
{
	
}

Метод, использующий указатели:

unsafe void Test()
{

}

Также с помощью unsafe можно объявлять структуры и классы:

unsafe struct State
{ 

}

unsafe class Person
{

}

Операции * и &

Ключевой при работе с указателями является операция *, которую еще называют операцией разыменовывания. Операция разыменовывания позволяет получить или установить значение по адресу, на который указывает указатель. Для получения адреса переменной применяется операция &:

unsafe
{
    int* x; // определение указателя
    int y = 10; // определяем переменную

    x = &y; // указатель x теперь указывает на адрес переменной y
    Console.WriteLine(*x); // 10

    y = y + 20;     // меняем значение
    Console.WriteLine(*x);// 30

    *x = 50;
    Console.WriteLine(y); // переменная y=50
}

При объявлении указателя указываем тип int* x; - в данном случае объявляется указатель на целое число. Но кроме типа int можно использовать и другие: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal или bool. Также можно объявлять указатели на типы enum, структуры и другие указатели.

Выражение x = &y; позволяет нам получить адрес переменной y и установить на него указатель x. До этого указатель x не на что не указывал.

После этого все операции с y будут влиять на значение, получаемое через указатель x и наоборот, так как они указывают на одну и ту же область в памяти.

Для получения значения, которое хранится в области памяти, на которую указывает указатель x, используется выражение *x.

Получение адреса

Используя преобразование указателя к целочисленному типу, можно получить адрес памяти, на который указывает указатель:

int* x; // определение указателя
int y = 10; // определяем переменную

x = &y; // указатель x теперь указывает на адрес переменной y

// получим адрес переменной y
ulong addr = (ulong)x;
Console.WriteLine($"Адрес переменной y: {addr}");

Для получения адреса используется преобразование в тип uint, long или ulong. Так как значение адреса - это целое число, а на 32-разрядных системах диапазон адресов 0 до 4 000 000 000, а адрес можно получить в переменную uint/int. Соответственно на 64-разрядных системах диапазон доступных адресов гораздо больше, поэтому в данном случае лучше использовать ulong, чтобы избежать ошибки переполнения.

Указатель на другой указатель

Объявление и использование указателя на указатель:

unsafe
{
    int* x; // определение указателя
    int y = 10; // определяем переменную

    x = &y; // указатель x теперь указывает на адрес переменной y
    int** z = &x; // указатель z теперь указывает на адрес, указателя x
    **z = **z + 40; // изменение указателя z повлечет изменение переменной y
    Console.WriteLine(y); // переменная y=50
    Console.WriteLine(**z); // переменная **z=50
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850