---
title: 'Boş ve Sarkan İşaretçiler'
source: 'https://academia.sh/tr/kurslar/c-bellek/bos-ve-sarkan-isaretciler'
course: "C'de Bellek ve İşaretçiler"
language: tr
updated: '2026-09-26T03:02:43+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Boş bir işaretçiyi dolaylamadan önce nasıl denetleyeceğini örnekle gösterir'
    - 'Sarkan bir işaretçinin bırakılmış bir bloğu gösterdiğini ve bu durumun neden hiç koşturulmadığını açıklar'
    - 'NULL atamanın bir işaretçiyi denetlenebilir kılarak sahipsiz sınıfından nasıl çıkardığını ölçer'
---

# Boş ve Sarkan İşaretçiler

Boş işaretçinin denetlenebilir bir durum olduğu, sarkan bir işaretçinin ise bırakılmış bir bloğu göstermeye devam ettiği ve bu yüzden hiç koşturulmadığı; ikisinin de sahipsiz sınıfının iki farklı yüzü olduğu.

Önceki dersin son satırı bir bırakma çağrısından hemen sonra işaretçiye `NULL` atadı ve
bunu "bir sonraki dersin konusu" diye bıraktı. Bu ders o atamanın neden zorunlu bir
alışkanlık olduğunu ölçüyor: bırakılmış bir bloğu göstermeye devam eden bir işaretçi
**sarkan** işaretçi olur ve bu durum çekirdeğin `sahipsiz` sınıfına giren iki
gözlemden biridir — kimse bu işaretçiyi geçerli tutmakla yükümlü değildir, ama kimse de
onu `NULL`'a çekmeyi garanti etmez.

## Boş İşaretçi: Denetlenebilir Bir Durum

```c
// bos_isaretci.c — bos isaretci, dolaylamadan once denetlenerek guvenli hale gelir
#include <stdio.h>
#include <stdlib.h>

static int *belki_ayir(int basarili) {
    if (!basarili) {
        return NULL;
    }
    int *p = malloc(sizeof *p);
    if (p != NULL) {
        *p = 9;
    }
    return p;
}

int main(void) {
    int *basarisiz = belki_ayir(0);
    printf("%s %d\n", "basarisiz cagri bos mu", (int)(basarisiz == NULL));

    int *basarili = belki_ayir(1);
    if (basarili != NULL) {
        printf("%s %d\n", "basarili cagridan gelen deger", *basarili);
    } else {
        printf("%s\n", "basarili cagri beklenmedik sekilde bos");
    }

    free(basarili);
    return 0;
}
```

```
basarisiz cagri bos mu 1
basarili cagridan gelen deger 9
```

**IS17.** `belki_ayir(0)` çağrısı `NULL` döndürüyor ve `basarisiz == NULL` karşılaştırması
**1** veriyor — bu, boş işaretçinin **beklenen ve denetlenebilir** bir durum olduğunu
gösteriyor. `belki_ayir(1)` çağrısı gerçek bir blok döndürüyor; ama bu blok da
kullanılmadan önce `basarili != NULL` ile denetleniyor, çünkü `malloc` her koşulda
başarılı olacağının garantisini vermiyor. Boş bir işaretçiyi **dolaylamadan önce**
denetlemek, bu ölçümün tek kuralı: `*basarisiz` hiç yazılmadı, çünkü `basarisiz`'in
boş olduğu zaten biliniyordu.

## Sarkan İşaretçi: Bırakılmış Bir Bloğu Göstermeye Devam Etmek

Bir işaretçi `free` ile bırakıldıktan sonra, o işaretçinin taşıdığı değer **değişmiyor** —
hâlâ aynı bloğu gösteriyor, ama artık o blok geçerli değil. Bu duruma **sarkan işaretçi**
deniyor. Sarkan bir işaretçiyi dolaylamak (`*p` ile okumak ya da yazmak) ya da onu tekrar
`free`'ye vermek (çift bırakma) tanımsız davranıştır: sonuç çoğu zaman sessizce yanlış bir
değer, bazen bir çökme, hiçbir zaman güvenilir bir davranıştır. Bu yüzden bu ders **hiçbir
sarkan işaretçiyi dolaylamıyor ya da tekrar bırakmıyor** — yalnız bunun neden tehlikeli
olduğunu betimliyor ve tanımlı karşılığını çalıştırıyor.

