C#默认禁止unsafe代码,需显式启用;指针声明为T*,托管对象须用fixed固定或分配非托管内存;fixed仅在块内有效,指针不可逃逸;指针性能优势限于特定场景。

为什么C#里写unsafe代码会编译失败
默认情况下,C#编译器禁止不安全上下文,直接写指针会报错 CS0227: Unsafe code may only appear if compiling with /unsafe。必须显式开启支持,否则连最基础的int* p都会被拦住。
解决方法只有两个:
• 项目文件(.csproj)里加
• 或命令行编译时加参数 /unsafe
• Visual Studio 用户:右键项目 →「属性」→「生成」→ 勾选「允许不安全代码」
在unsafe块里怎么声明和初始化指针
指针类型语法是 T*,比如 int*、byte*,不能直接指向托管对象(如普通数组或类实例),除非先“固定”它。常见错误是试图这样写:int[] arr = new int[10]; int* p = arr;——这会编译失败,因为数组在GC堆上,地址可能随时变动。
正确做法分三步:
• 用 fixed 语句固定托管内存:fixed (int* p = arr) { /* 使用 p */ }
• 或分配非托管内存:int* p = (int*)Marshal.AllocHGlobal(10 * sizeof(int));(记得后续 Marshal.FreeHGlobal((IntPtr)p))
• 局部栈内存可用 stackalloc:int* p = stackalloc int[10];(无需手动释放,但仅限方法内有效)
fixed语句的生命周期和常见陷阱
fixed只在语句块内有效,离开作用域自动“解固定”。很多人误以为固定后可以保存指针到字段或返回给调用方,这是危险的——比如:private int* _ptr; void Foo() { fixed (int* p = arr) { _ptr = p; } },此时_ptr已指向可能被移动或回收的内存,后续读写大概率触发访问冲突或静默数据损坏。
安全边界很明确:
• fixed块内可传指针给其他unsafe方法,但不能逃逸出该块
• 若需长期持有,必须用 GCHandle.Alloc(arr, GCHandleType.Pinned) 手动固定,并自行管理释放
• stackalloc分配的内存随方法返回自动销毁,绝不可返回其地址
用指针操作数组比for循环快吗
简单连续遍历场景下,指针未必更快。现代JIT对for循环做了大量优化(如范围检查消除、向量化),而指针代码失去边界检查优势后,性能差异常可忽略,甚至因缺少内联或寄存器优化而更慢。
真正适合指针的场景是:
• 图像像素批量处理(如RGBA转灰度,需按字节偏移直接读写)
• 与C/C++库交互(接收void*参数,需强转为具体类型指针)
• 序列化/反序列化底层字节流(绕过BinaryReader封装开销)
• 高频小结构体数组的随机访问(避免每次取索引的装箱或引用间接)
一句话:别为了“看起来快”写unsafe,先用dotnet trace或PerfView确认热点真在内存访问路径上。










