---
title: 'Bellek Sızıntıları'
source: 'https://academia.sh/tr/kurslar/c-bellek/bellek-sizintilari'
course: "C'de Bellek ve İşaretçiler"
language: tr
updated: '2026-09-26T03:02:43+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir işaretçinin üzerine yazılmasının önceki bloğu nasıl ulaşılamaz bıraktığını sayaç farkıyla gösterir'
    - 'Dönen bir değerin yok sayılmasının aynı sonucu nasıl ürettiğini ölçer'
    - 'Bellek sızıntısının bir hata mesajı değil, bir sayaç farkı olduğunu açıklar'
---

# Bellek Sızıntıları

Bir bellek sızıntısı ne bir hata mesajı verir ne programı çökertir — yalnız ayrılan ile bırakılan arasındaki farkı büyütür. Ders iki yoldan sızıntı üretiyor, ikisini de sayaç farkıyla yakalıyor ve toplamın neden tam 2 çıktığını gösteriyor.

Önceki ders bir sözleşmenin nasıl kırılabileceğini yalnız **tarif etti** — çift bırakma da,
bırakılmayan bir kopya da orada koşturulmadı. Bu ders, sözleşmenin kırılmasının bir türünü
gerçekten koşturuyor: kimsenin bırakmakla yükümlü olmadığı bir blok. Bu, önceki üç sınıftan
(dil, çağıran, çağrılan) hiçbirine girmez — kursun dördüncü ve son sınıfı **sahipsiz**dır.
Sahipsiz sınıf bir tasarım değil, bir kusurdur; ve göründüğü tek yer **sayaç farkıdır.**

## İki Yoldan Aynı Sonuç

Bir blok iki farklı biçimde sahipsiz kalabilir: ona ulaşan tek işaretçinin üzerine
yazılmasıyla, ya da onu döndüren bir çağrının dönüş değerinin hiç tutulmamasıyla. İkisi de
aynı şeyi üretir — artık hiçbir değişkenden ulaşılamayan bir blok — ama biri bir atama
hatası, öteki bir gözden kaçırmadır.

```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;
}
```

```c
// sizinti.c — uzerine yazma ile yok sayilan donusun karsilastirilmasi
#include <stdio.h>
#include "sahip.h"
#include "sayac.h"

static int fark(void) {
    return ayrilan_sayisi() - birakilan_sayisi();
}

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;
    int birinci_fark, ikinci_fark;

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

    /* --- uzerine yazilan isaretci: ilk bloga ulasilamaz --- */
    once = fark();
    int *kayip = say_ayir(sizeof *kayip);
    kayip = say_ayir(sizeof *kayip);
    say_birak(kayip);
    birinci_fark = fark() - once;
    snprintf(tampon, sizeof tampon, "sayac farki %d", birinci_fark);
    gozle("uzerine yazilan isaretci", "sahipsiz", tampon);

    /* --- yok sayilan donus: kopya hic tutulmadan kaybolur --- */
    Kayit *k = kayit_olustur("taslak", 16);
    once = fark();
    if (k != NULL) {
        kayit_ad_kopyasi(k);
    }
    ikinci_fark = fark() - once;
    snprintf(tampon, sizeof tampon, "sayac farki %d", ikinci_fark);
    kayit_yok_et(k);
    gozle("yok sayilan donus", "sahipsiz", tampon);

    printf("toplam sahipsiz sayac farki %d\n", birinci_fark + ikinci_fark);

    return 0;
}
```

```
gozlem                        sahip     kanit
uzerine yazilan isaretci      sahipsiz  sayac farki 1
yok sayilan donus             sahipsiz  sayac farki 1
toplam sahipsiz sayac farki 2
```

**BE8.** `uzerine yazilan isaretci` gözleminde `kayip` iki kez ayrılıyor ama yalnız bir kez
bırakılıyor: ikinci `say_ayir` çağrısı `kayip`'in üzerine yazınca, ilk çağrının döndürdüğü
adresi tutan hiçbir değişken kalmıyor. `say_birak(kayip)` yalnız **ikinci** bloğu
bırakabiliyor — birinciye artık hiçbir yoldan ulaşılamıyor. Sayaç farkı **1**: bir blok
ayrıldı, karşılığı hiç bırakılmadı.

**BE9.** `yok sayilan donus` gözlemi farklı bir yoldan aynı sonuca varıyor. `kayit_ad_kopyasi`
her çağrıldığında yeni bir blok ayırıyor ve onu **döndürüyor** — işlevin kendisi bloğu
bırakmıyor, çünkü sözleşmesi bunu çağırana bırakıyor (`Serbest Bırakma Disiplini` dersi).
Burada dönüş değeri hiçbir değişkene atanmadı; işlev başarıyla çalıştı, blok ayrıldı, ama
ona ulaşan hiçbir işaretçi hiç var olmadı. Sayaç farkı yine **1**. İki gözlem birlikte
**toplam sahipsiz sayac farki 2** veriyor — bu sayı, ölçüm çekirdeğinin tam koşumundaki
son satırla aynı **fark 2**dir; burada aynı sonucu yalnız iki sahipsiz gözlemden topluyor.

