type
Post
status
Published
date
Jan 22, 2024
slug
c-sharp-unsafe-1
summary
tags
思考
category
学习思考
icon
password
  • 使用指针的前提条件
    • 必须在unsafe上下文中
    • 必须使用AllowUnsafeBlocks编译器(Unity中为项目设置打开 Allow ‘unsafe’ code 选项)
    • 写作 引用类型* (type* identifier 或者未知引用类型的 void* identifier)
    • 引用类型必须为非托管类型
  • 使用fixed语句防止GC重新定位可移动变量,并声明指向该变量的指针
    • 固定变量的地址在语句的持续时间内不会更改
    • 只能在相应的fixed语句中使用声明的指针,该指针是只读的
    • 由于fixed语句中定义的指针不能动,可以在fixed域内使用另一个指针指向该指针进行移动in
      • int[] example = [10,20,30,40]; unsafe { fixed(int* staticP = &example[0]) //staticP固定指向数组头 { int* dynamicP = staticP;//先声明一个动态指针指向数组头的静态指针 Console.WriteLine(*dynamicP); //输出10 dynamicP += 1;//一个int的尺寸 Console.WriteLine(*dynamicP); //输出20 } }
  • void* p由于不知道目标数据类型,不能用*p来获取内容,但是可以通过强制转换到其他指定类型来获取,如(int*)p
  • 方法间传递指针会导致未定义的行为。考虑使用in\out\ref或是以函数结果的方式,将指针传至区域变量。如果已经在静态区域中设置指针,则该指针指向的变量可能是不固定的
  • 运算符
    • 运算符
      解释
      样例
      结果
      *
      执行指针间接取值
      *p
      指针p指向的数据内容
      ->
      透过指针存取结构的成员
      p->t
      相当于(*p).t,即获得指针p指向的数据类型中的t变量的数据
      []
      索引指针
      *p[10]
      指针数组p的第十个指针指向的数据内容
      &
      指针地址
      &p
      返回指针p的地址
      逻辑运算符(==等)
      判断指针地址逻辑
      p==t
      p指针指向地址是否等于t
      stackalloc
      在堆栈上配置内存
      Span<int> number = stackalloc int[length];
      方法中使用,在堆上分配内存块,在方法返回时自动丢弃。不能显式释放使用stackalloc分配的内存,此内存块不受GC影响,也不需要fixed固定。 分配给Span<T>或者ReadOnlySpan<T>时不需要unsafe
      fixed语句
      临时固定变量以找到其地址
  • fixed关键字用于创建数据结构中固定大小的数组的缓冲区,用法:private fixed char name[30]
    • C#的安全代码声明的数组是不包含数组内元素的,只有对这些元素的引用
    • 在安全代码里, struct中如果有可变数组,struct的大小并不依赖数组长度,因为在这里数组是引用
    • 结构可以在不安全代码中包含嵌入的固定大小数组,即fixed修饰的数组可以放在unsafe struct中
      • 此时使用fixed语句获取指向第一个元素的指针,即下面代码中charPtr指向fixedBuffer的第一个元素指针
      • unsafe struct Buffer { public fixed char fixedBuffer[128]; } unsafe class Example { public Buffer buffer; } void Test() { unsafe { fixed(char* charPtr = example.buffer.fixedBuffer) { *charPtr = 'A'; } } }
    • 固定大小的缓冲区相比常规数组的特性
      • 只能用于unsafe
      • 只能是结构的实例字段
      • 始终是矢量或一维数组
      • 声明应包括长度
    • 使用指针复制字节数组
      • unsafe关键字+Copy方法+fixed语句声明指向源数组与目标数组的指针并固定在内存中以避免被GC
      • static unsafe void Copy (byte[] src, int srcOffset, byte[] tar, int tarOffset, int count) { if(src==null || tar==null) throw Exception(); if(srcOffset<0 || tarOffset<0 || count<0) throw Exception(); if(src.length-srcOffset<count || tar.length-tarOffset<count) throw Exception(); fixed(byte* pSrc=src, pTar = tar) { for(int i=0; i<count; i++) { pTar[tarOffset+i]=pSrc[srcOffset+i]; } } }
    • C#提供delegate定义安全函数指针对象,调用委托时需要实例化委托拍省得类型并虚拟方法调用Invoke,该方式使用IL指令callvirt
      • IL指令calli效率更高,需要改用delegate* 实现,以下是两种不同实现
        • public static T Combine<T>(Func<T, T, T> combinator, T left, T right) => combinator(left, right); public static T UnsafeCombine<T>(delegate*<T, T, T> combinator, T left, T right) => combinator(left, right);
      • 如何使用Unsafe的高效版本
        • static int localMultiply(int x, int y) => x * y; int product = UnsafeCombine(&localMultiply, 3, 4);
      • 规则
        • 函数只能在unsafe{}中声明
        • 只能在unsafe中调用delegate*
        • 只能在static函数中用&获取函数地址
      • delegate* 表示声明是函数指针,方法组分配给函数指针时,&表示操作采用方法的地址
      • 使用managed和unmanaged为delegate* 指定 调用约定(?)
LocalKeyword与结构体传递的平台差异问题Unity环境光参数更新问题