← Kursa Dön
📄 Text · 12 min

C-Style Diziler

Programlama dünyasında en temel veri yapılarından biri dizi (array) kavramıdır. Şimdiye kadar tek tek değişkenler tanımladık — bir int, bir double, bir string. Ama ya 100 öğrencinin notunu saklamak istersen? 100 tane ayrı değişken mi tanımlayacaksın? İşte diziler tam da bu sorunu çözer.

Bu derste C++'ın "eski tarz" dizilerini, yani C-style dizileri öğreneceğiz. Modern C++'ta daha iyi alternatifler var (bir sonraki derste göreceğiz), ama C-style dizileri anlamak şart — çünkü tonla eski kod bunlarla yazılmış ve pointer mantığını kavramak için mükemmel bir başlangıç noktası.


Dizi Nedir?

Bir diziyi yan yana dizilmiş kutular gibi düşün. Her kutunun içinde aynı türden bir değer var ve her kutunun bir numarası (indeksi) var.

Mesela bir apartman düşün: 10 katlı, her katta bir daire. "3. kattaki daire" dediğinde hangi daireyi kastettiğin belli. İşte dizi de böyle — bellekte yan yana duran, numaralandırılmış kutucuklar.

Önemli özellikler:

  • Tüm elemanlar aynı türde olmalı (hepsi int, hepsi double vs.)

  • Boyut derleme zamanında belli olmalı (sabit)

  • Elemanlar bellekte ardışık (contiguous) yerleşir


Dizi Tanımlama ve Başlatma

Temel Söz Dizimi

Bir dizi tanımlamak için türün yanına köşeli parantez içinde boyutu yazarsın:

#include <iostream>
using namespace std;

int main() {
    int notlar[5];  // 5 elemanlı int dizisi (değerler çöp!)

    // Tek tek atama
    notlar[0] = 90;
    notlar[1] = 85;
    notlar[2] = 78;
    notlar[3] = 92;
    notlar[4] = 88;

    cout << "İlk not: " << notlar[0] << endl;  // 90
    cout << "Son not: " << notlar[4] << endl;   // 88

    return 0;
}

Dikkat: indeksler 0'dan başlar. 5 elemanlı bir dizinin geçerli indeksleri 0, 1, 2, 3, 4'tür. notlar[5] yazmak tanımsız davranış (undefined behavior) — program çöker, yanlış değer okur veya dünyayı yakar. Belli olmaz.

Tanımlarken Başlatma (Initialization)

Diziyi tanımlarken değer atayabilirsin:

#include <iostream>
using namespace std;

int main() {
    // Tam başlatma
    int notlar[5] = {90, 85, 78, 92, 88};

    // Boyut belirtmeden — derleyici sayar
    int puanlar[] = {100, 95, 80};  // 3 elemanlı

    // Kısmi başlatma — geri kalanlar 0 olur
    int dizi[10] = {1, 2, 3};  // ilk 3: 1,2,3 — kalan 7 tanesi 0

    // Hepsini sıfırla
    int sifirlar[100] = {};  // tüm elemanlar 0

    // C++17: Class Template Argument Deduction ile
    // (C-style dizilerde yok ama bilgi olsun)

    for (int i = 0; i < 5; i++) {
        cout << notlar[i] << " ";
    }
    cout << endl;

    return 0;
}

💡 İpucu: Diziyi başlatmadan kullanırsan içinde çöp değerler (garbage values) olur. Bu C++'ın "sana güveniyorum, sen ne yaptığını biliyorsun" felsefesinin bir parçası. Her zaman başlat!

Uniform Initialization (C++11)

int dizi[3]{10, 20, 30};       // = işareti olmadan da olur
int dizi2[5]{};                 // hepsi 0
// int hata[3]{1, 2, 3, 4};    // HATA: çok fazla eleman

Diziye Erişim ve Döngü ile Gezinme

İndeks ile Erişim

Dizi elemanlarına [] operatörü ile erişirsin:

#include <iostream>
using namespace std;

