---
title: 'Bellek Ayırma İşlevleri'
source: 'https://academia.sh/tr/kurslar/c-bellek/bellek-ayirma-islevleri'
course: "C'de Bellek ve İşaretçiler"
language: tr
updated: '2026-09-26T03:02:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "malloc, calloc ve realloc'un ayırdığı bloğun sahibinin her durumda çağıran olduğunu sayaç farkıyla ölçer"
    - "calloc'un ayırdığı bloğu sıfırladığını değerleri doğrudan okuyarak gösterir"
    - 'Bir yeniden boyutlandırmanın eski bloğun sahipliğini değiştirmediğini ve başarısızlıkta eski bloğun geçerli kaldığını açıklar'
---

# Bellek Ayırma İşlevleri

malloc, calloc, realloc ve free aynı öbekten çalışır ve dördü de aynı soruyu bırakır: ayırdığın bloğu kim bırakacak? Yanıt hep aynıdır — ayıran çağırandır; ders bunu sayaç farkıyla, sıfırlamayı doğrudan okuyarak, yeniden boyutlandırmayı ise eski sahipliğin değişmediğini göstererek ölçüyor.

Önceki altı ders bir işaretçinin gösterdiği belleğin nereden geldiğini hep önceden verilmiş
kabul etti: bir yerel değişkenin adresi, bir dizinin ilk elemanı, bir kaydın içindeki bir
alan. `Fonksiyon İşaretçileri` dersi bile bir geri çağrının bağlamını taşıyordu ama o
bağlamın kendisinin nasıl var olduğunu sormadı. Bu ders o boşluğu kapatıyor: bir blok, kimse
onu istemeden önce, önce **ayrılmak** zorundadır — ve ayıran taraf, dört işlevden hangisini
çağırırsa çağırsın, aynı yükü üstlenir.

C'nin standart kitaplığı dört ayırma ve bırakma işlevi verir: `malloc` istenen boyutta
sıfırlanmamış bir blok döndürür, `calloc` eleman sayısı ve eleman boyutunu ayrı parametre
alıp bloğu **sıfırlayarak** döndürür, `realloc` var olan bir bloğu büyütür ya da küçültür,
`free` bir bloğu bırakır. Dördü de aynı sözleşmeyi taşır: dönen işaretçinin **sahibi
çağırandır** — bloğu isteyen taraf, onu bırakmakla da yükümlüdür. Bu kursun ölçü ekseninde
bu, dört sınıftan en dolaysız olanıdır; burada bir sözleşme aramaya gerek yoktur, sözleşme
işlevin kendi imzasındadır.

## Ayıran Bırakır

Ölçüm, kursun geri kalanında yeniden kullanılan aynı sayaçlı sarmalayıcıyla yapılıyor.
`sayac.c` her `say_ayir` ve `say_buyut` çağrısını, her `say_birak` çağrısını sayıyor; bir
gözlemin başındaki ve sonundaki fark **0** ise, o gözlemde ayrılan her şey bırakılmış demektir.

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

Bu iki dosya kursun kalan on üç dersinde de aynı adla ve aynı içerikle dönüyor; burada ilk
kez göründükleri için tam hâlleriyle yazıldı. `say_ayir` `malloc`'ı, `say_buyut` `realloc`'ı
sarmalıyor — ikisi de yalnız sayacı artırıp gerçek işlevi çağırıyor, davranışı değiştirmiyor.

