ภาพรวม
ใน C# การใช้ Pointer ต้องใช้คีย์เวิร์ด unsafe และการเข้าถึงอาร์เรย์ด้วย Pointer ต้องใช้คีย์เวิร์ด fixed เพื่อ ป้องกัน Garbage Collector ย้ายหน่วยความจำ ระหว่างการใช้งาน Pointer เนื่องจากชนิดข้อมูลอาร์เรย์ (int[]) และ Pointer (int*) ไม่ใช่ชนิดเดียวกัน
ตัวอย่างโปรแกรม
using System;
namespace PointerDemo
{
class Example
{
public unsafe static void Main()
{
int[] array = { 55, 23, 90, 76, 9, 57, 18, 89, 23, 5 };
int n = array.Length;
// fixed: ทำให้อาร์เรย์ไม่ถูกย้ายโดย GC
fixed (int* ptr = array)
{
for (int i = 0; i < n; i++)
{
// *(ptr + i) = dereference pointer ที่ตำแหน่ง i
Console.WriteLine("array[{0}] = {1}", i, *(ptr + i));
}
}
}
}
}ผลลัพธ์
array[0] = 55 array[1] = 23 array[2] = 90 array[3] = 76 array[4] = 9 array[5] = 57 array[6] = 18 array[7] = 89 array[8] = 23 array[9] = 5
คำอธิบายโค้ดสำคัญ
1. คีย์เวิร์ด unsafe
ระบุว่าเมธอด/บล็อกนี้ใช้ pointer ต้องคอมไพล์ด้วยตัวเลือก /unsafe (ใน Project Properties → Build → Allow unsafe code)
2. คีย์เวิร์ด fixed
fixed (int* ptr = array)
ทำหน้าที่: Pin อาร์เรย์ไว้ในหน่วยความจำ (prevent relocation by GC) และให้ pointer ptr ชี้ไปที่องค์ประกอบแรก
3. Pointer Arithmetic
*(ptr + i)
เทียบเท่ากับ array[i] แต่เข้าถึงผ่านหน่วยความจำโดยตรง ptr + i จะชี้ไปที่บิตที่ i ของชนิด int (คือ i * 4 ไบต์)
การคอมไพล์
ใน Visual Studio: Project → Properties → Build → ☑ Allow unsafe code
ใน command line: csc /unsafe Program.cs
ข้อสังเกตและคำเตือน
- ความปลอดภัย: Pointer ข้ามขอบเขต array bounds checking ได้ → อาจทำให้ Access Violation
- GC Pressure:
fixedบล็อกการย้ายหน่วยความจำ → อาจทำให้ Heap แยกส่วน (fragmentation) - ใช้เฉพาะเมื่อจำเป็น: Interop, High-performance computing, Image processing, Network packet parsing
- Alternative: ใช้
Span<T>,Memory<T>, หรือArraySegment<T>สำหรับการเข้าถึงหน่วยความจำแบบปลอดภัยและมีประสิทธิภาพ
ตัวอย่างสมัยใหม่: ใช้ Span<T> (แนะนำ)
using System;
public class Demo
{
public static void Main()
{
int[] array = { 55, 23, 90, 76, 9, 57, 18, 89, 23, 5 };
// Span - safe, fast, no GC pinning needed
Span span = array;
for (int i = 0; i < span.Length; i++)
Console.WriteLine($"array[{i}] = {span[i]}");
// สามารถ slice ได้โดยไม่ copy
Span slice = span.Slice(2, 3); // 90, 76, 9
}
}