---
title: 'Diziler ve İşaretçiler'
source: 'https://academia.sh/tr/kurslar/c-bellek/diziler-ve-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:
    - 'Bir dizi adının bir işleve geçerken ilk elemanının adresine bozunduğunu örnekle gösterir'
    - 'Bozunma sonucu kaybolan uzunluk bilgisinin neden ayrı bir parametreyle taşınması gerektiğini açıklar'
    - 'Dinamik olarak ayrılmış bir dizinin sahipliğinin çağırana geçtiğini sayaçla ölçer'
---

# Diziler ve İşaretçiler

Bir dizi adının bir işleve geçerken ilk elemanının adresine bozunması, uzunluk bilgisinin bu bozunmada neden kaybolduğu ve dinamik olarak ayrılmış bir dizinin sahipliğinin çağırana geçtiği.

İki ders önce çekirdekteki `ayir ve birak` satırı bir bloğu `say_ayir` ile ayırıp
`say_birak` ile bıraktı; sınıfı **çağıran**dı çünkü belleği isteyen taraf onu bırakmakla
yükümlüydü. Bu ders o satırı derinleştiriyor ve önce dizi adlarının işlevlere nasıl
geçtiğini ölçüyor — çünkü dinamik bir diziyi doğru bırakabilmek, önce onun bir işleve
**ne olarak** ulaştığını bilmeyi gerektiriyor.

## Bozunma: Dizi Adı İşleve Geçerken

```c
// bozunma.c — bir dizi adi islev parametresinde ilk elemanin adresine bozunur
#include <stdio.h>

static void goster(int *p, int uzunluk) {
    int toplam = 0;
    for (int i = 0; i < uzunluk; i++) {
        toplam += p[i];
    }
    printf("%s %d\n", "islev icinde toplam", toplam);
    printf("%s %d\n", "islev icinde ilk eleman", *p);
}

int main(void) {
    int dizi[4] = {2, 4, 6, 8};

    printf("%s %d\n", "dizi disinda ilk eleman", *dizi);
    printf("%s %d\n", "dizi ile ilk elemanin adresi esit mi", (int)(dizi == &dizi[0]));

    goster(dizi, 4);

    int *isaretci = dizi;
    printf("%s %d\n", "isaretciye atanan dizi ile ilk eleman esit mi", (int)(isaretci == &dizi[0]));
    return 0;
}
```

```
dizi disinda ilk eleman 2
dizi ile ilk elemanin adresi esit mi 1
islev icinde toplam 20
islev icinde ilk eleman 2
isaretciye atanan dizi ile ilk eleman esit mi 1
```

**IS11.** `dizi == &dizi[0]` karşılaştırması **1** veriyor: bir dizi adı kullanıldığında,
o ad **ilk elemanının adresiyle aynı değeri** taşıyor. `goster(dizi, 4)` çağrısında
`dizi`, işlevin `int *p` parametresine bu adres olarak geçiyor — işlev içinde `p[i]`
yazımı `dizi[i]` ile aynı elemanlara ulaşıyor ve toplam **20** çıkıyor (2+4+6+8).
`isaretci = dizi` ataması da aynı şeyi gösteriyor: bir dizi adı bir işaretçi değişkenine
atandığında, dizinin kendisi değil, ilk elemanının adresi kopyalanıyor. Bu olguya
**bozunma** deniyor: dizi adı, bir işlev parametresinde ya da bir işaretçiye atamada
kendi tipini (dizi) kaybedip bir işaretçi tipine dönüşüyor.

## Kaybolan Uzunluk

```c
// uzunluk_kaybi.c — bozunma, dizinin eleman sayisi bilgisini tasimaz
#include <stdio.h>

static int topla(int *p, int uzunluk) {
    int toplam = 0;
    for (int i = 0; i < uzunluk; i++) {
        toplam += p[i];
    }
    return toplam;
}

int main(void) {
    int kucuk[3] = {1, 1, 1};
    int buyuk[7] = {1, 1, 1, 1, 1, 1, 1};

    printf("%s %d\n", "kucuk dizi toplami", topla(kucuk, 3));
    printf("%s %d\n", "buyuk dizi toplami", topla(buyuk, 7));
    printf("%s %d\n", "buyuk diziye yanlis uzunluk verilirse toplam", topla(buyuk, 3));
    return 0;
}
```