## Sızıntı Tekrarlanınca

Önceki bölümdeki iki gözlem sızıntıyı **tek seferlik** ölçtü: her biri sayaç farkını bir
artırdı. Ama "uzun süre çalışan bir süreçte sayaç sürekli büyür" iddiası henüz yalnız
sözle söylendi, ölçülmedi. Aynı sızdıran düzeneği bir döngü içinde birkaç kez çalıştırmak,
bu iddiayı doğrudan bir sayıya çevirir.

```c
// tekrar.c — ayni sizintinin dongude tekrarlanmasi
#include <stdio.h>
#include "sahip.h"
#include "sayac.h"

static int fark(void) {
    return ayrilan_sayisi() - birakilan_sayisi();
}

static void sizdiran_cagri(const Kayit *k) {
    /* donus deger kasten tutulmuyor: her cagri bir blok sizdirir */
    kayit_ad_kopyasi(k);
}

int main(void) {
    char tampon[64];
    Kayit *k = kayit_olustur("dongu", 16);
    int once = fark();

    for (int i = 1; i <= 3; i++) {
        sizdiran_cagri(k);
        int su_an = fark() - once;
        snprintf(tampon, sizeof tampon, "cagri %d sonrasi sayac farki %d", i, su_an);
        printf("%-30s%s\n", "sizinti tekrari", tampon);
    }

    kayit_yok_et(k);
    return 0;
}
```

```
sizinti tekrari               cagri 1 sonrasi sayac farki 1
sizinti tekrari               cagri 2 sonrasi sayac farki 2
sizinti tekrari               cagri 3 sonrasi sayac farki 3
```

**BE10.** Her `sizdiran_cagri` çağrısı `kayit_ad_kopyasi` üzerinden bir blok ayırıyor ve
dönüşü hiç tutmuyor — tıpkı `yok sayilan donus` gözlemindeki gibi, ama burada tek bir çağrı
yerine üç kez üst üste. Sayaç farkı çağrı başına tam **1** artıyor: 1, 2, 3. Bu üç sayı,
"kısa süren bir programda bu fark önemsiz kalabilir; uzun süre çalışan bir süreçte sayaç
sürekli büyür" cümlesinin artık bir gözlem, iddia olmadığını gösteriyor. Döngü üç turda
durduğu için fark üçte duruyor; gerçek bir süreçte böyle bir çağrı saniyede binlerce kez
çalışabilir ve aynı doğrusal artış hiç durmadan sürer — sayaç bu kursta elle tutulduğu için
görünür, gerçek bir programda görünmez ama **olmaz** değildir.

## Kısmi Sızıntı: Yarım Bırakılan Dizi

Önceki iki gözlem sızıntıyı ya tam yaptı ya da hiç yapmadı — bir çağrının dönüşü ya
tutuldu ya hiç tutulmadı. Ama gerçek kodda daha sinsi bir örüntü de olur: bir dizi bloğun
**çoğu** doğru bırakılır, biri **atlanır**. Toplam sayaç farkı bunu da aynı duyarlılıkla
yakalıyor — kaçının doğru yapıldığından bağımsız, yalnız kaçının **eksik** kaldığını
sayıyor.

```c
// kismi.c — dort kopyadan ucunun birakilip birinin atlanmasi
#include <stdio.h>
#include "sahip.h"
#include "sayac.h"

static int fark(void) {
    return ayrilan_sayisi() - birakilan_sayisi();
}

int main(void) {
    char tampon[80];
    enum { ADET = 4 };
    char *kopyalar[ADET];
    Kayit *k = kayit_olustur("kismi", 16);
    int once = fark();

    for (int i = 0; i < ADET; i++) {
        kopyalar[i] = kayit_ad_kopyasi(k);
    }

    for (int i = 0; i < ADET; i++) {
        if (i == 2) {
            continue; /* ucuncu kopya kasten birakilmiyor */
        }
        say_birak(kopyalar[i]);
    }

    int kalan_fark = fark() - once;
    kayit_yok_et(k);

    snprintf(tampon, sizeof tampon,
             "ayrilan %d, birakilan %d, kalan sayac farki %d", ADET, ADET - 1, kalan_fark);
    printf("%-30s%s\n", "kismi birakma", tampon);

    return 0;
}
```

```
kismi birakma                 ayrilan 4, birakilan 3, kalan sayac farki 1
```

