---
title: 'Serbest Bırakma Disiplini'
source: 'https://academia.sh/tr/kurslar/c-bellek/serbest-birakma-disiplini'
course: "C'de Bellek ve İşaretçiler"
language: tr
updated: '2026-09-26T03:02:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir başlık dosyasındaki yorumun sahiplik sözleşmesini nasıl taşıdığını gösterir'
    - "kayit_ad ile kayit_ad_kopyasi'nin aynı metni iki farklı sahiple döndürdüğünü sayaç farkıyla ayırt eder"
    - "Bu dersteki yükümlülük sorusunu M10/K02'deki ömür sorusundan ayırır"
---

# Serbest Bırakma Disiplini

Bir işlev bir metni döndürdüğünde, o metnin sahibinin kim olduğu imzada yazmaz — yazan, başlık dosyasındaki yorumdur. Aynı kaydı gösteren iki işlev, biri ödünç verip biri kopya döndürerek, aynı sözleşmenin iki farklı sahibe nasıl bağlanabildiğini gösteriyor.

Önceki ders dört ayırma işlevinin dördünün de aynı sözleşmeyi taşıdığını gösterdi: ayıran
bırakır. Ama bu, sözleşmenin **tek biçimi** değil. Bir işlev bir işaretçi döndürdüğünde, o
işaretçinin sahibinin kim olacağına imza karar vermez — `char *` döndüren iki işlev,
aralarında hiçbir görünür fark olmadan, birbirinden tamamen farklı bir yükümlülük taşıyabilir.
Bu ders, aynı kaydı gösteren iki işlevle bu farkı ölçüyor: biri belleği **modülün içinde**
tutuyor ve yalnız ödünç veriyor, öteki çağırana **yeni bir blok** döndürüyor.

## Sözleşme Başlık Dosyasında Yazılıdır

Ölçüm, kursun geri kalanında yeniden kullanılan `sahip.h` ve `sahip.c` çiftiyle yapılıyor;
ikisi de sayaçlı sarmalayıcının (`sayac.h`/`sayac.c`) üzerine kurulu, o yüzden aynı birim
içinde onlar da yer alıyor. Kaydın içi (`struct Kayit`in alanları) `sahip.h`de görünmüyor —
yalnızca bir isim (`Kayit`) ve dört işlev imzası var. Sahiplik bilgisi, imzanın **üzerindeki
yorumda**.

```c
// sayac.h — her ayirmayi ve her birakmayi sayan sarmalayici
#ifndef SAYAC_H
#define SAYAC_H

#include <stddef.h>

void *say_ayir(size_t boyut);
void *say_buyut(void *eski, size_t boyut);
void say_birak(void *blok);
int ayrilan_sayisi(void);
int birakilan_sayisi(void);

#endif
```

```c
// sayac.c — sayaclar yalniz bu birimde tutulur
#include <stdlib.h>
#include "sayac.h"

static int ayrilan = 0;
static int birakilan = 0;

void *say_ayir(size_t boyut) {
    void *blok = malloc(boyut);
    if (blok != NULL) {
        ayrilan++;
    }
    return blok;
}

void *say_buyut(void *eski, size_t boyut) {
    int yeni_blok = (eski == NULL);
    void *yeni = realloc(eski, boyut);
    if (yeni != NULL && yeni_blok) {
        ayrilan++;
    }
    return yeni;
}

void say_birak(void *blok) {
    if (blok != NULL) {
        birakilan++;
    }
    free(blok);
}

int ayrilan_sayisi(void) {
    return ayrilan;
}

int birakilan_sayisi(void) {
    return birakilan;
}
```

```c
// sahip.h — kaydin ici gizli, sahiplik sozlesmesi yorumlarda yazili
#ifndef SAHIP_H
#define SAHIP_H

#include <stddef.h>

typedef struct Kayit Kayit;

/* Cagiran sahiplenir ve kayit_yok_et ile birakir. Ad sinira sigmazsa NULL
   doner; o ana kadar ayrilan bellegi islev kendisi birakir. */
Kayit *kayit_olustur(const char *ad, size_t sinir);

/* Kaydi ve kaydin icindeki ad tamponunu birlikte birakir. */
void kayit_yok_et(Kayit *k);

/* Kayit sahiplenir: donen isaretci kayit yok edilene dek gecerlidir,
   cagiran onu birakmaz. */
const char *kayit_ad(const Kayit *k);

/* Cagiran sahiplenir: donen kopya say_birak ile birakilir. */
char *kayit_ad_kopyasi(const Kayit *k);

#endif
```