```
kucuk dizi toplami 3
buyuk dizi toplami 7
buyuk diziye yanlis uzunluk verilirse toplam 3
```

**IS12.** `topla` işlevi, kendisine geçen işaretçiden dizinin kaç eleman uzunluğunda
olduğunu **hiçbir şekilde** çıkaramıyor — bu yüzden `uzunluk` ayrı bir parametre olarak
taşınıyor. Son çağrıda `buyuk` dizisi (7 eleman) işleve geçiyor ama `uzunluk` olarak
**3** veriliyor; işlev bu yanlış bilgiye güveniyor ve yalnız ilk üç elemanı topluyor,
sonuç **3** çıkıyor — dizinin gerçek boyutuyla hiçbir ilgisi yok. Bu, bozunmanın
sınırlayıcı sonucudur: bir dizi bir işleve geçtiğinde, uzunluk bilgisi kaynakta ayrı
bir değer olarak **elle taşınmak zorundadır**; işaretçinin kendisinden geri kazanılamaz.

Uzunluğu ayrı bir parametreyle taşımak, C'de karşılaşılan tek yöntem değil. Bir başka
yaygın çözüm, dizinin kendi içine bir **sonlandırıcı değer** yerleştirmektir — dizgilerin
sonundaki `'\0'` tam olarak bu işi görüyor: bir dizgi üzerinde gezinen bir döngü, uzunluğu
ayrı bir parametreden değil, sonlandırıcıya ulaşana kadar ilerleyerek öğreniyor. İki
yöntemin de ortak noktası şu: bozunma sonucu kaybolan bilgi, kaynakta **başka bir biçimde**
geri kazanılmak zorunda — ya bir sayı olarak yanında taşınır, ya da dizinin içine gömülen
bir işaretle bulunur. Üçüncü bir yol yok; işaretçinin kendisi bu bilgiyi hiçbir zaman
taşımaz.

## Dinamik Bir Dizinin Sahipliği

```c
// dinamik_dizi.c — ayrilan bir dizinin sahibi onu isteyen taraftir
#include <stdio.h>
#include <stdlib.h>

static int *dizi_olustur(int uzunluk, int baslangic) {
    int *dizi = malloc((size_t)uzunluk * sizeof *dizi);
    if (dizi == NULL) {
        return NULL;
    }
    for (int i = 0; i < uzunluk; i++) {
        dizi[i] = baslangic + i;
    }
    return dizi;
}

int main(void) {
    int uzunluk = 5;
    int *dizi = dizi_olustur(uzunluk, 10);

    if (dizi == NULL) {
        printf("%s\n", "ayirma basarisiz");
        return 1;
    }

    int toplam = 0;
    for (int i = 0; i < uzunluk; i++) {
        toplam += dizi[i];
    }
    printf("%s %d\n", "dinamik dizi toplami", toplam);
    printf("%s %d\n", "son eleman", dizi[uzunluk - 1]);

    free(dizi);
    dizi = NULL;
    printf("%s %d\n", "birakildiktan sonra bos isaretci mi", (int)(dizi == NULL));
    return 0;
}
```

```
dinamik dizi toplami 60
son eleman 14
birakildiktan sonra bos isaretci mi 1
```

**IS13.** `dizi_olustur` bir dizi **ayırıyor** ama kendisi hiç **bırakmıyor** — bloğu
`main`'e döndürüyor. `main` içindeki toplam **60** (10+11+12+13+14) ve son eleman
**14**, dizinin doğru dolduğunu gösteriyor. Sahiplik burada işlev sınırını aşarak
geçiyor: `dizi_olustur` belleği ayırıyor, ama onu **isteyen** ve kullanan `main`
bırakmakla yükümlü — bu yüzden sınıf **çağıran**. `free(dizi)` çağrısından hemen sonra
`dizi = NULL` atanıyor ve `dizi == NULL` karşılaştırması **1** veriyor; bu, bırakılmış
bir işaretçinin bir sonraki dersin konusu olan tehlikeli durumunu önlüyor.

## Yanlış Uzunluk Sınırın Neresine Kadar Sessiz Kalır

