C# 是型別安全的語言,但透過 unsafe 關鍵字可啟用指標操作,適用於高效能運算、互操作非託管代碼、影像處理等場景。本文系統整理指標語法、記憶體管理與實戰技巧。
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
// 類別層級
public unsafe class PointerDemo { }
// 方法層級
public unsafe void ProcessData(byte* data, int length) { }
// 區塊層級
public void SafeMethod()
{
unsafe
{
int* ptr = stackalloc int[10];
// ...
}
}
| 分類 | 支援型別 | 說明 |
|---|---|---|
| 實值型別 | sbyte, byte, short, ushort, int, uint, long, ulong, float, double, decimal, bool | 所有基本型別 |
| 列舉 | enum 基底型別為整數者 | 可轉換為對應整數指標 |
| 結構體 | 不包含參考型別欄位的 struct | 需為 unmanaged type |
| void* | 通用指標 | 不可解參考,僅用於傳遞/轉換 |
不支援: 類別、陣列、委派、字串、介面、含參考型別欄位的結構體。
public unsafe void StackAllocDemo()
{
// 配置 100 個 byte 於堆疊,自動釋放(方法返回時)
byte* buffer = stackalloc byte[100];
for (int i = 0; i < 100; i++)
buffer[i] = (byte)i;
// 可直接傳遞給非託管 API
ProcessBuffer(buffer, 100);
}
// 優點:零 GC 壓力、極快配置/釋放、適合小型暫存緩衝區
// 缺點:堆疊空間有限(預設 1MB),過大導致 StackOverflowException
public unsafe void FixedDemo()
{
byte[] managedArray = new byte[1000];
// 固定陣列位址,取得指標
fixed (byte* ptr = managedArray)
{
ptr[0] = 255;
ptr[999] = 128;
// 區塊結束自動解除固定
}
// 字串固定(唯讀)
string text = "Hello";
fixed (char* p = text)
{
Console.WriteLine(p[0]); // 'H'
}
}
public unsafe class UnmanagedMemory
{
private static readonly IntPtr _processHeap = GetProcessHeap();
private const uint HEAP_ZERO_MEMORY = 0x00000008;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetProcessHeap();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr HeapAlloc(IntPtr hHeap, uint dwFlags, IntPtr dwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr HeapReAlloc(IntPtr hHeap, uint dwFlags, IntPtr lpMem, IntPtr dwBytes);
public static void* Alloc(int size)
{
void* ptr = HeapAlloc(_processHeap, HEAP_ZERO_MEMORY, (IntPtr)size);
if (ptr == null) throw new OutOfMemoryException();
return ptr;
}
public static void Free(void* block)
{
if (!HeapFree(_processHeap, 0, (IntPtr)block))
throw new InvalidOperationException("HeapFree failed");
}
public static void* ReAlloc(void* block, int size)
{
void* ptr = HeapReAlloc(_processHeap, HEAP_ZERO_MEMORY, (IntPtr)block, (IntPtr)size);
if (ptr == null) throw new OutOfMemoryException();
return ptr;
}
}
public unsafe class FastCopy
{
// 使用指標逐 byte 複製,避免邊界檢查
public static void Copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count)
{
if (src == null || dst == null) throw new ArgumentNullException();
if (count < 0 || srcOffset + count > src.Length || dstOffset + count > dst.Length)
throw new ArgumentOutOfRangeException();
fixed (byte* pSrc = src, pDst = dst)
{
byte* s = pSrc + srcOffset;
byte* d = pDst + dstOffset;
// 向量化複製(簡易版)
if (s < d)
{
// 重疊時從後向前複製
s += count; d += count;
while (count-- > 0) *--d = *--s;
}
else
{
while (count-- > 0) *d++ = *s++;
}
}
}
}
.NET Core 2.1+ / .NET Standard 2.1+ 提供 Span<T> 與 Memory<T>,無需 unsafe 即可獲得類似效能:
// 安全、高效、可用於 managed/非託管記憶體
public void ModernCopy(Span src, Span dst)
{
src.CopyTo(dst); // 內部已優化向量化
}
// 配置非託管記憶體
var buffer = new NativeMemory(1024); // .NET 7+
// 或
var handle = Marshal.AllocHGlobal(1024);
var span = new Span(handle.ToPointer(), 1024);
Marshal.FreeHGlobal(handle);
指標賦予 C# 底層控制力,但在現代 .NET 中,Span<T>、Memory<T>、NativeMemory、Unsafe 類別已提供更安全的高效能替代方案。僅在互操作、極致效能調校、演算法移植時才考慮直接使用 unsafe 指標。