Шрифт:
Интервал:
Закладка:
static unsafe void Main(string[] args)
{
int myInt2 = 5;
SquareIntPointer(&myInt2);
Console.WriteLine("myInt: {0}", myInt2);
}
Запустив такую версию кода, вы получите следующий вывод:
myInt: 25
На заметку! Важно отметить, что термин "небезопасный" был выбран небезосновательно. Прямой доступ к стеку и работа с указателями может приводить к неожиданным проблемам с вашим приложением, а также с компьютером, на котором оно функционирует. Если вам приходится иметь дело с небезопасным кодом, тогда будьте крайне внимательны.
Работа с операциями * и &
После установления небезопасного контекста можно строить указатели и типы данных с помощью операции *, а также получать адрес указываемых данных посредством операции &. В отличие от С или C++ в языке C# операция * применяется только к лежащему в основе типу, а не является префиксом имени каждой переменной указателя. Например, взгляните на показанный далее код, демонстрирующий правильный и неправильный способы объявления указателей на целочисленные переменные:
// Нет! В 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 123
Address of myInt 90F7E698
Небезопасная (и безопасная) функция обмена
Разумеется, объявлять указатели на локальные переменные, чтобы просто присваивать им значения (как в предыдущем примере), никогда не понадобится и к тому же неудобно. В качестве более практичного примера небезопасного кода предположим, что необходимо построить функцию обмена с использованием арифметики указателей:
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})unsafe static void UnsafeSwap(int* i, int* j)
{
int temp = *i;
*i = *j;
*j = temp;
}
Очень похоже на язык С, не так ли? Тем не менее, учитывая предшествующую работу, вы должны знать, что можно было бы написать безопасную версию алгоритма обмена с применением ключевого слова ref языка С#:
static 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();
Доступ к полям через указатели (операция ->)
Теперь предположим, что определена простая безопасная структура Point:
struct Point
{
public int x;
public int y;
public override string ToString() => $"({x}, {y})";
}
В случае объявления указателя на тип Point для доступа к открытым членам структуры понадобится применять операцию доступа к полям (имеющую вид ->). Как упоминалось в табл. 11.2, она представляет собой небезопасную версию стандартной (безопасной) операции точки (.). В сущности, используя операцию обращения к указателю (*), можно разыменовывать указатель для применения операции точки. Взгляните на следующий небезопасный метод:
- Понимание SQL - Мартин Грубер - Базы данных