```c
// sahip.c — sozlesmenin uygulandigi birim
#include <string.h>
#include "sahip.h"
#include "sayac.h"

struct Kayit {
    char *ad;
    size_t uzunluk;
};

Kayit *kayit_olustur(const char *ad, size_t sinir) {
    Kayit *k = say_ayir(sizeof *k);
    if (k == NULL) {
        return NULL;
    }
    k->uzunluk = strlen(ad);
    if (k->uzunluk >= sinir) {
        say_birak(k);
        return NULL;
    }
    k->ad = say_ayir(k->uzunluk + 1);
    if (k->ad == NULL) {
        say_birak(k);
        return NULL;
    }
    memcpy(k->ad, ad, k->uzunluk + 1);
    return k;
}

void kayit_yok_et(Kayit *k) {
    if (k == NULL) {
        return;
    }
    say_birak(k->ad);
    say_birak(k);
}

const char *kayit_ad(const Kayit *k) {
    return k->ad;
}

char *kayit_ad_kopyasi(const Kayit *k) {
    char *kopya = say_ayir(k->uzunluk + 1);
    if (kopya != NULL) {
        memcpy(kopya, k->ad, k->uzunluk + 1);
    }
    return kopya;
}
```

**BE4.** `kayit_ad` ile `kayit_ad_kopyasi`'nin imzaları neredeyse aynıdır — ikisi de bir
`const Kayit *` alır, ikisi de bir `char` dizisine işaretçi döndürür. Aralarındaki fark
derleyicinin denetleyebileceği hiçbir yerde durmuyor; yalnız yorumda duruyor. Bu, C'nin
bellek sahipliğini nasıl taşıdığının kısa özetidir: **tip sistemi bir yükümlülüğü
zorlamaz, yalnız bir insanın yazdığı ve bir başka insanın okuduğu bir cümle zorlar.**

## Ödünç Alınan ile Sahiplenilen

`kayit_ad` kaydın içindeki `ad` alanına doğrudan bir işaretçi döndürüyor — yeni bellek
ayırmıyor. Bu işaretçi **ödünçtür**: geçerliliği kaydın kendisine bağlıdır, kayıt
`kayit_yok_et` ile yok edilene kadar sürer. `kayit_ad_kopyasi` ise `say_ayir` ile **yeni**
bir blok ayırıp içine aynı metni kopyalıyor; bu blok kaydın ömründen bağımsızdır ve
sahibi artık çağırandır.

```c
// sozlesme.c — odunc verilen ad ile donen kopyanin karsitligi
#include <stdio.h>
#include <string.h>
#include "sahip.h"
#include "sayac.h"

static void gozle(const char *ad, const char *sinif, const char *kanit) {
    printf("%-30s%-10s%s\n", ad, sinif, kanit);
}

int main(void) {
    char tampon[64];
    int once, sonra;

    printf("%-30s%-10s%s\n", "gozlem", "sahip", "kanit");

    Kayit *k = kayit_olustur("sozlesme", 16);

    /* --- cagrilan: modul sahiplenir, cagiran birakmaz --- */
    once = ayrilan_sayisi() - birakilan_sayisi();
    const char *odunc = kayit_ad(k);
    sonra = ayrilan_sayisi() - birakilan_sayisi();
    snprintf(tampon, sizeof tampon, "ad %s, sayac farki %d", odunc, sonra - once);
    gozle("odunc verilen ad", "cagrilan", tampon);

    /* --- cagiran: donen kopyanin sahibi cagirandir --- */
    once = ayrilan_sayisi() - birakilan_sayisi();
    char *kopya = kayit_ad_kopyasi(k);
    int esit = (kopya != NULL) && (strcmp(kopya, odunc) == 0);
    say_birak(kopya);
    sonra = ayrilan_sayisi() - birakilan_sayisi();
    snprintf(tampon, sizeof tampon, "esit metin %d, sayac farki %d", esit, sonra - once);
    gozle("donen kopya", "cagiran", tampon);

    /* --- odunc hala gecerli: kopya birakildi, kayit hala ayakta --- */
    const char *odunc_sonra = kayit_ad(k);
    snprintf(tampon, sizeof tampon, "ad %s, kayittan once mi %d", odunc_sonra, odunc_sonra == odunc);
    gozle("odunc hala gecerli", "cagrilan", tampon);

    /* --- kayit yok edilince odunc de kaydin omrunu takip eder --- */
    once = ayrilan_sayisi() - birakilan_sayisi();
    kayit_yok_et(k);
    sonra = ayrilan_sayisi() - birakilan_sayisi();
    snprintf(tampon, sizeof tampon, "sayac farki %d", sonra - once);
    gozle("kaydin yok edilmesi", "cagrilan", tampon);

    return 0;
}
```

