C# 不安全代码unsafe方法 C#如何使用指针操作内存

来源:这里教程网 时间:2026-02-21 17:41:06 作者:

为什么C#里写
unsafe
代码会编译失败

默认情况下,C#编译器禁止不安全上下文,直接写指针会报错

CS0227: Unsafe code may only appear if compiling with /unsafe
。必须显式开启支持,否则连最基础的
int* p
都会被拦住。

解决方法只有两个:
• 项目文件(.csproj)里加

<allowunsafeblocks>true</allowunsafeblocks>

• 或命令行编译时加参数
/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
确认热点真在内存访问路径上。

相关推荐