int main() {
    int gunler[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

    cout << "Ocak: " << gunler[0] << " gün" << endl;
    cout << "Şubat: " << gunler[1] << " gün" << endl;

    // Değer değiştirme
    gunler[1] = 29;  // Artık yıl!
    cout << "Şubat (artık yıl): " << gunler[1] << " gün" << endl;

    return 0;
}

Klasik for Döngüsü ile Gezinme

#include <iostream>
using namespace std;

int main() {
    int sayilar[] = {10, 20, 30, 40, 50};
    int boyut = 5;

    // Tüm elemanları yazdır
    for (int i = 0; i < boyut; i++) {
        cout << "sayilar[" << i << "] = " << sayilar[i] << endl;
    }

    // Toplam hesapla
    int toplam = 0;
    for (int i = 0; i < boyut; i++) {
        toplam += sayilar[i];
    }
    cout << "Toplam: " << toplam << endl;

    return 0;
}

Range-based for Döngüsü (C++11)

C++11 ile gelen bu döngü, diziyi doğrudan gezmeyi sağlar. İndeksle uğraşmana gerek kalmaz:

#include <iostream>
using namespace std;

int main() {
    int notlar[] = {90, 85, 78, 92, 88};

    // Sadece okuma
    for (int not_degeri : notlar) {
        cout << not_degeri << " ";
    }
    cout << endl;

    // Değer değiştirmek istiyorsan referans kullan
    for (int& not_degeri : notlar) {
        not_degeri += 5;  // herkese 5 puan bonus
    }

    // Kontrol
    for (int n : notlar) {
        cout << n << " ";  // 95 90 83 97 93
    }
    cout << endl;

    return 0;
}

⚠️ Dikkat: Range-based for döngüsünde int n : dizi yazarsan elemanın kopyasını alırsın. Değiştirmek istiyorsan int& n : dizi (referans) kullanmalısın.


Dizi Boyutu ve sizeof Hilesi

C-style dizilerde boyutu öğrenmek için yerleşik bir fonksiyon yok. Ama bir hile var — sizeof operatörü:

#include <iostream>
using namespace std;

int main() {
    int notlar[] = {90, 85, 78, 92, 88};

    // sizeof dizinin TOPLAM byte boyutunu verir
    cout << "Toplam boyut: " << sizeof(notlar) << " byte" << endl;  // 20

    // Bir elemanın boyutu
    cout << "Bir eleman: " << sizeof(notlar[0]) << " byte" << endl;  // 4

    // Eleman sayısı = toplam / bir eleman
    int eleman_sayisi = sizeof(notlar) / sizeof(notlar[0]);
    cout << "Eleman sayısı: " << eleman_sayisi << endl;  // 5

    // Döngüde kullanmak
    for (int i = 0; i < sizeof(notlar) / sizeof(notlar[0]); i++) {
        cout << notlar[i] << " ";
    }
    cout << endl;

    return 0;
}

std::size() — Modern Yol (C++17)

C++17'den itibaren std::size() fonksiyonunu kullanabilirsin:

#include <iostream>
#include <iterator>  // std::size için
using namespace std;

int main() {
    int notlar[] = {90, 85, 78, 92, 88};

    cout << "Eleman sayısı: " << size(notlar) << endl;  // 5

    for (size_t i = 0; i < size(notlar); i++) {
        cout << notlar[i] << " ";
    }
    cout << endl;

    return 0;
}

💡 İpucu: std::size() hem daha okunabilir hem de daha güvenli. sizeof hilesi fonksiyon parametresi olarak geçen dizilerde çalışmaz (birazdan göreceğiz). std::size() da çalışmaz ama en azından derleme hatası verir, sessizce yanlış sonuç vermez.


Dizinin Fonksiyona Geçirilmesi (Decay to Pointer)

İşte C-style dizilerin en tuzaklı konusu. Bir diziyi fonksiyona parametre olarak geçirdiğinde, dizi pointer'a dönüşür (decay). Yani fonksiyon dizinin kendisini değil, ilk elemanının adresini alır.

Bu ne demek? Fonksiyonun içinde sizeof hilesi çalışmaz — çünkü artık elinde bir dizi değil, bir pointer var.

Sorunun Gösterimi

#include <iostream>
using namespace std;

void yanlis_boyut(int dizi[]) {
    // Bu dizinin boyutunu DEĞİL, pointer'ın boyutunu verir!
    cout << "Fonksiyon içi sizeof: " << sizeof(dizi) << endl;  // 8 (64-bit sistemde)
}

int main() {
    int notlar[] = {90, 85, 78, 92, 88};
    cout << "Main içi sizeof: " << sizeof(notlar) << endl;  // 20

    yanlis_boyut(notlar);
    return 0;
}

Doğru Yol: Boyutu Ayrı Parametre Olarak Geç

#include <iostream>
using namespace std;

void dizi_yazdir(int dizi[], int boyut) {
    for (int i = 0; i < boyut; i++) {
        cout << dizi[i] << " ";
    }
    cout << endl;
}

double ortalama_hesapla(int dizi[], int boyut) {
    int toplam = 0;
    for (int i = 0; i < boyut; i++) {
        toplam += dizi[i];
    }
    return static_cast<double>(toplam) / boyut;
}

int main() {
    int notlar[] = {90, 85, 78, 92, 88};
    int boyut = sizeof(notlar) / sizeof(notlar[0]);

    dizi_yazdir(notlar, boyut);
    cout << "Ortalama: " << ortalama_hesapla(notlar, boyut) << endl;

    return 0;
}

Fonksiyona Geçen Dizi Değiştirilebilir

Dizi pointer olarak geçtiği için, fonksiyon içinde yapılan değişiklikler orijinal diziyi etkiler:

#include <iostream>
using namespace std;

void hepsini_ikiyle_carp(int dizi[], int boyut) {
    for (int i = 0; i < boyut; i++) {
        dizi[i] *= 2;  // orijinal dizi değişir!
    }
}

void dizi_yazdir(const int dizi[], int boyut) {
    // const ile işaretlersek değiştirilemez
    for (int i = 0; i < boyut; i++) {
        cout << dizi[i] << " ";
    }
    cout << endl;
}

int main() {
    int sayilar[] = {1, 2, 3, 4, 5};

    cout << "Önce: ";
    dizi_yazdir(sayilar, 5);

    hepsini_ikiyle_carp(sayilar, 5);

    cout << "Sonra: ";
    dizi_yazdir(sayilar, 5);  // 2 4 6 8 10

    return 0;
}

Değiştirilmesini istemiyorsan const anahtar kelimesini kullan. Bu "ben sadece okuyacağım, değiştirmeyeceğim" demek.


C-Style Dizilerde Sık Yapılan Hatalar

1. Sınır Dışı Erişim (Out of Bounds)

int dizi[5] = {1, 2, 3, 4, 5};

// TEHLİKE! Tanımsız davranış
cout << dizi[5] << endl;   // geçerli indeksler 0-4
cout << dizi[-1] << endl;  // negatif indeks de sorun
dizi[10] = 42;             // başka bellek alanına yazıyorsun!

C++ sana sınır kontrolü yapmaz. Bu senin sorumluluğun. Java veya Python'dan geliyorsan bu seni şaşırtabilir — oralarda hemen hata alırsın, C++'ta ise program "çalışıyor gibi" yapıp sessizce yanlış sonuç üretebilir.

2. Değişken Boyutlu Dizi (VLA) Kullanımı

int n;
cin >> n;
int dizi[n];  // Bazı derleyicilerde çalışır ama STANDART DEĞİL!

Bu sözdizimi C99'da var ama C++ standardında yok. GCC izin verir (extension olarak), ama taşınabilir kod yazmak istiyorsan kullanma. Dinamik boyut istiyorsan std::vector kullan.

3. Dizi Atama ve Karşılaştırma

int a[3] = {1, 2, 3};
int b[3] = {1, 2, 3};

// b = a;           // HATA! Dizi ataması yapılamaz
// if (a == b) ...  // Adresleri karşılaştırır, içerikleri DEĞİL!

C-style dizileri ne kopyalayabilirsin ne de == ile karşılaştırabilirsin. Bu işlemler için ya döngü yazarsın ya da std::array / std::vector kullanırsın.


Pratik Örnek: En Büyük ve En Küçük Elemanı Bulma

#include <iostream>
#include <climits>  // INT_MAX, INT_MIN
using namespace std;

int main() {
    int sicakliklar[] = {23, 18, 31, 27, 15, 29, 22};
    int boyut = sizeof(sicakliklar) / sizeof(sicakliklar[0]);

    int en_buyuk = INT_MIN;  // olabilecek en küçük int
    int en_kucuk = INT_MAX;  // olabilecek en büyük int

    for (int i = 0; i < boyut; i++) {
        if (sicakliklar[i] > en_buyuk) {
            en_buyuk = sicakliklar[i];
        }
        if (sicakliklar[i] < en_kucuk) {
            en_kucuk = sicakliklar[i];
        }
    }

    cout << "En sıcak gün: " << en_buyuk << "°C" << endl;  // 31
    cout << "En soğuk gün: " << en_kucuk << "°C" << endl;   // 15

    return 0;
}

Pratik Örnek: Diziyi Ters Çevirme

#include <iostream>
using namespace std;

void ters_cevir(int dizi[], int boyut) {
    for (int i = 0; i < boyut / 2; i++) {
        int gecici = dizi[i];
        dizi[i] = dizi[boyut - 1 - i];
        dizi[boyut - 1 - i] = gecici;
    }
}

int main() {
    int sayilar[] = {1, 2, 3, 4, 5};
    int boyut = 5;

    cout << "Önce: ";
    for (int s : sayilar) cout << s << " ";
    cout << endl;

    ters_cevir(sayilar, boyut);

    cout << "Sonra: ";
    for (int s : sayilar) cout << s << " ";
    cout << endl;  // 5 4 3 2 1

    return 0;
}

Pratik Örnek: Lineer Arama

#include <iostream>
using namespace std;

int ara(const int dizi[], int boyut, int hedef) {
    for (int i = 0; i < boyut; i++) {
        if (dizi[i] == hedef) {
            return i;  // bulundu, indeksi döndür
        }
    }
    return -1;  // bulunamadı
}

int main() {
    int notlar[] = {90, 85, 78, 92, 88};
    int boyut = 5;

    int sonuc = ara(notlar, boyut, 78);
    if (sonuc != -1) {
        cout << "78 bulundu, indeks: " << sonuc << endl;
    } else {
        cout << "Bulunamadı" << endl;
    }

    return 0;
}

C-Style Dizilerin Sınırlamaları

C-style dizileri anlaman önemli ama günlük kodda bunları kullanmak için pek neden yok. İşte neden:

SorunAçıklama
Boyut sabitDerleme zamanında belli olmalı, çalışma zamanında büyüyüp küçülemez
Sınır kontrolü yokdizi[100] yazsan bile hata vermez, sessizce bellek bozar
Boyut bilgisi kayboluyorFonksiyona geçince pointer'a dönüşür, boyut bilgisi uçar
Kopyalama yok= ile kopyalayamazsın
Karşılaştırma yok== adresleri karşılaştırır, içerikleri değil
Iterator desteği yokSTL algoritmaları ile doğrudan kullanımı sınırlı

Bu sorunların hepsini çözen iki modern alternatif var: std::array (sabit boyut) ve std::vector (dinamik boyut). Bunları bir sonraki derste göreceğiz.

⚠️ Tavsiye: Yeni kod yazarken C-style dizi yerine std::array veya std::vector kullan. C-style dizileri sadece eski kodları okurken veya düşük seviyeli (low-level) işler yaparken karşına çıkacak.


Özet

  • C-style dizi, bellekte yan yana duran aynı türde elemanlar topluluğudur. int dizi[5] gibi tanımlanır.

  • İndeksler 0'dan başlar; 5 elemanlı dizide geçerli indeksler 0–4'tür.

  • Dizi boyutunu öğrenmek için sizeof(dizi)/sizeof(dizi[0]) hilesi veya C++17'deki std::size() kullanılır.

  • Fonksiyona geçince dizi pointer'a dönüşür (decay); bu yüzden boyut bilgisi kaybolur, ayrı parametre olarak geçirilmelidir.

  • C-style diziler kopyalanamaz, == ile karşılaştırılamaz ve sınır kontrolü yapılmaz.

  • Modern C++'ta std::array ve std::vector tercih edilmelidir; C-style diziler eski kodları anlamak için bilinmelidir.