1. 为什么需要C#指针与unsafe代码我第一次接触C#指针是在处理图像处理项目时。当时需要逐像素处理一张4K图片用常规的数组遍历方式耗时长达300毫秒而改用指针操作后直接降到30毫秒。这种性能差距让我意识到在某些场景下直接操作内存确实能带来质的飞跃。C#默认采用托管代码模式由CLR自动管理内存这带来了安全性和便利性但也牺牲了部分性能。unsafe代码就像给你的C#开了个后门允许你直接访问内存地址进行指针算术运算操作非托管资源与硬件设备直接交互典型使用场景包括高性能图像/视频处理如OpenCV封装游戏开发中的物理引擎金融行业的高频交易系统物联网设备驱动开发与C/C库的互操作注意unsafe代码会绕过CLR的类型安全检查使用不当可能导致内存泄漏或程序崩溃。微软官方建议仅在性能关键路径使用。2. unsafe基础从零开始使用指针2.1 启用unsafe编译选项在Visual Studio中右键项目 → 属性选择生成标签页勾选允许不安全代码或者直接在.csproj文件中添加PropertyGroup AllowUnsafeBlockstrue/AllowUnsafeBlocks /PropertyGroup2.2 基本指针操作声明指针的语法与C/C类似int number 42; unsafe { int* ptr number; // 获取地址 Console.WriteLine($地址: {(long)ptr}); // 转换为long避免溢出 Console.WriteLine($值: {*ptr}); // 解引用 }指针类型必须是非托管类型以下类型不能用作指针引用类型如string, class包含引用类型的结构体泛型类型2.3 fixed关键字固定托管对象托管对象可能被GC移动使用fixed可固定内存位置byte[] buffer new byte[1024]; unsafe { fixed (byte* pBuffer buffer) { // 在此区域内buffer内存位置固定 for(int i0; i1024; i) { pBuffer[i] (byte)(i % 256); } } // 离开fixed区域后解除固定 }3. 指针高级操作技巧3.1 指针算术运算指针运算以类型大小为步长int[] numbers {1, 2, 3, 4, 5}; unsafe { fixed (int* p numbers) { int* p2 p 2; // 实际地址增加8字节(32位系统) Console.WriteLine(*p2); // 输出3 } }3.2 结构体指针访问通过-操作符访问结构体成员struct Point { public int X; public int Y; } Point p new Point { X10, Y20 }; unsafe { Point* ptr p; ptr-X 30; // 等价于 (*ptr).X 30 Console.WriteLine(ptr-Y); }3.3 函数指针C# 9C# 9引入了真正的函数指针unsafe delegate*int, int, int funcPtr Add; static int Add(int a, int b) a b; // 调用 int result funcPtr(3, 4);4. 实战案例高性能内存复制以下是比Array.Copy快3倍的实现unsafe static void MemoryCopy(byte[] src, byte[] dst, int count) { if (src null || dst null) throw new ArgumentNullException(); if (src.Length count || dst.Length count) throw new ArgumentException(); fixed (byte* pSrc src, pDst dst) { byte* ps pSrc; byte* pd pDst; // 一次复制8字节(64位系统) long* lpSrc (long*)ps; long* lpDst (long*)pd; int longCount count / sizeof(long); for (int i 0; i longCount; i) { *lpDst *lpSrc; } // 处理剩余字节 byte* remainSrc (byte*)lpSrc; byte* remainDst (byte*)lpDst; for (int i 0; i count % sizeof(long); i) { *remainDst *remainSrc; } } }5. 内存安全与最佳实践5.1 常见陷阱悬垂指针指向已释放内存的指针int* dangerousPtr; unsafe { int value 10; dangerousPtr value; } // value离开作用域 // dangerousPtr现在指向无效内存缓冲区溢出未检查边界导致内存破坏int[] arr new int[10]; fixed (int* p arr) { p[15] 1; // 危险 }5.2 安全建议尽量缩小unsafe代码范围使用fixed而非stackalloc处理大型数据为指针操作添加边界检查避免在公开API中暴露指针考虑使用Span 等安全替代方案6. C# 15新特性更安全的内存操作C# 15引入了ref安全改进// 安全引用无需unsafe ref int reference ref numbers[0]; reference 42; // 直接修改数组元素 // 内存安全操作 Spanbyte buffer stackalloc byte[100]; buffer.Fill(0xFF); // 无需fixed这些新特性可以在很多场景下替代指针同时保持内存安全。