```c
// tahsis.c — malloc, calloc ve realloc'un cagiran sinifiyla olcumu
#include <stdio.h>
#include <stdlib.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");

    /* --- malloc: ham blok, ayiran birakir --- */
    once = ayrilan_sayisi() - birakilan_sayisi();
    int *ham = say_ayir(3 * sizeof *ham);
    int toplam = 0;
    if (ham != NULL) {
        for (int i = 0; i < 3; i++) {
            ham[i] = i + 1;
            toplam += ham[i];
        }
    }
    say_birak(ham);
    sonra = ayrilan_sayisi() - birakilan_sayisi();
    snprintf(tampon, sizeof tampon, "toplam %d, sayac farki %d", toplam, sonra - once);
    gozle("ham blok", "cagiran", tampon);

    /* --- calloc: sifirlanmis blok, degerler dogrudan okunur --- */
    int *sifirlanan = calloc(4, sizeof *sifirlanan);
    int hepsi_sifir = 1;
    if (sifirlanan != NULL) {
        for (int i = 0; i < 4; i++) {
            if (sifirlanan[i] != 0) {
                hepsi_sifir = 0;
            }
        }
    }
    snprintf(tampon, sizeof tampon, "hepsi sifir %d", hepsi_sifir);
    gozle("sifirlanan blok", "cagiran", tampon);
    free(sifirlanan);

    /* --- realloc: yeniden boyutlandirma yeni bir sahip dogurmaz --- */
    once = ayrilan_sayisi() - birakilan_sayisi();
    int *buyuyen = say_buyut(NULL, 2 * sizeof *buyuyen);
    int korunan = 0;
    if (buyuyen != NULL) {
        buyuyen[0] = 7;
        int *yeni = say_buyut(buyuyen, 8 * sizeof *yeni);
        if (yeni != NULL) {
            buyuyen = yeni;
        }
        korunan = buyuyen[0];
    }
    say_birak(buyuyen);
    sonra = ayrilan_sayisi() - birakilan_sayisi();
    snprintf(tampon, sizeof tampon, "ilk eleman %d, sayac farki %d", korunan, sonra - once);
    gozle("yeniden boyutlandirma", "cagiran", tampon);

    printf("ayrilan %d birakilan %d fark %d\n", ayrilan_sayisi(), birakilan_sayisi(),
           ayrilan_sayisi() - birakilan_sayisi());

    return 0;
}
```

```
gozlem                        sahip     kanit
ham blok                      cagiran   toplam 6, sayac farki 0
sifirlanan blok               cagiran   hepsi sifir 1
yeniden boyutlandirma         cagiran   ilk eleman 7, sayac farki 0
ayrilan 2 birakilan 2 fark 0
```

**BE1.** Üç gözlemin üçü de sayaç farkını **0**'a döndürdü ve bunun tek nedeni var: her
ayırma çağrısının hemen ardından ya da işlev sonunda karşılığı olan bir bırakma çağrısı var.
Bu, dört işlevin ortak bir davranışı değil, **çağıranın verdiği bir söz**: `malloc`,
`calloc` ve `realloc` bir sözleşme dayatmaz, yalnız bir blok döndürür; bloğu bırakmayı
unutmak derleyicinin uyarısını da tetiklemez, bağlayıcının hatasını da. `ham blok` üç
elemanlık bir dizi gibi kullanıldı ve toplam **6** verdi — bu, önceki dersteki bozunmuş
dizilerden farklı olarak, tipi belli ve uzunluğu kaynakta sabit bir bloktu; farkı, bu
uzunluğun bu kez sabit bir tanımdan değil bir çalışma zamanı isteğinden gelmesiydi.

## calloc'un Sıfırlaması

`malloc` istediği boyutu ayırır ama içeriğine dokunmaz: döndürdüğü bellek, önceden orada ne
duruyorsa onu taşır ve bu değer **tanımsızdır** — okunması güvenli değildir, bu yüzden ders
bu değeri hiçbir yerde basmıyor. `calloc` aynı isteği iki parametreyle alır — eleman sayısı
ve eleman boyutu — ve döndürdüğü bloğu **sıfırlar**. `sifirlanan blok` gözlemi bunu doğrudan
okuyarak kanıtlıyor: dört `int`'in dördü de, hiçbir atama yapılmadan, **sıfır** değerinde
bulundu.

Bu fark yalnız bir kolaylık değildir. `malloc` ile ayrılan bir blok, ilk kullanımdan önce
elle sıfırlanmazsa, üzerinde okunan her değer önceki bir ayırmanın artığı olabilir —
`calloc` bu riski işlevin kendi sözleşmesine taşır. İki elemanın çarpımı taştığında
(`eleman_sayisi * eleman_boyutu` bir tam sayı sınırını aşarsa) `calloc`'un bunu **kendisinin**
denetlemesi de aynı nedenle önemlidir: `malloc(eleman_sayisi * eleman_boyutu)` yazmak bu
denetimi çağırana bırakır, `calloc(eleman_sayisi, eleman_boyutu)` yazmak denetimi işlevin
içine alır. Sahiplik burada da değişmiyor — sıfırlanan blok da **çağıranın**dır ve `free`
ile bırakılması gerekiyor, ders bunu son satırda yapıyor.

