Diziler

C'de bir isim altında toplanan aynı tipteki değişkenlerin oluşturduğu yapılara dizi denir. Bir dizinin içerisindeki değişkenlerin adresleri sıralı şekildedir.


Tek Boyutlu Diziler

Tek boyutlu bir dizi şu şekilde tanımlanır:

veri_tipi değişken_ismi[dizideki eleman sayısı];

Örneğin short int tipinde 5 değişkenden oluşan grade isimli değişkenin tanımlanması şu şekildedir:

short int grade[5];

C89 standartlarında dizinin boyutu bir sabitle belirtilmelidir. C99'da ise sabit yerine başka bir ifade de kullanılabilir. Dizilerin içerisindeki tekil elemanlara ise aşağıdaki kalıp ile erişilir:

değişken_ismi[indis]

Burada indisin kullanılmasında bir nüans vardır. Dizinin ilk elemanının indisi 0'dır. Bu nedenle seçilecek eleman kaçıncıysa, indise o değerin bir düşüğü girilmelidir. Misalen yukarıda tanımladığımız grade dizisi grade[0], grade[1], grade[2], grade[3] ve grade[4] olmak üzere 5 elemandan oluşur ve bu dizinin üçüncü elemanına 0 değeri atanmak istenirse bu aşağıdaki şekilde yapılır:

grade[2]=0;

Örneğin aşağıdaki kod bir diziye 1'den 100'e kadar olan sayıları atamakta ve 50. elemanın değerini yazdırmaktadır.

#include <stdio.h>

int main(void){
    int a[100];
    int i;
    for (i=0;i<100;i++){
        a[i]=i+1;
    }
    printf("%d",a[49]);
    return 0;
}

Yukarıdaki örnekte örneğin a[1000] elemanına ulaşmaya yarayacak kod da yazılabilirdi. Bu durumda atanan değer dizi için ayrılan belleğin dışında bir yerlere yazılacaktır ve bu da programın yanlış çalışmasına sebep olabilir. C'nin bu tür durumlarda sınırları kontrol etmediği göz önüne alınırsa dizi ve elemanlarıyla çalışırken dikkatli olunmalıdır.

Bir dizinin ilk elemanının adresini almak için kısa bir yol mevcuttur. Aşağıdaki üç örneği inceleyelim:

#include <stdio.h>

int main(void){
    int a=3;
    int *i;
    i=&a;
    printf("%d",*i);
    return 0;
}

#include <stdio.h>

int main(void){
    int a[100];
    a[0]=3;
    int *i;
    i=&a[0];
    printf("%d",*i);
    return 0;
}

#include <stdio.h>

int main(void){
    int a[100];
    a[0]=3;
    int *i;
    i=a;
    printf("%d",*i);
    return 0;
}

Üç örnekte de aynı işlem yapılmıştır. Önce bir değişken tanımlanıp değer atanmış, sonra bir işaretçi tanımlanmış, ardından bu işaretçi vasıtasıyla değişkenin adresi alınmış ve son olarak da o adresteki değer yazdırılmıştır.

En üstteki örnekte normal bir değişkenin adresi alınmıştır. Ortadaki örnekte bir dizinin ilk elamanının adresi aynı bir değişkenin adresi alınır gibi alınmıştır. Son örnekte ise bir dizinin ilk elemanının adresini alırken kullanılabilecek bir kısa yöntem gösterilmiştir. Görüldüğü gibi bir dizinin ilk elemanının adresi parantezler olmaksızın sadece dizinin adı yazılarak alınabilir.

Dizilerin elemanları tek tek fonksiyonlara argüman olarak verilebilir fakat bir dizi bir fonksiyona argüman olarak verilemez. Bununla birlikte tek bir ifadeyle bir dizinin işaretçilerini fonksiyona argüman olarak vermek mümkündür.

...
int a[10];
f1(a);
...

Misalen yukarıdaki örnekte f1 fonksiyonuna argüman olarak a dizisinin işaretçisi verilmiştir.