```c
// tanimli_karsilik.c — birakma sonrasi NULL atamasi, sarkan durumu denetlenebilir kilar
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p = malloc(sizeof *p);
    if (p == NULL) {
        printf("%s\n", "ayirma basarisiz");
        return 1;
    }
    *p = 21;
    printf("%s %d\n", "birakmadan once deger", *p);

    free(p);
    p = NULL;
    printf("%s %d\n", "birakmadan sonra bos mu", (int)(p == NULL));

    if (p != NULL) {
        printf("%s %d\n", "hic ulasilmayan dal", *p);
    } else {
        printf("%s\n", "dolaylama denetim sayesinde atlandi");
    }
    return 0;
}
```

```
birakmadan once deger 21
birakmadan sonra bos mu 1
dolaylama denetim sayesinde atlandi
```

**IS18.** `free(p)` çağrısından **hemen sonra** `p = NULL` atanıyor; bu iki satır arasına
hiçbir başka işlem girmiyor. `p == NULL` karşılaştırması **1** veriyor ve son `if`
bloğunda `p != NULL` **yanlış** olduğu için `*p` satırına hiç ulaşılmıyor — "dolaylama
denetim sayesinde atlandi" satırı bunun kanıtı. Eğer `p = NULL` ataması atlanmış olsaydı,
`p` bırakılmış bloğu göstermeye devam eden bir **sarkan işaretçi** olurdu ve `p != NULL`
denetimi onu yakalayamazdı — çünkü sarkan bir işaretçinin değeri boş değildir, yalnızca
geçersizdir. Bu fark, boş işaretçiyle sarkan işaretçi arasındaki temel ayrımdır: biri
denetlenebilir bir **değer** (boş), diğeri denetlenemeyen bir **durumdur** (geçersiz ama
boş görünmeyen).

Sarkan bir işaretçinin ikinci bir tehlikesi, onu tekrar `free`'ye vermektir — bu duruma
**çift bırakma** deniyor. `free(p)` çağrısından sonra, eğer `p` hâlâ eski adresi
taşıyorsa, ikinci bir `free(p)` çağrısı aynı bloğu iki kez bırakmaya çalışır; bu da
tanımsız davranıştır ve genellikle ayırıcının kendi iç durumunu bozar. Yukarıdaki örnekte
bu ikinci çağrı hiç yazılmadı, çünkü `p = NULL` atamasından sonra `free(NULL)` çağırmak
zararsızdır — standart, boş bir işaretçiyi bırakmayı **hiçbir işlem yapmama** olarak
tanımlıyor. Bu, `NULL` atamasının ikinci bir yararı: yalnız dolaylamayı değil, olası bir
ikinci bırakma çağrısını da zararsız hale getiriyor.

```c
// guvenli_ikinci_cagri.c — free(NULL) standart tarafindan zararsiz tanimlanir
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p = malloc(sizeof *p);
    if (p == NULL) {
        printf("%s\n", "ayirma basarisiz");
        return 1;
    }
    *p = 4;
    printf("%s %d\n", "birakmadan once deger", *p);

    free(p);
    p = NULL;

    free(p);
    printf("%s\n", "ikinci free NULL uzerinde calisti");
    return 0;
}
```

```
birakmadan once deger 4
ikinci free NULL uzerinde calisti
```

**IS19.** Bu kod `free(p)`'yi **iki kez** çağırıyor, ama aralarına `p = NULL`
satırı giriyor. İlk çağrı gerçek bloğu bırakıyor; ikinci çağrı `NULL` üzerinde
çalışıyor ve standart bunu açıkça "hiçbir işlem yapma" olarak tanımladığı için
program çökmeden "ikinci free NULL uzerinde calisti" satırına ulaşıyor. Eğer ikinci
satırdaki `p = NULL` ataması olmasaydı, bu aynı kod ikinci `free(p)` çağrısında aynı
bloğu **iki kez** bırakmaya çalışırdı — bu da yukarıda tanımlanan çift bırakma hatası
olurdu ve bu yüzden burada denenmiyor.