## Yeniden Boyutlandırma Yeni Bir Sahip Doğurmaz

`realloc` (burada `say_buyut` sarmalayıcısı üzerinden) dört işlevin en tuzaklısıdır, çünkü
tek çağrıda üç farklı şey olabilir: blok yerinde büyür, blok başka bir adrese taşınır ve
eski adres bırakılır, ya da istek karşılanamaz ve `NULL` döner. `yeniden boyutlandirma`
gözlemi ilk iki durumu tek akışta gösteriyor: `buyuyen` iki elemanlık ayrıldı, ilk elemanına
**7** yazıldı, sonra sekiz elemana büyütüldü. Çıktıdaki `ilk eleman 7` değeri, büyütme
sırasında adresin değişip değişmediğinden bağımsız olarak, **içeriğin taşındığını**
kanıtlıyor — `realloc` yeni bir yere geçtiyse bile eski içeriği yeni bloğa kendisi kopyaladı.

Sahiplik açısından belirleyici olan şudur: büyütme öncesi `buyuyen`'in sahibi çağırandı,
büyütme sonrası da hâlâ çağırandır — **yeni bir sahip doğmadı**, yalnız aynı sahibin elindeki
adres değişmiş olabilir. Sayaç bunu doğruluyor: `say_buyut(NULL, ...)` çağrısı yeni bir blok
sayıldığı için ayrılan sayacını bir artırdı, ikinci `say_buyut(buyuyen, ...)` çağrısı ise
**yeni bir ayırma saymadı** — `sayac.c`deki `yeni_blok` denetimi tam bunun için var. Gözlemin
sonunda `say_birak(buyuyen)` çağrısı tek bir bloğu bırakıyor ve fark **0**'a dönüyor.

Kodun yapısı, `yeni` değişkenine dikkat çekiyor: `say_buyut`'un dönüşü doğrudan `buyuyen`'in
üzerine yazılmıyor, önce ayrı bir değişkene alınıyor ve `NULL` olup olmadığı denetleniyor.
Bunun nedeni, başarısız bir yeniden boyutlandırmada **eski bloğun hâlâ geçerli ve hâlâ
çağıranın olması**dır: `realloc` isteği karşılayamazsa orijinal bloğu bırakmaz, olduğu gibi
bırakır ve `NULL` döner. Dönüşü doğrudan `buyuyen = say_buyut(buyuyen, ...);` biçiminde
yazmak, başarısızlık durumunda `buyuyen`'in kendisini `NULL`'a çevirir — o andan sonra eski
bloğa ulaşacak hiçbir işaretçi kalmaz ve blok **sahipsiz** hâle gelir, sızıntı olarak
görünürdü. Bu dersin kodu bu hatayı işlemiyor, çünkü istek burada her zaman karşılanabilecek
kadar küçük; ama akış — ayrı değişkene alma, `NULL` denetimi, yalnız başarıyla üzerine yazma
— tam olarak bu ikinci kalıbı da engelleyecek biçimde kuruldu. **Doğru kalıp ile yanlış
kalıp arasındaki fark, bir satırlık bir atamadır**, ama sonucu bir sahibin bloğu hâlâ
bulabilmesi ile tamamen kaybetmesi arasındaki farktır.

## Zincirleme Büyütme

Önceki gözlem `buyuyen`'i tek adımda büyüttü. Ama bir dizinin dolarken tekrar tekrar
büyütülmesi de yaygın bir kalıptır — her eleman eklendiğinde bloğu bir eleman daha büyütmek
yerine, dolduğunda iki katına çıkarmak. Bu kalıbın sahiplik açısından ilginç yanı, kaç kez
büyütülürse büyütülsün bloğun hâlâ **tek bir ayırma** sayılmasıdır.