`uzunluk_kaybi.c` örneği yanlış bir uzunluğun eksik bir toplam verdiğini gösterdi, ama
bu yalnız "az sayma" durumuydu. Yanlış uzunluk, dizinin **gerçek sınırını aşan** bir
değer olduğunda ne olur? Aşağıdaki örnek bu soruyu, sınırın içinde kalan ve sınırı aşan
iki çağrıyı yan yana koyarak ölçüyor.

```c
// sinir_asimi.c — dogru uzunluk sinirin icinde kalir, yanlis uzunluk disina tasabilir
#include <stdio.h>

static int en_buyuk(const int *p, int uzunluk) {
    int buyuk = p[0];
    for (int i = 1; i < uzunluk; i++) {
        if (p[i] > buyuk) {
            buyuk = p[i];
        }
    }
    return buyuk;
}

int main(void) {
    int dizi[4] = {3, 2, 9, 7};

    printf("%s %d\n", "dogru uzunlukla en buyuk", en_buyuk(dizi, 4));
    printf("%s %d\n", "eksik uzunlukla en buyuk", en_buyuk(dizi, 2));
    return 0;
}
```

```
dogru uzunlukla en buyuk 9
eksik uzunlukla en buyuk 3
```

**IS14.** Doğru uzunlukla (`4`) çağrıldığında `en_buyuk` dizinin tamamını tarıyor ve
**9**'u buluyor. Eksik bir uzunlukla (`2`) çağrıldığında ise yalnız ilk iki elemana
bakıyor ve **3**'ü döndürüyor — `9` değeri dizide var olmasına rağmen üçüncü sırada
kaldığı için hiç görülmüyor. Bu ders
kasıtlı olarak yalnız **eksik** uzunluk çağrısını içeriyor; gerçek uzunluğu **aşan** bir
değer verilmesi (`en_buyuk(dizi, 6)` gibi) dizinin sonundan sonraki belleği okumaya
çalışır ve bu tanımsız davranıştır — bu yüzden burada hiç koşturulmuyor. İki durumun
ortak noktası şu: `en_buyuk` işlevi kendisine geçen `uzunluk` değerine **koşulsuz
güveniyor**; ne eksik ne fazla bir değeri kendi başına tespit edemiyor, çünkü elindeki
tek bilgi bir başlangıç adresi ve bir sayı.

## Uzunluğu da Birlikte Döndürmek

`dizi_olustur` uzunluğu `main`'in zaten bildiği bir değer olarak aldı ve kendisi hiç
hesaplamadı. Bir işlev, ayırdığı dizinin uzunluğunu **kendisi belirliyorsa** — örneğin bir
filtreleme sonucu, girişten daha kısa bir dizi üretiyorsa — bu uzunluğu çağırana nasıl
bildirir? İşaretçinin kendisi bunu taşımadığına göre, tek yol bir **çıkış parametresi**.

```c
// ciktisi_da_donen.c — ayiran islev, uzunlugu bir cikis parametresiyle geri bildirir
#include <stdio.h>
#include <stdlib.h>

static int *ciftleri_sec(const int *kaynak, int uzunluk, int *sonuc_uzunlugu) {
    int *sonuc = malloc((size_t)uzunluk * sizeof *sonuc);
    if (sonuc == NULL) {
        *sonuc_uzunlugu = 0;
        return NULL;
    }
    int sayac = 0;
    for (int i = 0; i < uzunluk; i++) {
        if (kaynak[i] % 2 == 0) {
            sonuc[sayac] = kaynak[i];
            sayac++;
        }
    }
    *sonuc_uzunlugu = sayac;
    return sonuc;
}

int main(void) {
    int kaynak[6] = {1, 2, 3, 4, 5, 6};
    int uzunluk = 0;

    int *ciftler = ciftleri_sec(kaynak, 6, &uzunluk);
    printf("%s %d\n", "secilen eleman sayisi", uzunluk);

    int toplam = 0;
    for (int i = 0; i < uzunluk; i++) {
        toplam += ciftler[i];
    }
    printf("%s %d\n", "secilenlerin toplami", toplam);

    free(ciftler);
    ciftler = NULL;
    return 0;
}
```