Bu derste `sahipsiz` sınıfının iki üyesi de aynı kökten geliyor: birinci dersteki
çekirdekte `uzerine yazilan isaretci` gözlemi, bir bloğun adresini tutan tek işaretçinin
**bırakılmadan** üzerine yazılmasıyla o bloğu erişilemez bırakmıştı — bu bir **sızıntı**.
Sarkan işaretçi ise tersi yönde bir hatadır: blok bırakılmıştır ama işaretçi hâlâ ona
işaret etmeye devam eder. İkisi de "kimse bu durumu garanti altına almadı" anlamına
geliyor, bu yüzden ikisi de `sahipsiz` sınıfında.

## Birden Çok Kopya İşaretçi: NULL Ataması Neden Yetmez

Şimdiye kadarki iki örnek de tek bir işaretçi üzerinden ilerledi. Gerçek kodda aynı
bloğu birden çok işaretçi gösterebilir — biri bırakma çağrısını yapar, ama diğerleri
`p = NULL` satırından habersiz kalır.

```c
// kopya_isaretci.c — ayni blogu gosteren iki isaretciden yalniz biri NULL'a cekilir
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *p = malloc(sizeof *p);
    if (p == NULL) {
        printf("%s\n", "ayirma basarisiz");
        return 1;
    }
    *p = 7;

    int *q = p;
    printf("%s %d\n", "p ve q ayni degeri gosteriyor mu", (int)(*p == *q));
    printf("%s %d\n", "p ve q ayni adresi mi tasiyor", (int)(p == q));

    free(p);
    p = NULL;
    printf("%s %d\n", "p birakma sonrasi bos mu", (int)(p == NULL));
    return 0;
}
```

```
p ve q ayni degeri gosteriyor mu 1
p ve q ayni adresi mi tasiyor 1
p birakma sonrasi bos mu 1
```

**IS20.** `int *q = p;` satırından sonra `p` ve `q` **aynı adresi** taşıyor — hem
`*p == *q` hem `p == q` karşılaştırması **1** veriyor. `free(p)` ve ardından `p = NULL`
çalıştığında yalnız `p` değişkeninin **kendisi** etkileniyor; `q` bu iki satırda hiç
geçmiyor, dolayısıyla hâlâ `free` ile bırakılmış bloğun eski adresini taşıyor —
`q` şimdi bir **sarkan işaretçi**. Bu kod `q`'yu bu noktadan sonra ne dolaylıyor ne de
`NULL` ile karşılaştırıyor, çünkü `q`'nun taşıdığı değer artık geçersiz ve onu okumaya
çalışmak (bir karşılaştırma dahil) tanımsız davranıştır. Gösterilmek istenen şey kod
çalışırken **görülmüyor** — tam olarak da bu, sınırın kendisi: `p = NULL` yalnızca `p`
adlı değişkenin durumunu değiştirir, `q`'nun durumunu değil.

## Sahiplik Ekseninde Sarkan İşaretçi: Üç Farklı Kaynak

Bu derse kadarki iki örnek de sarkan durumu tek bir kaynaktan üretti: bir `malloc`
bloğunun `free` ile bırakılması. Ama sarkan işaretçi, kursun dört sahiplik sınıfının
(`dil`, `çağıran`, `çağrılan`, `sahipsiz`) her birinde farklı bir yoldan ortaya
çıkabilir; kod burada çalıştırılmıyor çünkü her biri tanımsız davranışa yol açar, ama
üçü de bu dersin ölçtüğü aynı köke bağlanıyor.

`dil` sınıfında, bir işlev yerel bir değişkenin adresini döndürürse (`return &yerel;`
gibi), döndürülen işaretçi işlev geri döndüğü anda sarkan olur — çünkü `yerel`'in
ömrünü yöneten dilin kendisidir ve bu ömür işlevin gövdesiyle sınırlıdır. Burada hiçbir
`free` çağrısı yok; blok bir `malloc`'la değil, çağrı çerçevesinin
kendisiyle ortadan kalkıyor. Sonuç yine de aynı: çağıran taraf geçersiz bir adresi
gösteren bir işaretçiyle baş başa kalıyor.