**BE11.** Döngü dört kopya ayırdı, ikinci döngü bunlardan üçünü bıraktı ve yalnız `i == 2`
olan kopyayı atladı. `kalan_fark` **1** çıkıyor — tam olarak atlanan kopya sayısı kadar,
ne fazla ne eksik. Bu gözlem, sızıntının "ya hep ya hiç" olmadığını gösteriyor: bir döngü
içindeki dört bırakma çağrısından üçünün doğru çalışması, dördüncüsünün eksikliğini
gizlemiyor, sayaç farkı yine de tam eksik kalan kadar büyüyor. Gerçek bir programda bu
örüntü, bir döngünün erken `continue` ya da `break` ile atladığı bir dalda ortaya çıkar —
kodun geri kalanı tamamen doğru bırakma disiplini uygularken, tek bir kontrol akışı dalı
bu disiplinin dışında kalır.

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

Bu iki gözlemin ikisinde de program **çökmedi**, hiçbir uyarı basmadı, hiçbir işlev `NULL`
döndürmedi. Ayırma işlevleri sözleşmelerini tam olarak yerine getirdi: istenen boyutta bir
blok verdiler. Sorun onların tarafında değil, çağıranın tarafında — bir atama unutuldu ya
da bir dönüş değeri yok sayıldı. Bu yüzden bir bellek sızıntısı, çalışma zamanında **hiçbir
gözlemlenebilir belirti** vermez; yalnız ayrılan ile bırakılan arasındaki fark büyür. Kısa
süren bir programda bu fark önemsiz kalabilir; uzun süre çalışan bir süreçte aynı düzenek
tekrar tekrar işletilirse, sayaç sürekli büyür ve sonunda bellek tükenir.

**Bu, M10/K02'de ölçülen ömür sorusuyla karışmaz.** Orada bir değerin ömrünün nerede
bittiği soruluyordu ve o yeri **dil** belirliyordu — kapsam dışına çıkan bir yerel
değişken otomatik olarak geri alınırdı. Burada bloğun ömrünü bitirecek **hiçbir dil kuralı
yok**; blok, dil tarafından değil, **çağıranın kendi bırakma çağrısıyla** geri alınır. O
çağrı hiç yapılmazsa, dil hiçbir şeyi kendiliğinden bitirmez — bu yüzden sahipsiz sınıf
diğer üçünden farklı olarak bir **eksikliğin** adıdır, bir kuralın değil.

Diğer üç sınıfın her biri, sahibin kim olduğuna bir **cevap** verir: dil sınıfında sahip
kapsamın kendisi, çağıran sınıfında sahip çağıran tarafın kodu, çağrılan sınıfında sahip
modülün içindeki sözleşmedir. Sahipsiz sınıfın verdiği cevap yoktur — çünkü soru hiç
sorulmamıştır. `uzerine yazilan isaretci` gözleminde blok, ayrıldığı anda **çağıran**
sınıfına aitti; `say_ayir`'in döndürdüğü işaretçi kimin sorumluluğunda olduğu konusunda
belirsiz değildi. Belirsizlik sonradan, o işaretçi ikinci bir atamayla ezilince doğdu. Aynı
şey `yok sayilan donus` için de geçerli: `kayit_ad_kopyasi`'nin sözleşmesi net (çağıran
sahiplenir), ama sahiplenecek çağıran tarafında hiçbir değişken o rolü üstlenmedi.
Sahipsizlik bu yüzden dördüncü, **bağımsız** bir sınıf değil; önceden var olan bir
sahiplik ilişkisinin koddaki izinin silinmesidir. Bir bloğun sahibi hiçbir zaman
"sahipsiz" olarak tasarlanmaz — yalnız öyle **bırakılır.**

## Özet

- Bir bellek sızıntısı iki yoldan doğar: bir işaretçinin üzerine yazılması ya da dönen bir
  değerin yok sayılması; bu dersin ölçtüğü bellek sınıfı **sahipsiz**dır.
- Sızıntı hiçbir hata mesajı ya da çökme üretmez; tek görünür kanıtı **sayaç farkıdır.**
- İki gözlemin her biri sayaç farkını **1** artırdı, toplamı **2** verdi — ölçüm
  çekirdeğinin bağlayıcı son satırındaki fark ile aynı sayı.
- Sahipsiz sınıf bir dil kuralı değil, bir **eksikliktir**: dil hiçbir bırakma çağrısını
  kendiliğinden yapmaz.

## Sonraki Adım

Bu ders sızıntıyı **sayaç farkıyla** ölçtü, çünkü çekirdeğin kendisi sayaçlı bir
sarmalayıcıydı. Gerçek bir programda böyle bir sayaç elle tutulmaz — bunun yerine bu işi
yapan **bir araç** çalıştırılır. Sıradaki ders, bu dersin sayaçlı sarmalayıcısının aslında
böyle bir aracın yaptığını nasıl modellediğini gösteriyor: `yakalandi` ya da `yakalanmadi`,
başka hiçbir isim geçmeden.
