çok boyutlu diziler (multi-dimensional arrays)

C dilinde iki ya da daha çok boyuta sahip diziler tanımlanabilir:

a iki boyutlu bir dizi. Aşağıda ismi ar olan üç boyutlu bir dizi tanımlanıyor:

Uygulamalarda daha çok kullanılan iki boyutlu dizilerdir. İki boyutlu diziler “matris” olarak da isimlendirilir.

C dilinde çok boyutlu bir dizi aslında elemanları dizi olan bir dizi olarak ele alınır. Tanımda birinci köşeli parantez içine yazılan sabit ifadesi dizinin boyutu, diğer köşeli parantezler içine yazılan sabit ifadeleri ise dizinin tür bilgisinin bileşenleridir. Aşağıdaki tanımlamalara bakalım:

a dizisi her elemanı iki boyutlu dizi olan 5 elemanlı bir dizidir. a dizisinin boyutu birinci köşeli parantezin içine yazılan değeridir. a dizisi, b dizisi gibi 5 elemana sahip bir dizidir.
b dizisi her elemanı tek boyutlu dizi olan 10 elemanlı bir dizidir. b dizisinin boyutu birinci köşeli parantezin içine yazılan 10 değeridir. b dizisi, c dizisi gibi 10 elemana sahip bir dizidir.
c dizisi her elemanı int türden olan 20 elemanlı bir dizidir. c dizisinin boyutu köşeli parantezin içine yazılan 10 değeridir. c dizisi d gibi 20 elemana sahip bir dizidir.

Yukarıdaki tanımlamaya dayanarak derleyici dizisinin yeri (storage) olarak bellekte

byte büyüklüğünde bir blok ayırır. Bu dizi de tüm diğer diziler gibi bellekte tek bir blok halinde (contiguous) bulunur:

Yukarıdaki kodu int türünün 4 byte olduğu benim çalıştığım sistemde derleyip çalıştırdığımda çıktısı şu şekilde oldu:

a ifadesi elemanları 20 öğelik int dizi olan 10 elemana sahip diziye
a[0]
ifadesi a dizisinin ilk elemanı olan 20 elemanlı int diziye
a[0] [0] ifadesi ise a dizisinin ilk elemanı olan 20 elemanlı dizinin ilk elemanı olan int türden nesneye karşılık gelmektedir.

Çok boyutlu dizilerin tanımlanmasında typedef bildirimlerinden de faydalanılabilir:

Yukarıdaki kodda bir typedef bildirimiyle 5 elemanlı int dizi türüne Intar5 eş ismi veriliyor. Daha sonra elemanları Intar5 türünden boyutu 10 olan bir dizi tanımlanıyor. a dizisi typedef bildirimi olmadan aşağıdaki gibi tanımlanabilirdi:

ar bir dizi ismi olmak üzere

ifadesinin değeri ar dizisinin boyutudur, değil mi?

İki boyutlu bir dizinin elemanlarıyla bu dizinin temsil ettiği matrisin elemanlarını birbirlerine karıştırmak, sık yapılan hatalardandır:

5 x 10 boyutunda bir tamsayı matrisinin 50 elemanı vardır. Ancak yukarıdaki a dizisi yalnızca 5 elemana sahiptir. Matristeki tamsayılar a dizisinin elemanı olan dizilerin elemanlarıdır. a dizisinin elemanları matrisin satırlarıdır.
Yukarıdaki kodda ar isimli dizide yer alan toplam 50 int nesnenin herhangi birine nasıl erişilebilir? Bunun için bir gösterici (pointer) işleci olan köşeli parantez işleci dizi ismiyle birlikte iki kez kullanılabilir. En yüksek öncelik seviyesinde bulunan köşeli parantez işlecinin öncelik yönü soldan sağadır (left associative):

ifadesiyle ar dizisinin 3 indisli elemanı olan 10 elemanlı int diziye erişilir.

ifadesiyle de ar dizisinin 3 indisli elemanı olan 10 elemanlı int dizinin 4 indisli elemanına erişilir.

