Приветствую всех, сегодня я покажу как можно использовать небезопасный код в сишарпе.
В небезопасном коде или другими словами неуправляемого кода можно объявлять и использовать указатели. Но вопрос в том, почему мы пишем неуправляемый код? Если мы хотим написать код, который взаимодействует с операционной системой, или хотим получить доступ к памяти отображаемого устройства или хотите реализовать критический алгоритм, использование указателя может дать много преимуществ.
Но есть некоторые недостатки использования указателей. Если указатели были выбраны 32-битных машина во время компиляции, то код будет ограничено 4gig адресного пространства, даже если бы это было работать на 64 битной машине. Если указатели были выбраны во время компиляции 64 бита, то код не может быть запущен на 32 битной машине.
Управляемый код является то, что код, который выполняется под контролем CLR. CLR отвечает за различные служебные задачи, как:
- управление памятью для объектов
- проверка исполнительского типа
- делает сбор мусора
Пользователь полностью изолирован из упомянутых выше задач. Пользователь не манипулирует памятью напрямую, потому что это делается с помощью CLR.
С другой стороны, неуправляемый код, это код , который выполняет вне контекста CLR. Лучшим примером этого является, наши традиционные Win32 DLL , как kernel32.dll , user32.dll , и COM — компоненты , установленные в нашей системе. Как они выделяют память для их использования, как освобождается память, как (если таковые имеются) тип проверки занимает места некоторые из задач, которые осуществляются ими самостоятельно. Программа типичной C ++ , который выделяет память для указателя символов является еще одним примером неуправляемого кода , потому что вы , как программист , несут ответственность за:
- вызов функции распределения памяти
- убедившись, что отливка сделано правильно
- убедившись, что память освобождается, когда работа сделана
Если вы заметили, вся эта забота производится с помощью CLR, как описано выше, избавляя программиста от бремени.
Написание небезопасного кода требует использования двух специальных ключевых слов: unsafe
и fixed
. Если вспомнить, что есть три вида операторов указателя:
*
&
->
В C # можно написать небезопасный код с модификатором unsafe
. Все небезопасный код должен быть четко обозначен с unsafe
модификатором.
Теперь давайте посмотрим на первый пример
1 2 3 4 5 6 7 8 9 10 |
using System; class MyClass { public static void Main() { int iData = 10; int* pData = &iData; Console.WriteLine("Data is " + iData); Console.WriteLine("Address is " + (int)pData ); } } |
В этом примере я использую указатель, однако при компиляции, компилятор выдает ошибку.
Все дело в том что нам необходимо использовать небезопасный модификатор метода unsafe
1 2 3 4 5 6 7 8 9 10 |
using System; class MyClass { public unsafe static void Main() { int iData = 10; int* pData = &iData; Console.WriteLine("Data is " + iData); Console.WriteLine("Address is " + (int)pData ); } } |
Однако не обязательно определять unsafe
модификатор с помощью метода. Мы можем определить блок небезопасного кода. Давайте изменим программу немного больше.
1 2 3 4 5 6 7 8 9 10 11 12 |
using System; class MyClass { public static void Main() { unsafe { int iData = 10; int* pData = &iData; Console.WriteLine("Data is " + iData); Console.WriteLine("Address is " + (int)pData ); } } } |
Как получить значение указателей?
В следующем примере мы рассмотрим код и возможность получить значения из указателей.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using System; class MyClass { public static void Main() { unsafe { int iData = 10; int* pData = &iData; Console.WriteLine("Data is " + iData); Console.WriteLine("Data is " + pData->ToString() ); Console.WriteLine("Address is " + (int)pData ); } } } |
Мы можем использовали ToString()
метод чтобы получить данные из указателя.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); Obj.testFun(); } } class TestClass { public unsafe void testFun() { int x = 10; int y = 20; Console.WriteLine("Before swap x = " + x + " y= " + y); swap(&x, &y); Console.WriteLine("After swap x = " + x + " y= " + y); } public unsafe void swap(int* p_x, int *p_y) { int temp = *p_x; *p_x = *p_y; *p_y = temp; } } |
В этой программе небезопасный методtestFun()
вызывает метод swap()
метод производит перестановку значения двух переменных , проходящих по ссылке. Теперь измените программу немного.
В следующем примере мы можем предыдущую программу модифицировать иначе:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); unsafe { int x = 10; int y = 20; Console.WriteLine("Before swap x = " + x + " y= " + y); Obj.swap(&x, &y); Console.WriteLine("After swap x = " + x + " y= " + y); } } } class TestClass { public unsafe void swap(int* p_x, int* p_y) { int temp = *p_x; *p_x = *p_y; *p_y = temp; } } |
Следующая программа отображает квадрат чисел от 0 до 9.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); Obj.fun(); } } class TestClass { public unsafe void fun() { int [] iArray = new int[10]; // store value in array for (int iIndex = 0; iIndex < 10; iIndex++) { iArray[iIndex] = iIndex * iIndex; } // get value from array for (int iIndex = 0; iIndex < 10; iIndex++) { Console.WriteLine(iArray[iIndex]); } } } |
Давайте изменим программу немного и будем передавать массив в качестве параметра функции.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); Obj.fun(); } } class TestClass { public unsafe void fun() { int [] iArray = new int[10]; // store value in array for (int iIndex = 0; iIndex < 10; iIndex++) { iArray[iIndex] = iIndex * iIndex; } testFun(iArray); } public unsafe void testFun(int [] p_iArray) { // get value from array for (int iIndex = 0; iIndex < 10; iIndex++) { Console.WriteLine(p_iArray[iIndex]); } } } |
Теперь давайте изменим программу и попытаемся получить значение массива из указателя, а не из индекса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<span class="code-keyword">using</span> System; <span class="code-keyword">class</span> MyClass { <span class="code-keyword">public</span> <span class="code-keyword">static</span> <span class="code-keyword">void</span> Main() { TestClass Obj = <span class="code-keyword">new</span> TestClass(); Obj.fun(); } } <span class="code-keyword">class</span> TestClass { <span class="code-keyword">public</span> <span class="code-keyword">unsafe</span> <span class="code-keyword">void</span> fun() { <span class="code-keyword">int</span> [] iArray = <span class="code-keyword">new</span> <span class="code-keyword">int</span>[<span class="code-digit">10</span>]; <span class="code-comment">//</span><span class="code-comment"> store value in array</span> <span class="code-keyword">for</span> (<span class="code-keyword">int</span> iIndex = <span class="code-digit">0</span>; iIndex <span class="code-keyword"><</span> <span class="code-digit">10</span>; iIndex++) { iArray[iIndex] = iIndex * iIndex; } <span class="code-comment">//</span><span class="code-comment"> get value from array</span> <span class="code-keyword">for</span> (<span class="code-keyword">int</span> iIndex = <span class="code-digit">0</span>; iIndex <span class="code-keyword"><</span> <span class="code-digit">10</span>; iIndex++) { Console.WriteLine(*(iArray + iIndex) ); } } } |
Вот в этой программе мы пытаемся получить доступ к значению массива из , *(iArray + iIndex)
а не iArray[iIndex]
. Но программа выдает сообщение об ошибке.
Все дело в том что в C # int*
и in[]
относится не к одному и тому же. Чтобы понять это давайте посмотрим еще одну программу.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); Obj.fun(); } } class TestClass { public unsafe void fun() { int [] iArray = new int[10]; iArray++; int* iPointer = (int*)0; iPointer++; } } |
Есть два различных типа переменных в этой программе. Во- первых, переменная iArray
объявляется массивом и вторая переменная iPointer
является переменной указателя. Теперь я собираюсь увеличивать обе переменных. Мы можем увеличивать переменную указатель , поскольку он не фиксируется в памяти , но мы не можем увеличивать iArray
, потому что начальный адрес массива хранится в iArray
и если бы нам позволили увеличить это , то мы потеряли бы начальный адрес массива.
Выход программы является ошибкой.
Чтобы получить доступ к элементу массива с помощью указателя мы должны зафиксировать указатель , поэтому он не может быть увеличен. C # использует fixed
зарезервное слово , чтобы сделать это.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); Obj.fun(); } } class TestClass { public unsafe void fun() { int [] iArray = new int[10]; // store value in array for (int iIndex = 0; iIndex < 10; iIndex++) { iArray[iIndex] = iIndex * iIndex; } // get value from array fixed(int* pInt = iArray) for (int iIndex = 0; iIndex < 10; iIndex++) { Console.WriteLine(*(pInt + iIndex) ); } } } |
Мы можем использовать тот же метод, чтобы передать массив в функцию, которая принимает указатель в качестве параметра.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); Obj.fun(); } } class TestClass { public unsafe void fun() { int [] iArray = new int[10]; // store value in array for (int iIndex = 0; iIndex < 10; iIndex++) { iArray[iIndex] = iIndex * iIndex; } // get value from array fixed(int* pInt = iArray) testFun(pInt); } public unsafe void testFun(int* p_pInt) { for (int iIndex = 0; iIndex < 10; iIndex++) { Console.WriteLine(*(p_pInt + iIndex) ); } } } |
Выход программы является такой же, как и предыдущий. Если попытаться получить доступ за пределы массива, то он будет сообщать об этом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using System; class MyClass { public static void Main() { TestClass Obj = new TestClass(); Obj.fun(); } } class TestClass { public unsafe void fun() { int [] iArray = new int[10]; // store value in array for (int iIndex = 0; iIndex < 10; iIndex++) { iArray[iIndex] = iIndex * iIndex; } // get value from array fixed(int* pInt = iArray) testFun(pInt); } public unsafe void testFun(int* p_pInt) { for (int iIndex = 0; iIndex < 20; iIndex++) { Console.WriteLine(*(p_pInt + iIndex) ); } } } |
Здесь мы попытаемся прочитать 20 элементов из массива, но есть только 10 элементов в массиве, так он будет выводить сообщение после вывода элементов массива.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
using System; struct Point { public int iX; public int iY; } class MyClass { public unsafe static void Main() { // reference of point Point refPoint = new Point(); refPoint.iX = 10; refPoint.iY = 20; // Pointer of point Point* pPoint = &refPoint; Console.WriteLine("X = " + pPoint->iX); Console.WriteLine("Y = " + pPoint->iY); Console.WriteLine("X = " + (*pPoint).iX); Console.WriteLine("Y = " + (*pPoint).iY); } } |
Вот pPoint
это указатель экземпляра класса Point. Мы можем получить доступ к элементу его с помощью ->
оператора.