C# Unsafe Kod ve Pointer Kullanımı
C#’ta unsafe kod ve pointer kullanımını öğrenin. Bellek adresleri, pointer işlemleri ve düşük seviye senaryolar örneklerle anlatılıyor.
C# dili güvenli (safe) bellek yönetimine sahip olsa da, bazı yüksek performans gerektiren senaryolarda
doğrudan belleğe erişim gerekebilir. Unsafe kod bu durumlar için kullanılır.
Unsafe bloklar, pointer (işaretçi) kullanarak bellek adreslerine doğrudan erişmenizi sağlar.
Bu yaklaşım düşük seviye kontrol imkânı sunsa da dikkatli kullanılmalıdır; çünkü .NET’in garbage collector (GC) korumalarının dışındadır.
Unsafe Kod Nedir?
Unsafe anahtar kelimesi, C# derleyicisine kodun yönetilmeyen (unmanaged) bellekle çalışacağını belirtir. Bu tür kodlarda derleyici, bellek güvenliği kontrollerini yapmaz. Unsafe kodu kullanabilmek için proje derleme ayarlarında “Allow unsafe code” seçeneği aktif olmalıdır.
// .csproj dosyasına eklenmesi gereken özellik:
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Pointer (İşaretçi) Nedir?
Pointer, bir değişkenin bellekteki adresini tutan değişkendir.
C#’ta pointer’lar yalnızca unsafe bloklar içinde tanımlanabilir ve stack üzerinde kullanılır.
* işareti pointer tanımı yapar, & operatörü ise bir değişkenin adresini alır.
using System;
class Program
{
static unsafe void Main()
{
int x = 42;
int* ptr = &x; // x'in bellekteki adresini al
Console.WriteLine($"x değeri: {*ptr}");
Console.WriteLine($"x adresi: {(ulong)ptr}");
}
}
*ptr ifadesi “adresin işaret ettiği değeri oku” anlamına gelir.
Bu örnekte hem değişkenin adresine hem değerine erişilir.
Pointer Üzerinden Değer Değiştirme
Pointer kullanarak bir değişkenin değerini doğrudan değiştirebilirsiniz. Bu işlem C/C++ dillerindeki pointer mantığıyla aynıdır.
unsafe
{
int x = 10;
int* p = &x;
*p = 99; // pointer üzerinden x değerini değiştir
Console.WriteLine($"x yeni değeri: {x}");
}
// Çıktı:
// x yeni değeri: 99
Pointer Arithmetic (İşaretçi Aritmetiği)
Pointer aritmetiği, pointer adreslerini artırarak dizilerde gezinmeyi sağlar.
Ancak yalnızca int, byte, double gibi ilkel türlerde kullanılabilir.
unsafe
{
int[] sayilar = { 10, 20, 30, 40 };
fixed (int* p = sayilar)
{
for (int i = 0; i < sayilar.Length; i++)
Console.WriteLine($"Adres: {(ulong)(p + i)}, Değer: {*(p + i)}");
}
}
fixed anahtar kelimesi, dizi belleğini GC’nin taşınamayacağı şekilde sabitler.
Böylece pointer her zaman doğru adresi gösterir.
fixed Anahtar Kelimesi
fixed, yönetilen bir nesneyi (örneğin dizi veya string) GC tarafından taşınamaz hale getirir.
Bu, pointer kullanarak güvenli şekilde bellek adreslerine erişmeyi sağlar.
unsafe
{
string mesaj = "Merhaba";
fixed (char* p = mesaj)
{
for (int i = 0; i < mesaj.Length; i++)
Console.Write(p[i]);
}
}
Dikkat: fixed bloğu bittiğinde nesne yeniden taşınabilir hale gelir,
bu yüzden pointer’ı bu blok dışında kullanmak güvenli değildir.
struct ve Pointer ile Bellek Erişimi
Pointer kullanarak struct’lar üzerinde de doğrudan belleğe erişim mümkündür. Bu, performans kritik durumlarda (ör. oyun motorları, grafik hesaplama, native interop) sık kullanılır.
using System;
struct Nokta
{
public int X;
public int Y;
}
class Program
{
static unsafe void Main()
{
Nokta n = new Nokta { X = 5, Y = 10 };
Nokta* ptr = &n;
ptr->X = 20;
Console.WriteLine($"Nokta: X={ptr->X}, Y={ptr->Y}");
}
}
ptr->X ifadesi C/C++’taki struct pointer erişim biçimidir.
stackalloc ile Stack Üzerinde Bellek Ayırma
stackalloc, stack üzerinde doğrudan ham bellek ayırır.
Bu bellek otomatik olarak scope bitiminde serbest bırakılır.
unsafe
{
int* p = stackalloc int[5];
for (int i = 0; i < 5; i++)
p[i] = i * 2;
for (int i = 0; i < 5; i++)
Console.WriteLine(p[i]);
}
stackalloc hızlıdır ancak yalnızca küçük verilerde kullanılmalıdır.
Büyük veriler için heap tahsisi (new) tercih edilmelidir.
Unsafe Kod Ne Zaman Kullanılır?
- Performansın kritik olduğu durumlarda (ör. matematiksel hesaplamalar, oyun motorları, görüntü işleme)
- Yönetilmeyen C/C++ kodlarıyla etkileşim (P/Invoke, Interop)
- Büyük diziler veya byte akışları üzerinde doğrudan bellek manipülasyonu gerektiğinde
- Fixed-size buffer yapıları veya custom memory layout gerektiren senaryolarda
Performans Karşılaştırması
Unsafe kod ile doğrudan bellek erişimi, GC’yi atladığı için genellikle %30–50 oranında performans kazandırabilir. Ancak bu kazanç, hata riskinin artması pahasına gelir. Yanlış adres erişimi, uygulamanın çökmesine veya bellek bozulmasına neden olabilir.
Dikkat Edilmesi Gerekenler
- Null pointer erişimi veya yanlış adres kullanımı
AccessViolationExceptionfırlatır. - Fixed blok dışında pointer’ı kullanmayın (GC nesneyi taşıyabilir).
- Unsafe kod, güvenlik açısından yönetilmeyen ortam sayılır — dikkatli denetlenmelidir.
- Uygulamanın tüm güvenlik modeli unsafe koda göre yeniden değerlendirilmelidir.
- Unsafe kod, partial trust (kısıtlı izin) altında çalışmaz.
Örnek: Görüntü Piksel Verisine Erişim
Aşağıdaki örnek, bir bitmap üzerindeki piksellere unsafe blokla doğrudan erişimi gösterir. Bu yöntem, piksel işleme (örneğin filtre uygulama) işlemlerini klasik API’lere göre çok daha hızlı yapar.
using System;
using System.Drawing;
using System.Drawing.Imaging;
class Program
{
static unsafe void Main()
{
using Bitmap bmp = new Bitmap("image.png");
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
int bytesPerPixel = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
byte* ilkPiksel = (byte*)data.Scan0;
for (int y = 0; y < bmp.Height; y++)
{
byte* satir = ilkPiksel + (y * data.Stride);
for (int x = 0; x < bmp.Width; x++)
{
byte* piksel = satir + (x * bytesPerPixel);
piksel[0] = (byte)(255 - piksel[0]); // Mavi kanalı tersine çevir
piksel[1] = (byte)(255 - piksel[1]); // Yeşil
piksel[2] = (byte)(255 - piksel[2]); // Kırmızı
}
}
bmp.UnlockBits(data);
bmp.Save("output.png");
Console.WriteLine("Görüntü ters renklere dönüştürüldü!");
}
}
Bu örnek, yüksek performanslı pixel manipulation işlemlerinde unsafe kodun gücünü gösterir.
TL;DR
- unsafe bloklar, C#’ta pointer ile doğrudan bellek erişimi sağlar.
- fixed anahtar kelimesi, GC’nin nesneleri taşımamasını sağlar.
- stackalloc, stack üzerinde hızlı, geçici bellek tahsisi yapar.
- Performans kazanımı sağlar, ancak güvenlik riskleri yüksektir.
- Yönetilmeyen kodlarla (C/C++) etkileşim veya düşük seviye bellek işlemlerinde kullanılır.
İlişkili Makaleler
C# Memory Management ve Garbage Collector
C#’ta memory management ve garbage collector yapısını öğrenin. Bellek yaşam döngüsü, tahsis ve temizlik süreçleri anlatılıyor.
C# Span<T> ve Memory<T> ile Performans Optimizasyonu
C#’ta Span<T> ve Memory<T> ile performans optimizasyonunu öğrenin. Bellek yönetimi ve yüksek performanslı veri işlemleri örneklerle.
C# Struct (Yapılar) – Class ile Farkları
C#’ta struct ve class arasındaki farkları öğrenin. Bellek modeli, kalıtım, boxing ve performans karşılaştırmalarıyla açıklanıyor.
C# Temel Veri Tipleri
C#’ta temel veri tipleri: sayısal, metinsel, mantıksal, nesne tabanlı ve nullable tiplerin kullanımı.