Argüman olarak tek boyutlu bir dizinin işaretçisini alacak olan bir fonksiyon aşağıdaki şekillerde tanımlanabilir:

veri_tipi fonksiyonun_ismi(veri_tipi *dizinin_ismi)
veri_tipi fonksiyonun_ismi(veri_tipi dizinin_ismi[eleman_sayısı])
veri_tipi fonksiyonun_ismi(veri_tipi dizinin ismi[ ])

Örneğin çıktı olarak int tipinde bir değer verecek olan, argüman olarak da int tipindeki elemanlardan oluşan, 10 elamanlı x dizisinin işaretçisini alacak olan bir f1 fonksiyonu şu şekillerde tanımlanabilir:

int f1(int *x)
int f1(int x[10])
int f1(int x[ ])

Fonksiyonun değer veya işaretçi almasının sonuçlarını daha ileriki bölümlerde işleyeceğiz.

Çok sık kullanılan tek boyutlu dizilerden birisi de dizgilerdir. 0 ile sonlanan karakter dizilerine dizgi denmektedir. Dizgi tanımlarken depolanması istenen karakter sayısından bir fazlası kullanılmalıdır ki son byte 0 olabilsin.


Çok Boyutlu Diziler

C birden fazla boyutlu dizilere de izin vermektedir. Aşağıda sırayla iki ve üç boyutlu dizilerin tanımlanmasına örnekler verilmiştir:

int a[5][5];
int b[5][5][2];

Bu dizilerdeki tekil elemanlara da tek boyutlu dizilerdekine benzer şekilde erişilir:

a[0][3]=5;
b[2][2][1]=10;

Çok boyutlu dizilerdeki elemanların adresleri de yine ardışıktır. Adres öncelikle soldaki boyut sabitken en sağdaki boyuta göre değişir.

Örneğin iki boyutlu c[2][2] ve üç boyutlu d[2][2][2] dizilerinde adreslerin sırası şu şekildedir:

c[0][0], c[0][1], c[1][0], c[1][1]
d[0][0][0], d[0][0][1], d[0][1][0], d[0][1][1], d[1][0][0], d[1][0][1], d[1][1][0], d[1][1][1]

Çok boyutlu dizilerin işaretçileriyle fonksiyon tanımlanacağında en soldaki boyut haricindeki boyutların büyüklükleri belirtilmelidir. Bu fonksiyonun hangi işaretçiden sonra diğer boyutlara geçeceğini bilmesini sağlar.

Örnek:

int func(int x[ ][3][2][2]){
....
}

İstenirse en baştaki boyutun büyüklüğü de belirtilebilir.

Bunlarla birlikte iki boyutlu karakter dizileri de dizgi dizileri olarak kullanılabilir. Örneğin "char a[10][20]" dizisi herbiri en fazla 19 karakter uzunluğunda 10 dizgiyi depolamak için kullanılabilir. Örnekteki dizideki beşinci dizginin ilk karakterinin adresine kısaca ulaşmak için "a[4]" ifadesi kullanılabilir.


İşaretçileri İndisleme

Daha önceden tek boyutlu bir dizinin sadece ismini yazarak ilk elemanının adresine erişebileceğimizi söylemiştik. Şimdi bu durumu biraz daha genelleştirelim:

a[a1][a2]...[aN] N boyutlu bir dizi olmak üzere a[b1][b2]...[bK] yazılarak a[b1][b2]...[bK][0][0]...[0] elemanının adresine erişilebilir.

Örnek olarak;

a[10] için "a" a[0]'ın adresini verir.
b[5][5] için "b" b[0][0]'ın, "b[2]" b[2][0]'ın adresini verir.
c[5][5][2] için "c" c[0][0][0]'ın, "c[2]" c[2][0]'ın, "c[2][1]" c[2][1][0]'ın adresini verir.

İşaretçiler de diziler gibi insilenebilir. Örneğin "p" isimli bir işaretçi ilan edilmiş olsun. Bu durumda p[3] şeklinde bir işaretçi değişkeni ile işlem yapılabilir. Bu değişken p işaretçisinin tuttuğu adresin 3 ilerisindeki adreste tutulan değişkendir.

