不安全

unsafe 關鍵字可用於型別或方法宣告或宣告內聯塊。

此關鍵字的目的是為相關塊啟用 C# 的不安全子集。不安全子集包括指標,堆疊分配,類 C 陣列等功能。

不安全的程式碼是不可驗證的,這就是為什麼不鼓勵使用它的原因。編譯不安全程式碼需要將開關傳遞給 C#編譯器。此外,CLR 要求正在執行的程式集具有完全信任。

儘管有這些限制,但不安全的程式碼在使某些操作更高效(例如陣列索引)或更容易(例如與一些非託管庫互操作)方面具有有效的用法。

作為一個非常簡單的例子

// compile with /unsafe
class UnsafeTest
{
   unsafe static void SquarePtrParam(int* p)
   {
      *p *= *p; // the '*' dereferences the pointer.
      //Since we passed in "the address of i", this becomes "i *= i"
   }

   unsafe static void Main()
   {
      int i = 5;
      // Unsafe method: uses address-of operator (&):
      SquarePtrParam(&i); // "&i" means "the address of i". The behavior is similar to "ref i"
      Console.WriteLine(i); // Output: 25
   }
}

在使用指標時,我們可以直接更改記憶體位置的值,而不必通過名稱來解決它們。請注意,這通常需要使用 fixed 關鍵字來防止可能的記憶體損壞,因為垃圾收集器會移動東西(否則,你可能會收到錯誤 CS0212 )。由於無法寫入已經修復的變數,我們通常還必須有一個第二個指標,該指標開始指向與第一個相同的位置。

void Main()
{
    int[] intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    UnsafeSquareArray(intArray);
    foreach(int i in intArray)
        Console.WriteLine(i);
}

unsafe static void UnsafeSquareArray(int[] pArr)
{
    int len = pArr.Length;

    //in C or C++, we could say
    // int* a = &(pArr[0])
    // however, C# requires you to "fix" the variable first 
    fixed(int* fixedPointer = &(pArr[0]))
    {
        //Declare a new int pointer because "fixedPointer" cannot be written to.
        // "p" points to the same address space, but we can modify it
        int* p = fixedPointer;

        for (int i = 0; i < len; i++)
        {
            *p *= *p; //square the value, just like we did in SquarePtrParam, above
            p++;      //move the pointer to the next memory space.
                      // NOTE that the pointer will move 4 bytes since "p" is an
                      // int pointer and an int takes 4 bytes

            //the above 2 lines could be written as one, like this:
            // "*p *= *p++;"
        }
    }
}

輸出:

1
4
9
16
25
36
49
64
81
100

unsafe 還允許使用 stackalloc ,它將在 C 執行時庫中像_alloca 一樣在堆疊上分配記憶體。我們可以修改上面的例子來使用 stackalloc 如下:

unsafe void Main()
{
    const int len=10;
    int* seedArray = stackalloc int[len];
    
    //We can no longer use the initializer "{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}" as before.
    // We have at least 2 options to populate the array. The end result of either
    // option will be the same (doing both will also be the same here).

    //FIRST OPTION:
    int* p = seedArray; // we don't want to lose where the array starts, so we
                        // create a shadow copy of the pointer
    for(int i=1; i<=len; i++)
        *p++ = i;
    //end of first option

    //SECOND OPTION:
    for(int i=0; i<len; i++)
        seedArray[i] = i+1;
    //end of second option

    UnsafeSquareArray(seedArray, len);
    for(int i=0; i< len; i++)
        Console.WriteLine(seedArray[i]);
}

//Now that we are dealing directly in pointers, we don't need to mess around with
// "fixed", which dramatically simplifies the code
unsafe static void UnsafeSquareArray(int* p, int len)
{
    for (int i = 0; i < len; i++)
        *p *= *p++;
}

(輸出與上面相同)