不安全
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++;
}
(输出与上面相同)