```c
// zincir.c — art arda buyutmenin sayaci yalniz bir kez artirmasi
#include <stdio.h>
#include "sayac.h"

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

    int kapasite = 1;
    int *dizi = say_buyut(NULL, kapasite * sizeof *dizi);
    int dolu = 0;

    for (int i = 0; i < 5; i++) {
        if (dolu == kapasite) {
            kapasite *= 2;
            int *yeni = say_buyut(dizi, kapasite * sizeof *dizi);
            if (yeni == NULL) {
                break;
            }
            dizi = yeni;
        }
        dizi[dolu] = i + 1;
        dolu++;
    }

    int toplam = 0;
    for (int i = 0; i < dolu; i++) {
        toplam += dizi[i];
    }

    int ara_fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;
    say_birak(dizi);
    int son_fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;

    snprintf(tampon, sizeof tampon,
             "dolu %d, kapasite %d, toplam %d, buyutme sonrasi fark %d, birakma sonrasi fark %d",
             dolu, kapasite, toplam, ara_fark, son_fark);
    printf("%-30s%s\n", "zincirleme buyutme", tampon);

    return 0;
}
```

```
zincirleme buyutme            dolu 5, kapasite 8, toplam 15, buyutme sonrasi fark 1, birakma sonrasi fark 0
```

**BE2.** Döngü `kapasite`'yi 1 → 2 → 4 → 8 olmak üzere üç kez büyüttü, yani `say_buyut`
toplam dört kez çağrıldı (ilk ayırma dahil). Buna karşın büyütmeler bittiğinde sayaç farkı
hâlâ **1**: `sayac.c`deki `yeni_blok` denetimi yalnız `eski == NULL` olduğu ilk çağrıda
`ayrilan`'ı artırıyor, sonraki üç çağrı aynı bloğun **devamı** sayılıyor. Bu, "Yeniden
Boyutlandırma Yeni Bir Sahip Doğurmaz" bölümünde tek adımda gösterilen kuralın, kaç adıma
yayılırsa yayılsın değişmediğini kanıtlıyor: sahiplik zincirin başında kaç kez el
değiştirdiğine değil, kaç kez **yeni** bir ayırmayla başladığına bakar. `say_birak(dizi)`
çağrısından sonra fark **0**'a dönüyor — zincirin sonunda da, tıpkı başında olduğu gibi, tek
bir sahip vardı.

## Küçülen Blok

Zincirleme büyütme bloğu hep genişletti; ama `realloc` aynı sözleşmeyle bloğu
**küçültebilir** de. Küçültme, büyütmenin aynadaki görüntüsü değildir: uygulama isterse
bloğu yerinde küçültür, isterse daha küçük bir adrese taşır, ama iki durumda da sahiplik
sorusunun yanıtı değişmez — küçülen blok da hâlâ çağırana aittir.

```c
// kucul.c — realloc ile kucultme de ayni cagiran sahipligini korur
#include <stdio.h>
#include "sayac.h"

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

    int *genis = say_buyut(NULL, 10 * sizeof *genis);
    for (int i = 0; i < 10; i++) {
        genis[i] = i;
    }
    int ilk_fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;

    int *dar = say_buyut(genis, 2 * sizeof *dar);
    int korunan_ilk = -1, korunan_ikinci = -1;
    if (dar != NULL) {
        korunan_ilk = dar[0];
        korunan_ikinci = dar[1];
    }
    int kuculme_farki = (ayrilan_sayisi() - birakilan_sayisi()) - once;

    say_birak(dar);
    int son_fark = (ayrilan_sayisi() - birakilan_sayisi()) - once;

    snprintf(tampon, sizeof tampon,
             "ilk eleman %d, ikinci eleman %d, buyume farki %d, kuculme farki %d, birakma sonrasi %d",
             korunan_ilk, korunan_ikinci, ilk_fark, kuculme_farki, son_fark);
    printf("%-30s%s\n", "kuculen blok", tampon);
    return 0;
}
```

```
kuculen blok                  ilk eleman 0, ikinci eleman 1, buyume farki 1, kuculme farki 1, birakma sonrasi 0
```