`çağıran` sınıfında sarkan durum, bu dersin iki örneğinde de görülen biçimdir: belleği
ayıran ve `free` çağırma yükümlülüğü taşıyan taraf, bırakma sonrası `NULL` atamayı
unutursa (ya da `kopya_isaretci.c`'deki gibi ikinci bir kopya taşıyorsa), o işaretçi
sarkan kalır.

`çağrılan` sınıfında ise sarkan durum ters yönden gelir: bir işlev kendi tuttuğu bir
bloğu (`sahip.c` gibi bir çekirdeğin sahiplendiği veriyi düşün) kendi içinde
`free` ile bırakırsa, ama çağırana bu bloğa daha önce verdiği bir işaretçiyi geri
çekmezse, çağıranın elindeki kopya artık sarkan bir işaretçidir — çağıran bunu bilmez,
çünkü çağrılan bunu ona hiçbir şekilde bildirmedi. Bu yüzden `çağrılan` sınıfındaki
işlevlerin sözleşmesi açık olmalı: bir bloğu ne zaman bıraktığını ve bu bırakmanın
çağırandaki kopyaları geçersiz kıldığını belgelemek çağrılan tarafın sorumluluğudur.

Üçü de aynı gözleme varıyor: sarkan işaretçi, bir işaretçinin **taşıdığı adresin**
hâlâ geçerli bir bloğu gösterip göstermediğiyle ilgili bir sorudur ve bu soru hangi
sınıftan geldiğine bakılmaksızın dilin kendisi tarafından yanıtlanmaz — yanıt her
zaman programcının izlemesine kalır. Üç kaynak arasındaki fark, sorunun **ne zaman**
fark edilebilir olduğudur: `dil` sınıfındaki hata genellikle işlev döner dönmez, ilk
kullanımda ortaya çıkar; `çağıran` sınıfındaki hata bu dersteki gibi bir `NULL`
atamasının unutulmasıyla gecikebilir; `çağrılan` sınıfındaki hata ise en geç fark
edilenidir, çünkü çağıran tarafın kendi kodunda hiçbir şey değişmemiştir — yalnız
çağırdığı işlevin iç davranışı değişmiştir.

## Rust'ın Ödünç Denetleyicisiyle Farkı

C'de bırakılmış belleği kimin gözettiği sorusu, Sahiplik ve Bellek Modeli kursunda (M10/K02) ele alınan Rust'ın
ödünç denetleyicisinden farklı bir yerde duruyor: Rust'ta bir değerin ömrü derleme zamanında
belirlenir ve ömrü biten bir referans derleyici tarafından reddedilir, C'de ise `p = NULL`
gibi bir alışkanlık dışında hiçbir mekanizma bir işaretçinin hâlâ geçerli bir bloğu
gösterip göstermediğini denetlemez. Rust'ta yukarıdaki `kopya_isaretci.c` örneğinin bir
benzeri **derlenmezdi** — `q`'ya `p`'nin ödünç verildiği an, ödünç denetleyici `p`'nin
serbest bırakılmasını `q` hâlâ yaşarken engellerdi. C'de bu denetim yok; `q`'nun sarkan
olduğunu bilmek tamamen programcının derste burada yapılan türden bir izlemesine kalır.

## Sarkan İşaretçiyi Derleyici Değil, Araç Yakalar

Bu derste kullanılan tüm denetimler (`== NULL`, `!= NULL`) elle yazılan, programcının
kendi eklediği kontrollerdir — derleyicinin kendisi bir işaretçinin sarkan olup
olmadığını bilmez ve bunu asla otomatik olarak işaretlemez. Gerçek projelerde bu
boşluk çalışma zamanı araçlarıyla kapatılır: dinamik çözümleyiciler bir programı çalıştırırken her `malloc`/`free` çiftini izler
ve bir blok bırakıldıktan sonra o bloğa erişilirse (dolaylama, çift bırakma) bunu
raporlar. Bu araçlar derleme zamanında değil, **çalışma zamanında** ve yalnız o
koşumda gerçekten tetiklenen yollar için çalışır — Rust'ın derleme zamanı ödünç
denetleyicisinin tersine, bir sarkan işaretçi hatası yakalanmadan önce en az bir kez
gerçekten koşmuş olmalıdır. Bu, C'deki savunmanın doğasını özetliyor: dil kendisi
hiçbir garanti vermiyor, ama disiplin (bu derste görülen `NULL` atama alışkanlığı) ve
araçlar (çalışma zamanı denetleyicileri) birlikte bu garantinin eksikliğini
telafi ediyor.

## Sınırlayıcı Ölçüm

`p = NULL` ataması, sarkan işaretçi sorununu **ortadan kaldırmıyor** — yalnız o tek
işaretçi için görünür kılıyor. Yukarıdaki `kopya_isaretci.c` bunu somut olarak
gösterdi: `q`, `p`'yle aynı adresi taşımasına rağmen `p = NULL` satırından hiç
etkilenmedi. Bu ders tek işaretçili ve iki işaretçili durumu ayrı ayrı ölçtü; üç ya da
daha fazla işaretçinin aynı bloğu paylaştığı, ya da bir işaretçinin bir yapı alanında
saklandığı daha karmaşık paylaşım örüntüleri, kursun Dinamik Bellek konusundaki Serbest
Bırakma Disiplini dersinin konusu. Oradaki temel soru aynı kalıyor: bir bloğu bırakma
yetkisine kim sahip ve bırakıldığını diğer kopyalara kim bildirecek.

## Özet

- Boş işaretçi (`NULL`) **denetlenebilir bir değerdir**; `dolaylamadan önce == NULL ile
  sınanabilir ve bu ders bunu her ayırma çağrısından sonra uyguladı.
- Sarkan işaretçi, bırakılmış bir bloğu göstermeye **devam eden** bir işaretçidir; bu
  ders onu hiç dolaylamadı ya da tekrar bırakmadı, yalnız betimledi.
- `free` çağrısından hemen sonra işaretçiye `NULL` atamak, sarkan durumu denetlenebilir
  bir boş duruma **dönüştürüyor**.
- Boş ve sarkan işaretçi, çekirdeğin **sahipsiz** sınıfının iki farklı yüzüdür: biri
  bırakılmamış bir sızıntı, diğeri bırakılmış ama hâlâ gösterilen bir blok. `p = NULL`
  yalnız **o tek işaretçi** için geçerlidir; `kopya_isaretci.c`'nin gösterdiği gibi
  aynı bloğu gösteren başka bir kopya varsa o kopya hâlâ sarkandır.
- Sarkan durum yalnız `çağıran`'ın unuttuğu bir `NULL` atamasından değil, `dil`
  sınıfında bir yerel değişkenin adresinin döndürülmesinden ya da `çağrılan`'ın kendi
  bıraktığı bir bloğu çağırana bildirmemesinden de doğabilir — üçü de aynı soruya
  (adres hâlâ geçerli mi) farklı bir yoldan varır.
- Çift bırakma (`free`'yi aynı bloğa iki kez çağırmak) da sarkan işaretçinin bir
  sonucudur; `free(NULL)`'ın standart tarafından zararsız tanımlanması, `p = NULL`
  alışkanlığını bu ikinci tehlikeye karşı da koruyucu kılar — `guvenli_ikinci_cagri.c`
  bunu somut olarak gösterdi.

## Sonraki Adım

Bu derse kadarki dört ders, işaretçilerin `int *`, `double *` gibi **somut tiplerini**
kullandı — tip her zaman gösterilen değerin ne olduğunu söylüyordu. Bir sonraki ders,
`void` ve Opak İşaretçiler, bir işaretçinin tipinin bilinçli olarak **gizlendiği**
durumları ölçüyor: çekirdekteki `opak kayit` gözlemi, `Kayit` yapısının içeriğini
tamamen saklarken sahipliği nasıl `çağrılan` sınıfına taşıdığını gösteriyor. Bu
derste görülen sarkan işaretçi tehlikesi orada da geçerli kalıyor — bir opak kaydın
adresini elinde tutan çağıran, o kaydı bırakma yükümlülüğü `çağrılan`'a ait olduğu
için, kaydın ne zaman geçersiz olduğunu ancak `çağrılan`'ın sözleşmesinden öğrenebilir;
kaydın içeriği gizli olduğu için bu sözleşme, tip tanımının kendisinden değil, yalnız
dokümantasyondan gelir.