çok boyutlu dizilere ilk değer verilmesi

Çok boyutlu dizilere de ilk değer verilebilir. Tüm dizilerde olduğu gibi çok boyutlu bir dizinin elemanlarına da küme parantezi içinde virgüllerle ayrılan bir liste (comma separated list) ile ilk değer verilir:

a dizisinin elemanları da dizi olduğu için küme parantezi içinde yeniden küme parantezlerinin kullanıldığını görüyorsunuz. İstenirse ilk değerleri (initializer) içeren bloğun içinde içsel bloklar kullanılmadan da ilk değer verilebilir. Bu durumda derleyici verilen ilk değerleri sırasıyla, eleman olan dizilerin elemanları ile eşler:

Kodu derleyip çalıştırdığınızda şöyle bir ekran çıktısı elde edeceksiniz:

Aşağıdaki kodda ise 3 boyutlu bir tamsayı dizisine ilk değer veriliyor:

Bir diziye ilk değer verilmesi durumunda ilk değer verilmeyen dizi elemanlarının 0 değeriyle hayata başlatıldığını hatırlayalım. Yukarıdaki program derlenip çalıştırıldığında ekran çıktısı şöyle oldu:

çok boyutlu dizilerin adresleri

Aşağıdaki gibi bir dizimiz olsun:

Bu dizinin adresi nedir? Bu dizinin ilk elemanı a[0]‘dır. a[0], 64 elemanlı int türden bir dizidir. Şimdi dizinin ilk elemanının adresinin alındığını düşünelim:

Bu ifadenin türü nedir? C dilini yeni öğrenmekte olanlar böyle bir ifadenin türünün

olması gerektiğini düşünebilirler. Oysa bu adresin türü

olarak ifade edilir.
Bir dizinin ilk elemanının adresine 1 topladığımızda dizinin ikinci elemanının adresini elde ederiz, değil mi? Bu durumda, a dizisinin ilk elemanının adresini tutacak bir gösterici de 1 arttırıldığında a dizisinin 2. elemanının adresi elde edilmelidir. Yani adres

kadar artmalıdır. Böyle bir gösterici değişken aşağıdaki gibi tanımlanır:

a dizisinin ilk elemanının adresi bu türden bir gösterici değişkene ilk değer verebilir:

Bir dizinin ismi bir ifade içinde kullanıldığında otomatik olarak dizinin ilk elemanının adresine dönüştürülür (array to pointer conversion), değil mi? O zaman

ifadesi yerine doğrudan a ifadesi de yazılabilir:

Bu durum şöyle de ifade edilebilir: pa, int türden 10 elemanlı bir diziyi gösteren gösterici değişkendir. pa herhangi bir boyuttaki int türden bir diziyi değil, yalnızca 10 elemanlı int türden bir diziyi gösterebilir. pa‘nın değeri örneğin ++ işleci ile 1 artırılırsa, bellekte yer alan bir sonraki 64 elemanlı int türden diziyi gösterecek hale gelir. Yani

adresinin sayısal bileşeni pa adresinin sayısal bileşeninden sizeof(int) * 64 kadar daha büyüktür.
pa + 1 ifadesi iki boyutlu a dizisinin ikinci elemanı olan 64 elemanlı int türden dizinin adresine karşılık gelir. Aşağıdaki koda bakalım:

int türünün 4 byte olduğu bir sistemde örnek bir çıktı şöyle olabilir:

Şimdi de aşağıdaki koda bakalım:

main işlevi içinde a isimli iki boyutlu diziye ilk değer verildiğini görüyorsunuz.

deyimiyle ptr göstericisine a dizisinin ilk elemanı olan dizinin ilk elemanının adresiyle ilk değer veriliyor. Bu deyim yerine

yazılsaydı C’de geçerli olmakla birlikte yanlış kabul edilecek ve C++ dilinde de bu kod geçersiz olacaktı. Çünkü a dizi ismi derleyici tarafından a dizisinin ilk elemanının adresine dönüştürülecekti. Bu adresin türünün