```
secilen eleman sayisi 3
secilenlerin toplami 12
```

**IS15.** `ciftleri_sec` kaynak dizinin **en fazla** `uzunluk` kadar eleman
üretebileceğini biliyor, o yüzden bloğu o boyutta ayırıyor, ama gerçekte kaç elemanın
çift olduğunu ancak taradıktan sonra öğreniyor — bu bilgiyi `*sonuc_uzunlugu` üzerinden
çağırana **geri yazıyor**. `main` bu çağrıdan sonra hem yeni diziyi (`ciftler`) hem onun
gerçek uzunluğunu (`uzunluk`, artık **3**) elinde buluyor; toplam **12** (2+4+6) bu ikisinin
doğru eşleştiğini kanıtlıyor. Sahiplik yine `dizi_olustur` örneğindeki gibi **çağıran**da:
`ciftleri_sec` belleği ayırıyor ama hiç bırakmıyor, `main` onu kullanıp `free` ile
bırakıyor. Bu örnek `dizi_olustur`'dan farklı olarak, uzunluğun **girişten** değil
**işlevin kendi hesabından** geldiği durumu ölçüyor — bu yüzden uzunluk artık bir giriş
parametresi değil, bir çıkış parametresi. `sonuc_uzunlugu`'nun kendisi bir `int *`
olarak geçiyor — bu, işaretçilerin yalnız dizileri değil, tek bir değeri "çağırana geri
yazmak" için de kullanılabildiğini gösteren ayrı bir kullanım biçimi.

## İki Boyutlu Bir Dizinin Bozunması

Tek boyutlu bir dizi bir işleve geçtiğinde `int *`'a bozunuyordu. İki boyutlu bir dizi
geçtiğinde bozunma **bir düzey eksik** işliyor: `int matris[3][4]` bir işlev
parametresinde `int **` olmaz, satır boyutu (4 eleman) tipin bir parçası olarak kalır ve
`int (*)[4]` tipine bozunur.

```c
// matris_bozunmasi.c — int matris[3][4] bir isleve int (*)[4] olarak gecer, int ** olmaz
#include <stdio.h>

static int satir_toplami(int satir[4]) {
    int toplam = 0;
    for (int i = 0; i < 4; i++) {
        toplam += satir[i];
    }
    return toplam;
}

static int tum_toplam(int (*matris)[4], int satir_sayisi) {
    int toplam = 0;
    for (int i = 0; i < satir_sayisi; i++) {
        toplam += satir_toplami(matris[i]);
    }
    return toplam;
}

int main(void) {
    int matris[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    printf("%s %d\n", "ilk satirin toplami", satir_toplami(matris[0]));
    printf("%s %d\n", "tum matrisin toplami", tum_toplam(matris, 3));
    return 0;
}
```

```
ilk satirin toplami 10
tum matrisin toplami 78
```

