- 使用指针的前提条件
- 必须在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]; } } }
- 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);
static int localMultiply(int x, int y) => x * y; int product = UnsafeCombine(&localMultiply, 3, 4);
- 函数只能在unsafe{}中声明
- 只能在unsafe中调用delegate*
- 只能在static函数中用&获取函数地址
- 作者:Reguluz
- 链接:https://reguluz.site/article/c-sharp-unsafe-1
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。