#include <stdio.h>

int main(void){
    int a[2][2][2];
    int *p;
    p=a;
    p[3]=15;
    printf("%d",a[0][1][1]);
    return 0;
}

Yukarıdaki örneği inceleyecek olursak p işaretçisi a[0][0][0]'ın adresini tutmaktadır. Bu adresin 3 ilerisinde tutulan değişken a[0][1][1]'dir. "p[3]=15;" satırı p işaretçisinin tuttuğu adresin 3 ilerisindeki adrese 15 değerinin yazılmasını sağlamıştır. Bu da a[0][1][1]'i 15 yapmıştır. Bu örnek kod aşağıdakiyle aynı işlemi yapmaktadır.

#include <stdio.h>

int main(void){
    int a[2][2][2];
    int *p;
    p=a;
    *(p+3)=15;
    printf("%d",a[0][1][1]);
    return 0;
}

Üstteki iki örnekteki işaretçi tanımlamaları bu örnekteki kullanım için uygun olsa da aslında birden fazla boyutlu dizilerde doğru bir yöntem değildir. Çünkü dizinin ismi dizi işaretçisidir, tanımlanan işaretçinin de dizi işaretçisi olarak tanımlanması uygun olandır. Bir diziyi işaret edecek olan işaretçi dizinin en soldaki boyutu haricindeki boyutları belirtilerek tanımlanır:

veri_tipi (*işaretçi ismi) [2. boyutun büyüklüğü][3.boyutun büyüklüğü]...;

Böyle yapıldığında işaretçi de diziyle tamamen aynı şekilde indislenebilir:

#include <stdio.h>

int main(void){
    int a[2][2][2];
    int (*p)[2][2];
    p=a;
    p[0][1][1]=15;
    printf("%d",a[0][1][1]);
    return 0;
}

Fakat dizi işaretçilerinde aritmetik işlem yapabilmek için zorlama gereklidir.

#include <stdio.h>

int main(void){
    int a[2][2][2];
    int (*p)[2][2];
    p=a;
    *((int*)p+3)=15;
    printf("%d",a[0][1][1]);
    return 0;
}

Yukarıdaki örnekte dizi işaretçisi olan "p"nin aritmetik işlemde int tipinde bir değişeni işaret eden bir işaretçi gibi davranması istenmiştir. Bu işaretçi aritmetiğinin düzgün çalışabilmesi için gereklidir ve bir işaretçi tanımlamak yerine doğrudan dizinin ismi ile çalışılacağında da kullanılır:

#include <stdio.h>

int main(void){
    int a[2][2][2];
    *((int*)a+3)=15;
    printf("%d",a[0][1][1]);
    return 0;
}

Dizilere İlk Değer Atama

Tek boyutlu dizilere ilk değer şu kalıpla atanabilir:

int a[n]={a1, a2, a3, ..., an};

Kısacası, görüldüğü gibi tırnaklı parantezler arasına dizinin elemanı kadar değer yazılmalı ve değerler arasına virgül konmalıdır.

Dizgiler ise dizgi sabitleri kullanılarak ilk değer alabilirler:

char a[15]="This is a test"

Yukarıdaki başlatma işlemi her karakteri dizginin bir elemanına atayacak ve sona da 0 koyacaktır.

Çok boyutlu dizilerde de ilk değer tek boyutlu dizilerde olduğu gibi atanır. Burada tırnaklı parantez içerisindeki elemanlar diziye işaretçi sırası takip edilerek yerleştirilir. İşaretçilerin nasıl sıralandığı daha önce anlatılmıştı.

Dizi tanımlamalarında en soldaki parantezin içi boş bırakılarak ilk değer atanabilir. Bu durumda o boyut için büyüklük değeri atanan değere göre belirlenecektir. Tüm elemanların sığabileceği en küçük değer o boyutun büyüklüğü olacaktır.