**IS16.** `tum_toplam` işlevinin parametresi `int (*matris)[4]` —
"dört elemanlık bir dizinin adresi" tipini taşıyor. `matris[i]` yazımı burada `*(matris +
i)` anlamına geliyor ve bu, `i`'inci **satırın** ilk elemanının adresini veriyor; o adres
`satir_toplami`'ye bir kez daha bozunarak (bu kez `int *` olarak) geçiyor. `ilk satirin
toplami` **10** (1+2+3+4), `tum matrisin toplami` **78** (1'den 12'ye kadar tüm
elemanların toplamı) — iki katmanlı bozunmanın doğru elemanlara ulaştığını kanıtlıyor.
Eğer parametre `int **matris` olarak yazılsaydı, derleyici bunu **kabul etmezdi**: `int
(*)[4]` ile `int **` aynı boyutta (bir adres) olsalar da farklı tiplerdir, çünkü ilki
"satır boyutu 4 olan bir dizinin adresi" bilgisini taşırken ikincisi yalnız "bir
işaretçiye işaretçi" demektir — bu bilgi kaybı, derleyicinin `matris[i][j]` erişimini
doğru satıra yönlendirebilmesi için tam olarak gerekli olan bilgidir.

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

Bozunma yalnız **tek düzeyde** işliyor: bir `int dizi[4]` bir işleve geçtiğinde `int *`
olur, ama bir `int matris[3][4]` geçtiğinde `int **` **olmaz** — yukarıdaki
`matris_bozunmasi.c` örneği bunu satır toplamları üzerinden zaten ölçtü. Bu ders çok
boyutlu diziye burada duruyor; sabitlenen kural şu: bozunma her zaman **bir adres**
üretir ve o adresin "kaç elemanlık bir bloğu gösterdiği" bilgisini asla taşımaz. Bu bilgi
ya bir ayrı parametreyle (yukarıdaki `uzunluk` gibi) ya da bir sonlandırıcı değerle
(dizgi sonundaki `'\0'` gibi) kaynakta açıkça taşınmalıdır. `matris_bozunmasi.c`'de bu
bilgi üçüncü bir yoldan taşındı: satır boyutu (**4**) tipin kendisine (`int (*)[4]`)
gömülü kaldığı için ayrı bir parametre gerekmedi, ama satır **sayısı** (**3**) yine de
`tum_toplam`'a ayrı bir `satir_sayisi` parametresiyle geçirilmek zorunda kaldı — çünkü
tip yalnız sütun boyutunu taşıyor, satır sayısını taşımıyor.

## Özet

- Bir dizi adı, bir işleve parametre olarak geçtiğinde ya da bir işaretçiye atandığında
  ilk elemanının adresine **bozunur**; `dizi == &dizi[0]` bu bozunmayı kanıtlıyor.
- Bozunma, dizinin **eleman sayısı bilgisini taşımaz** — yanlış bir uzunluk parametresi
  verildiğinde işlev bunu fark edemiyor, yalnız verilen sayı kadar elemana erişiyor.
- Dinamik olarak ayrılmış bir dizinin sahibi, onu **isteyen ve kullanan taraftır**
  (sınıf **çağıran**); ayıran işlev bloğu geri döndürüp bırakma yükümlülüğünü
  devredebilir.
- Bir bloğu bıraktıktan hemen sonra işaretçiye `NULL` atamak, o işaretçinin artık
  geçerli bir bloğu göstermediğini kaynakta açıkça işaretliyor.
- Çok elemanlı bir bloğun uzunluğu, işaretçinin kendisinden **geri kazanılamaz**; ayrı
  bir değer, bir çıkış parametresi ya da bir sonlandırıcı ile taşınmak zorundadır —
  uzunluk girişten geliyorsa giriş parametresi, işlev onu kendisi hesaplıyorsa çıkış
  parametresi kullanılır, ama sahiplik her iki durumda da `çağıran`'da kalır.
- İki boyutlu bir dizi bir işleve geçtiğinde `int (*)[N]` tipine bozunur, `int **`'a
  değil — satır boyutu tipin bir parçası olarak taşınır, satır sayısı taşınmaz.

Bu dersteki üç yeni ölçüm de aynı gözleme farklı açılardan bağlanıyor: bir dizinin
işaretçiye bozunması, dizinin **kendisi** hakkında hiçbir bilgi taşımıyor — ne
uzunluğunu, ne satır sayısını, ne de nereye kadar okumanın güvenli olduğunu. Bu bilgi
her zaman ayrı bir kanaldan gelmek zorunda: ya bir parametre olarak (`en_buyuk`'teki
`uzunluk`), ya tipin kendisine gömülü olarak (`matris_bozunmasi.c`'deki `int (*)[4]`),
ya da bir çıkış parametresi olarak (`ciktisi_da_donen.c`'deki `sonuc_uzunlugu`). Üçü de
aynı sınırın farklı çözümleri; hiçbiri sınırı ortadan kaldırmıyor, yalnız onu görünür ve
taşınabilir kılıyor.

## Sonraki Adım

Önceki örnekte `free(dizi)` çağrısından hemen sonra `dizi`'ye `NULL` atandı — ama bu
adım atlanırsa ne olur? Bir sonraki ders, Boş ve Sarkan İşaretçiler, bırakılmış bir
bloğu hâlâ gösteren bir işaretçinin ne anlama geldiğini, bu durumun neden hiç
koşturulmadığını ve `NULL` atamanın neden onu **sahipsiz** sınıfından çıkarıp
denetlenebilir bir duruma getirdiğini ölçüyor.