olduğunu hatırlayalım. İlk değer verme deyimi aşağıdaki gibi de yazılabilirdi:

Yazdığımız kod iki boyutlu bir dizinin elemanlarının bellekte ardışık (contiguous) olmasından faydalanıyor. ptr göstericisi a dizisinin ilk elemanı olan dizinin son elemanını yani

nesnesini gösterdiği zaman onu 1 arttırdığımızda, a dizisinin ikinci elemanı olan dizinin ilk elemanını yani

nesnesini gösteriyor olacak. Buradan şu anlaşılmalıdır: Çok boyutlu dizinin elemanları  aslında belleğe ardışık olarak yerleştirilen tek boyutlu bir dizi olarak kullanılabilir.

iki boyutlu dizilerin işlevlere gönderilmesi

İki boyutlu bir dizinin işleve gönderilmesi, tek boyutlu dizilerin işleve gönderilmesinden farklı değildir. Bir diziyi bir işleve göndermek için dizinin ilk elemanının adresi ile dizinin boyutu işleve argüman olarak gönderilir, değil mi? Bu durumda işlevin bir parametre değişkeni, dizinin başlangıç adresini tutmaya uygun türden bir gösterici olmalıdır. İşlevin diğer parametresi de dizinin boyutunu alabilir. Aşağıdaki programı inceleyin:

Yukarıdaki programda fillMatrix isimli işlev, adresini ve boyutunu aldığı, elemanları int[COL] türünden olan dizileri, rastgele değerlerle dolduruyor. displayMatrix isimli işlev ise bu tür dizileri bir matris formunda standart çıkış akımına yazdırıyor.

biçiminde tanımlanan bir gösterici, bir işlevin parametre değişkeni olarak kullanıldığında

biçiminde de yazılabilir. Yani aşağıdaki iki bildirim aslında derleyici açısından birbirine eşdeğerdir:

Aşağıdaki gibi tanımlanmış dizilerimiz olsun:

a ve b dizilerinin türleri farklıdır. Bu dizileri işleyecek işlevlerin parametrik yapıları da farklı olmalıdır. Ancak b ve c dizilerinin türleri aynı, boyutları farklıdır. Bu iki diziyi aynı parametrik yapıda bir işlev işleyebilir. Şimdi akla şöyle soru gelebilir: Türleri birbirinden farklı çok sayıda iki boyutlu dizimiz (matrisimiz) olsun. Her farklı türde bir matris için ayrı bir işlev mi tanımlayacağız? İşimizi kolaylaştırmak için iki ayrı yol izleyebiliriz:

i) Bu işlevlerin kodlarını kendimiz yazmak yerine bu işi önişlemci programa (preprocessor) yaptırabiliriz. (Bu seçeneği başka bir yazımda ele alacağım.)
ii) İki boyutlu bir dizinin bellekte tek bir blokta bulunmasından faydalanarak bu diziyi tek boyutlu bir diziymiş gibi işleyecek tek bir işlev yazabiliriz:

displayMatrix işlevi kendisini çağıran koddan, elemanlarını yazdıracağı matrisin adresini, satır ve sütun sayısını alıyor.
Ancak bu parametrik yapıda bir işlev tanımlandığında bu işlev müşteri kodlar tarafından iki boyutlu bir dizinin ismiyle çağrılmamalıdır. Böyle bir çağrı C’de yanlış kabul edilirken C++ dilinde de tür farklılığı yüzünden sentaks hatası olarak değerlendirilir. İşlev aşağıdaki biçimlerde çağrılabilir:

Yazılacak işlevin void * türden bir bir parametreye sahip olması işlevin doğrudan dizi ismi ile çağrılmasını mümkün kılar. Ancak bu durumda derleyicinin yapabileceği tür uyumu kontrollerini de devre dışı bırakmış oluruz:

char türden iki boyutlu diziler

