不安全

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++;
}

(输出与上面相同)