**BE3.** `dar`, `genis`'in ilk on elemanından yalnız ikisini tutacak kadar küçük ayrıldı;
buna karşın `korunan_ilk` ve `korunan_ikinci` değerleri, küçültmeden önce yazılmış **0** ve
**1**'i aynen taşıyor — `realloc` bloğu küçültürken kalan içeriği bozmuyor, yalnız artık
alanı serbest bırakıyor. Sayaç farkı `ilk_fark`'tan `kuculme_farki`'ya hiç değişmedi: ayırma
tek kaldı, büyüme de küçülme de aynı bloğun devamıydı. Bu, "Yeniden Boyutlandırma Yeni Bir
Sahip Doğurmaz" bölümündeki kuralın yönden bağımsız olduğunu gösteriyor — kural büyütmeye
özgü değil, `realloc`'un boyutu **her iki yönde de** değiştirebilmesine dayanıyor.

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

Boyutu **sıfır** olan bir `malloc` ya da `calloc` çağrısı standart tarafından iki yönde de
bırakılmıştır: uygulama ya `NULL` döndürebilir ya da `free` ile bırakılabilecek, boyutu
sıfır olan geçerli bir işaretçi döndürebilir. Bu, ortama bağlı bir davranıştır ve hangisinin
seçildiği burada **basılmaz** — kaynakta okunan tek şey, çağıranın bu iki olasılığın da
gerçekleşebileceğini bilerek yazması gerektiğidir. Dört ayırma işlevinin **sahiplik**
yönünden verdiği güvence değişmez — dönen işaretçi `NULL` değilse sahibi yine çağırandır —
ama bu güvence, boyutu sıfır olan bir istekte dönüşün ne olacağını söylemez.

Aynı belirsizlik `realloc(blok, 0)` çağrısı için de geçerlidir: bazı uygulamalarda bu çağrı
`free(blok)` ile aynı etkiyi yapıp `NULL` döner, bazılarında boyutu sıfır olan geçerli bir
blok döner. İkisi arasındaki fark, çağıranın bloğu **hâlâ elinde tutup tutmadığını**
etkiler — birinci durumda blok zaten bırakılmıştır, ikinci durumda hâlâ çağıranındır ve ayrı
bir `free` çağrısı gerekir. Bu kararsızlık yüzünden bu kursun kodu `realloc`'u hiçbir yerde
sıfır boyutla çağırmıyor; boyutu sıfıra indirmek istendiğinde blok doğrudan `say_birak` ile
bırakılıyor ve işaretçi ayrı olarak `NULL`'a atanıyor. Sahiplik sorusunun ortama bağlı iki
farklı yanıtı olabildiği bir yerde, ders o soruyu hiç sormamayı tercih ediyor.

## Özet

- `malloc`, `calloc` ve `realloc` dönen bloğun sahibi her zaman **çağırandır**; bu dersin
  ölçtüğü bellek sınıfı **çağıran**dır.
- `malloc` içeriği sıfırlamaz, `calloc` sıfırlar — bu fark ölçümde değerler doğrudan
  okunarak gösterildi, tahmin edilmedi.
- `realloc` yeni bir sahip doğurmaz; içerik korunur, sayaç yalnız ilk ayırmada artar.
- Başarısız bir `realloc` eski bloğu bırakmaz; dönüşü doğrudan eski değişkenin üzerine
  yazmak eski bloğu sahipsiz bırakır, bu yüzden dönüş önce ayrı bir değişkende denetlenir.
- `malloc(0)` ve `calloc` ile boyutu sıfır olan bir istek uygulamaya bağlıdır ve bu dersin
  ölçümünde basılmaz.

## Sonraki Adım

Bu ders sahipliğin **nasıl başladığını** ölçtü: bir blok ayrılır ayrılmaz, onu bırakma
yükümlülüğü çağırana geçer. Ama bu yükümlülüğün **nerede yazılı olduğu** henüz sorulmadı —
`say_ayir` ve `say_birak`'ın kendisi hiçbir yerde "bunu sen bırakacaksın" demiyor, bu bilgi
yalnızca işlevin adından ve alışkanlıktan çıkarılıyor. Sıradaki ders bu yükümlülüğü bir
başlık dosyasına yazılı hâle getiriyor ve sahipliğin çağırandan **çağrılana** geçtiği ilk
düzeneği gösteriyor: aynı metni döndüren iki işlevden birinin sahibi bırakan taraf, ötekinin
sahibi çağıran olacak.
