Язык программирования C#9 и платформа .NET5. Страница 231
static unsafe void Main(string[] args){ int myInt2 = 5; SquareIntPointer(&myInt2); Console.WriteLine("myInt: {0}", myInt2);}Запустив такую версию кода, вы получите следующий вывод:
myInt: 25На заметку! Важно отметить, что термин "небезопасный" был выбран небезосновательно. Прямой доступ к стеку и работа с указателями может приводить к неожиданным проблемам с вашим приложением, а также с компьютером, на котором оно функционирует. Если вам приходится иметь дело с небезопасным кодом, тогда будьте крайне внимательны.
Работа с операциями * и &
После установления небезопасного контекста можно строить указатели и типы данных с помощью операции
*&*// Нет! В C# это некорректно!int *pi, *pj;// Да! Так поступают в С#.int* pi, pj;Рассмотрим следующий небезопасный метод:
static unsafe void PrintValueAndAddress(){ int myInt; // Определить указатель на int и присвоить ему адрес myInt. int* ptrToMyInt = &myInt; // Присвоить значение myInt, используя обращение через указатель. *ptrToMyInt = 123; // Вывести некоторые значения. Console.WriteLine("Value of myInt {0}", myInt); // значение myInt Console.WriteLine("Address of myInt {0:X}", (int)&ptrToMyInt); // адрес myInt}В результате запуска этого метода из блока
unsafe**** Print Value And Address ****Value of myInt 123Address of myInt 90F7E698Небезопасная (и безопасная) функция обмена
Разумеется, объявлять указатели на локальные переменные, чтобы просто присваивать им значения (как в предыдущем примере), никогда не понадобится и к тому же неудобно. В качестве более практичного примера небезопасного кода предположим, что необходимо построить функцию обмена с использованием арифметики указателей:
unsafe static void UnsafeSwap(int* i, int* j){ int temp = *i; *i = *j; *j = temp;}Очень похоже на язык С, не так ли? Тем не менее, учитывая предшествующую работу, вы должны знать, что можно было бы написать безопасную версию алгоритма обмена с применением ключевого слова
refstatic void SafeSwap(ref int i, ref int j){ int temp = i; i = j; j = temp;}Функциональность обеих версий метода идентична, доказывая тем самым, что прямые манипуляции указателями в C# не являются обязательными. Ниже показана логика вызова, использующая безопасные операторы верхнего уровня, но с небезопасным контекстом:
Console.WriteLine("***** Calling method with unsafe code *****");// Значения, подлежащие обмену.int i = 10, j = 20;<b>// "Безопасный" обмен значений местами.</b>Console.WriteLine("\n***** Safe swap *****");Console.WriteLine("Values before safe swap: i = {0}, j = {1}", i, j);SafeSwap(ref i, ref j);Console.WriteLine("Values after safe swap: i = {0}, j = {1}", i, j);<b>// "Небезопасный" обмен значений местами.</b>Console.WriteLine("\n***** Unsafe swap *****");Console.WriteLine("Values before unsafe swap: i = {0}, j = {1}", i, j);unsafe { UnsafeSwap(&i, &j); }Console.WriteLine("Values after unsafe swap: i = {0}, j = {1}", i, j);Console.ReadLine();Доступ к полям через указатели (операция ->)
Теперь предположим, что определена простая безопасная структура
Pointstruct Point{ public int x; public int y; public override string ToString() => $"({x}, {y})"; } В случае объявления указателя на тип
Point->.*