C# 不安全代码

C# 不安全代码

当一个代码块使用 unsafe 修饰符标记时,C# 允许在函数中使用指针变量。不安全代码或非托管代码是指使用了指针变量的代码块。

指针变量

指针 是值为另一个变量的地址的变量,即,内存位置的直接地址。就像其他变量或常量,必须在使用指针存储其他变量地址之前声明指针。

指针变量声明的一般形式为:

type* var-name;

下面是指针类型声明的实例:

实例描述
int* p p 是指向整数的指针。
double* p p 是指向双精度数的指针。
float* p p 是指向浮点数的指针。
int** p p 是指向整数的指针的指针。
int*[] p p 是指向整数的指针的一维数组。
char* p p 是指向字符的指针。
void* p p 是指向未知类型的指针。

在同一个声明中声明多个指针时,星号 * 仅与基础类型一起写入;而不是用作每个指针名称的前缀。 例如:

int* p1, p2, p3;     // 正确  
int *p1, *p2, *p3;   // 错误 

下面的实例说明了 C# 中使用了 unsafe 修饰符时指针的使用:

使用指针检索数据值

可以使用 ToString() 方法检索存储在指针变量所引用位置的数据。

unsafe
            {
                bool val = true;
                bool* pointer = &val;
                Console.WriteLine($"val:{val}=val pointer ToString:{pointer->ToString()},address is:{(int)pointer}");
            }

传递指针作为方法的参数

可以向方法传递指针变量作为方法的参数。

public static void run()
        {
            unsafe
            {
                bool val = true;
                bool* pointer = &val;
                Console.WriteLine($"val:{val}=val pointer ToString:{pointer->ToString()},address is:{(int)pointer}");
                Console.WriteLine(getAddress(pointer)); 
            }
        }
        public static unsafe int getAddress(bool *pointer)
        {
            return (int)pointer;
        }

使用指针访问数组元素

在 C# 中,数组名称和一个指向与数组数据具有相同数据类型的指针是不同的变量类型。例如,int *p 和 int[] p 是不同的类型。可以增加指针变量 p,因为它在内存中不是固定的,但是数组地址在内存中是固定的,所以不能增加数组 p。

因此,如果需要使用指针变量访问数组数据,可以像通常在 C 或 C++ 中所做的那样,使用 fixed 关键字来固定指针。

 bool val = true;
                bool* pointer = &val;
                Console.WriteLine(*pointer);//简写ToString()?
                //如果需要使用指针变量访问数组数据
                //可以像通常在 C 或 C++ 中所做的那样,使用 fixed 关键字来固定指针。
                int[] vals = {11,45,14};
                int index = 0;
                fixed (int* valsPointer = vals)
                foreach(int v in vals)
                {
                    Console.WriteLine($"val:{v}=getVal={*(valsPointer + index)}=getPointer:{(valsPointer + index)->ToString()},address:{(int)(valsPointer+index)}");
                    index++;
                }

ixed关键字

由于C#中声明的变量在内存中的存储受垃圾回收器管理;因此一个变量(例如一个大数组)有可能在运行过程中被移动到内存中的其他位置。如果一个变量的内存地址会变化,那么指针也就没有意义了。

解决方法就是使用fixed关键字来固定变量位置不移动。

static unsafe void Main(string[] args)
{
  fixed(int *ptr = int[5])  {//...}
}

stackalloc //C#8.0+以上使用

在unsafe不安全环境中,可以通过stackalloc在堆栈上分配内存,因为在堆栈上分配的内存不受内存管理器管理,因此其相应的指针不需要固定。

static unsafe void Main(string[] args)
{
  int *ptr = stackalloc int[1] ;
}

 

编译不安全代码

为了编译不安全代码,必须切换到命令行编译器指定 /unsafe 命令行。

例如,为了编译包含不安全代码的名为 prog1.cs 的程序,需在命令行中输入命令:

csc /unsafe prog1.cs

如果使用的是 Visual Studio IDE,那么需要在项目属性中启用不安全代码。

步骤如下:

  • 通过双击资源管理器(Solution Explorer)中的属性(properties)节点,打开项目属性(project properties)
  • 点击 Build 标签页。
  • 选择选项"Allow unsafe code"。
posted @ 2020-08-28 12:31  虚青海  阅读(251)  评论(0)    收藏  举报