```
gozlem                        sahip     kanit
odunc verilen ad              cagrilan  ad sozlesme, sayac farki 0
donen kopya                   cagiran   esit metin 1, sayac farki 0
odunc hala gecerli            cagrilan  ad sozlesme, kayittan once mi 1
kaydin yok edilmesi           cagrilan  sayac farki -2
```

**BE5.** `odunc verilen ad` gözleminde sayaç farkı **0**'dır — `kayit_ad` hiçbir şey
ayırmadı, hiçbir şey bırakmadı; yalnız var olan bir bloğa işaret eden bir işaretçi
kopyaladı. `donen kopya` gözleminde de fark **0**, ama nedeni farklı: burada bir ayırma
**oldu**, ardından `say_birak(kopya)` ile hemen bırakıldı. İki sıfır, iki farklı öyküyü
gizliyor — biri hiç ayırma yapmadığı için sıfır, öteki ayırdığını bıraktığı için sıfır.
`kaydin yok edilmesi` gözlemindeki **-2** ise beklenen: `kayit_yok_et` iki bloğu (adı ve
kaydın kendisini) bıraktı, hiçbirini yeniden ayırmadı; sayaç farkı negatife döndü çünkü bu
kez ölçülen yalnız bırakma tarafı.

`odunc hala gecerli` gözlemi, ödünç verilen işaretçinin **kaydın ömrüne bağlı** olduğunu
gösteriyor: `kayit_ad_kopyasi` çağrısı ayrı bir blok ayırıp bıraktı, ama kaydın kendisine
hiç dokunmadı — bu yüzden `kayit_ad`'in ikinci çağrısı **aynı** adresi (`odunc_sonra == odunc`)
döndürdü. Ödünç verilen işaretçiyi geçersiz kılan tek şey `kayit_yok_et`'in kendisidir.

## İki Bağımsız Kopya

`kayit_ad_kopyasi` her çağrıldığında **yeni** bir blok ayırıyor; bu, aynı kayıttan iki kopya
istenirse iki ayrı sahibin doğduğu anlamına gelir. Kopyalardan biri erken bırakılırsa
öteki bundan hiç etkilenmez — çünkü ikisi arasında adresten başka hiçbir bağ yok.

```c
// iki_kopya.c — ayni kayittan alinan iki kopyanin bagimsiz omru
#include <stdio.h>
#include <string.h>
#include "sahip.h"
#include "sayac.h"

int main(void) {
    char tampon[96];
    Kayit *k = kayit_olustur("bagimsiz", 16);

    int once = ayrilan_sayisi() - birakilan_sayisi();
    char *kopya_a = kayit_ad_kopyasi(k);
    char *kopya_b = kayit_ad_kopyasi(k);
    int ara_fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;

    say_birak(kopya_a);
    int b_ayakta_mi = (strcmp(kopya_b, "bagimsiz") == 0);
    int orta_fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;

    say_birak(kopya_b);
    int son_fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;
    kayit_yok_et(k);

    snprintf(tampon, sizeof tampon,
             "iki ayirma sonrasi fark %d, a birakilinca %d, b hala okunuyor %d, ikisi de birakilinca %d",
             ara_fark, orta_fark, b_ayakta_mi, son_fark);
    printf("%-30s%s\n", "iki bagimsiz kopya", tampon);

    return 0;
}
```