Nasıl bir yazı char türden bir dizide tutulabiliyor ise, mantıksal bir ilişki içindeki birden fazla yazı, elemanları char dizi olan iki boyutlu bir dizi içinde tutulabilir:

Yukarıda tanımlanan names isimli dizinin 10 elemanı vardır. names isimli dizinin her bir elemanı char türden 50 elemanlı bir dizidir. names dizisinde uzunluğu 49 karakteri geçmeyen, 10 yazı tutulabilir.

Bu yazılardan dördüncüsünü tutan dizidir ve bir ifade içinde kullanıldığında derleyici names[3] dizisini bu dizinin adresine dönüştürür. Aşağıdaki kodu inceleyelim:

main işlevi içinde iki boyutlu names isimli bir dizi tanımlanıyor:

Bu dizi içinde uzunluğu en fazla 19 karakter olabilecek ARRAY_SIZE sayıda isim tutulabilir, değil mi?

döngü deyimiyle pnames isimli bir gösterici dizisinin (pointer array) elemanlarının gösterdiği isimler, iki boyutlu names dizisinin elemanları olan char türden dizilere standart strcpy işleviyle kopyalanıyor. Aşağıdaki döngü deyimiyle ise her bir isim standart çıkış akımına yazdırılıyor:

Aşağıdaki döngü deyimi ile bir POSIX işlevi olan strrev‘e yapılan çağrılarla, iki boyutlu dizi içinde tutulan isimlerin hepsi ters çevriliyor:

char türden iki boyutlu dizilere ilkdeğer verilmesi

char türden bir diziye dizgelerle ilk değer verilebileceğine göre, iki boyutlu bir dizinin elemanları olan char türden tek boyutlu dizilere de benzer biçimde ilk değer verilebilir:

Yukarıda, names isimli iki boyutlu dizinin elemanları olan, 10 elemanlı char türden dizilere, string sabitleri (string literals) ilk değer veriliyor.

Şimdi de iki boyutlu char türden bir dizi üzerinde işlem yapacak bazı işlevler tanımlayalım:

Yukarıdaki kodda tanımlanan swap_str işlevi adreslerini aldığı iki yazıyı takas ediyor.
display_names isimli işlev ise başlangıç adresini ve boyutunu aldığı iki boyutlu dizide yer alan isimleri ekrana yazdırıyor.
sort_names isimli işlev ise başlangıç adresini ve boyutunu aldığı iki boyutlu dizi içinde tutulan isimleri küçükten büyüğe doğru sıralıyor.

char * türden diziyle char türden iki boyutlu dizi arasındaki farklar

Mantıksal ilişki içinde n tane yazı, bir gösterici dizisi yardımıyla tutulabileceği gibi iki boyutlu bir dizi içinde de tutulabilir:

Yukarıdaki diziler birbirinden tamamen farklıdır: pnames isimli dizinin elemanları

türdendir. Bu dizinin elemanları yazıları değil yazıların başlangıç adreslerini tutar. Yukarıdaki bildirimle tanımlanan pnames dizisi dizge sabitlerinin (string literals) adreslerini tutmaktadır. Dizge sabitlerinin yalnızca okuma amacıyla kullanılabilecek yazılar olduğunu anımsamalısınız. Derleyicinin yaptığı tür kontrolünden geçse de pnames dizisinin elemanlarının gösterdiği yazıları değiştirme girişimi çalışma zamanı hatasına neden olur. Diğer taraftan pnames dizisine ilk değer olarak verilen string sabitleri statik ömürlüdür. Yani programın sonuna kadar bellekte tutulurlar.
Ancak names dizisinin elemanları göstericiler değil char dizilerdir. names dizisi const anahtar sözcüğüyle tanımlanmadığı için bu dizinin elemanlarında turulan yazılar değiştirilebilir. names dizisine ilk değer vermede kullanılan string sabitlerinden  derleyici yalnızca derleme zamanında faydalanır. Bu yazılar programın çalışma zamanında names dizisi dışında başka bir bellek alanında tutulmazlar.

Share

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Kod Eklemek İçin Okuyun