```
iki bagimsiz kopya            iki ayirma sonrasi fark 2, a birakilinca 1, b hala okunuyor 1, ikisi de birakilinca 0
```

**BE6.** `kopya_a` ile `kopya_b` aynı kaynaktan (`k->ad`) türedi, ama ikisi de
`kayit_ad_kopyasi` üzerinden **ayrı** bir `say_ayir` çağrısıyla doğdu — bu yüzden sayaç
farkı iki ayırmadan sonra **2**. `say_birak(kopya_a)` çağrısı yalnız `kopya_a`'nın bloğunu
etkiliyor; `kopya_b` hâlâ geçerli ve `strcmp` bunu doğruluyor (`b hala okunuyor 1`). Bu,
`kayit_ad`'in tek bir ödünç işaretçisi döndürmesiyle tam tersi bir örüntü: orada aynı
kaydın iki çağrısı **aynı** adresi verirdi (`odunc_sonra == odunc`), burada aynı kaydın
iki `kayit_ad_kopyasi` çağrısı **farklı** iki blok verir. Sahiplik sınıfı ikisinde de
çağırandır, ama her kopya kendi başına, birbirinden bağımsız bir sahiplik taşır —
birini bırakmak ötekinin sözleşmesini hiç etkilemez.

## Sınır Aşımında Temizlik

`kayit_olustur`'un başlık dosyasındaki yorumu bir üçüncü olasılığı da kayda geçiriyor: ad
verilen sınıra sığmazsa işlev `NULL` döner, ama o ana kadar ayırdığı belleği **kendisi**
bırakır. Bu, çağrılan sınıfının yalnızca başarılı dönüşte değil, **başarısız** dönüşte de
bir yükümlülük taşıdığını gösteriyor — modül kendi yarım kalan işini kendi temizliyor,
çağırana hiçbir şey bırakmıyor.

```c
// sinir.c — kayit_olustur sinira sigmayinca kendi ayirdigini kendisi birakir
#include <stdio.h>
#include "sahip.h"
#include "sayac.h"

int main(void) {
    char tampon[64];
    int once = ayrilan_sayisi() - birakilan_sayisi();

    Kayit *basarisiz = kayit_olustur("cok-uzun-bir-ad", 4);
    int null_mu = (basarisiz == NULL);
    int fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;

    snprintf(tampon, sizeof tampon, "NULL mu %d, sayac farki %d", null_mu, fark);
    printf("%-30s%s\n", "sinira sigmayan ad", tampon);

    return 0;
}
```

```
sinira sigmayan ad            NULL mu 1, sayac farki 0
```

**BE7.** `"cok-uzun-bir-ad"` on beş karakter uzunluğunda, sınır ise **4** — `kayit_olustur`
`k` için bir blok ayırdıktan sonra uzunluk denetimini başarısız buluyor ve `k->ad`ı hiç
ayırmadan `say_birak(k)` çağırıp `NULL` döndürüyor. Sayaç farkı yine de **0**: tek ayırma,
tek bırakma, ikisi de işlevin kendi içinde oldu. Çağıran taraf `basarisiz`'in `NULL`
olduğunu görüyor ama hangi bloğun ayrılıp hangisinin bırakıldığını hiç bilmiyor — ve
bilmesine gerek de yok, çünkü sözleşme başarısızlık durumunda da modülün üzerinde.

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

`sahip.h`deki yorum iki yönde de bağlayıcıdır. Çağıran `kayit_ad`'in döndürdüğü işaretçiyi
`say_birak` ile bırakırsa, kaydın kendi bırakma çağrısı (`kayit_yok_et` içindeki
`say_birak(k->ad)`) aynı bloğu **ikinci kez** bırakır — bu, tanımsız davranıştır ve bu
derste koşturulmaz. Tersi de kırılabilir: çağıran `kayit_ad_kopyasi`'nin döndürdüğü bloğu
hiç bırakmazsa, blok kaydın ömründen bağımsız olduğu için kaydın yok edilmesi de onu
kurtarmaz — sızar. Sözleşmenin iki yönü de aynı yorumda yazılı, ama derleyici hiçbirini
denetlemez; denetleyen, yalnız kodu okuyan kişidir.

Bu iki kırılma biçiminin ortak noktası, ikisinin de **imzadan görünmemesidir**. `char *
kayit_ad(const Kayit *k)` ile `char *kayit_ad_kopyasi(const Kayit *k)` derleyicinin
gözünde eş değerdedir — ikisi de bir `char` dizisine işaretçi döndürür, ikisi de `const
Kayit *` alır. Çağıran taraf yanlış işlevi yanlış şekilde ele alsa (`kayit_ad`'in dönüşünü
bırakmaya kalksa ya da `kayit_ad_kopyasi`'nin dönüşünü hiç bırakmasa) derleme yine de
başarılı olur; hata yalnız çalışma zamanında, çift bırakma ya da sızıntı olarak ortaya
çıkar. Bu, önceki dersteki `realloc`'un başarısız dönüşünü **üzerine yazmanın** aynı
sınıftan bir hatadır: ikisi de sözdizimsel olarak geçerli, ikisi de derleyicinin
yakalayamadığı bir sahiplik ihlalidir. Aradaki fark kaynaktır — orada hata bir dönüş
değerinin göz ardı edilmesinden, burada bir yorumun okunmamasından doğuyor.

`sahip.c`nin kendi içinde bu iki kırılmaya karşı hiçbir savunması yok; savunmayı
üstlenen, modülün **kullanıcısıdır**. `kayit_olustur` ile `kayit_yok_et` çifti bir
kaydın tüm ömrünü modülün kendi kontrolünde tutuyor — çağıran yalnız bu çifti simetrik
kullandığı sürece güvende. `kayit_ad`'in ödünç verdiği işaretçiyi bir değişkende saklayıp
kaydı yok ettikten sonra o değişkeni okumak da aynı aileden bir hatadır (sarkan
işaretçi); bu ders bunu ayrıca koşturmuyor çünkü zaten M10/K02'nin ömür sorusuna geri
dönüyor — burada asıl konu **kimin bırakacağı**, orada konu **ne zaman bittiği**ydi ve
ikisi burada aynı anda kırılıyor.

**Bu ders, M10/K02'de ölçülen ömür sorusuyla karışmaz.** Orada bir değerin ömrünün
**nerede bittiği** ölçülmüştü ve o yeri **dil** belirliyordu. Burada ölçülen, o ömrü
bitirmekle **kimin yükümlü olduğudur** ve bunu belirleyen dil değil, `sahip.h`deki
**sözleşmedir.** `kayit_ad` ile `kayit_ad_kopyasi` aynı metni verir, ikisinin de ömrü
sonunda biter — ama biri o sonu kaydın sahibine bırakır, öteki çağırana devreder.

## Özet

- Bir işlevin döndürdüğü işaretçinin sahibi imzadan değil, başlık dosyasındaki **yorumdan**
  anlaşılır; bu dersin ölçtüğü bellek sınıfı **çağrılan**dır.
- `kayit_ad` ödünç verir: sayaç farkı **0**, geçerliliği kaydın ömrüne bağlıdır.
- `kayit_ad_kopyasi` yeni bir blok ayırır: sayaç farkı da **0**, ama bu kez ayırma ve
  bırakma gerçekten oldu — sahibi artık çağırandır.
- Sözleşmeyi kırmak (ödünç alınanı bırakmak ya da kopyayı hiç bırakmamak) çift bırakma ya
  da sızıntı doğurur; ikisi de bu derste koşturulmadı, yalnız tarif edildi.
- Bu dersteki yükümlülük sorusu, M10/K02'deki ömür sorusundan ayrıdır: biri **kim
  bırakacak**ı sorar, öteki **ne zaman biter**i.

## Sonraki Adım

`kayit_ad_kopyasi`'nin döndürdüğü kopya bırakılmazsa ne olur, bu derste yalnız tarif edildi
— koşturulmadı. Sıradaki ders tam bunu ölçüyor: dönüşü hiç tutulmayan bir kopya, hiçbir
işaretçinin artık ulaşamadığı bir blok bırakır ve bu blok, ne bir hata mesajıyla ne bir
çökmeyle görünür — yalnız sayaç farkıyla.
