çok boyutlu diziler

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 öğeleri 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 öğesi iki boyutlu dizi olan 5 öğeli bir dizidir. a dizisinin boyutu birinci köşeli parantezin içine yazılan değeridir. a dizisi, b dizisi gibi 5 öğeye sahip bir dizidir.
b dizisi her öğesi tek boyutlu dizi olan 10 öğeli bir dizidir. b dizisinin boyutu birinci köşeli parantezin içine yazılan 10 değeridir. b dizisi, c dizisi gibi 10 öğeye sahip bir dizidir.
c dizisi her öğesi int türden olan 20 öğeli bir dizidir. c dizisinin boyutu köşeli parantezin içine yazılan 10 değeridir. c dizisi d gibi 20 öğeye 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 öğeleri 20 öğelik int dizi olan 10 öğeye sahip diziye
a[0]
ifadesi a dizisinin ilk öğesi olan 20 öğelik int diziye
a[0] [0] ifadesi ise a dizisinin ilk öğesi olan 20 öğelik dizinin ilk öğesi 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 öğeli int dizi türüne Intar5 eş ismi veriliyor. Daha sonra öğeleri 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 öğeleriyle, bu dizinin temsil ettiği matrisin öğelerini birbirlerine karıştırmak, sık yapılan hatalardandır:

5 x 10 boyutunda bir tamsayı matrisinin 50 öğesi vardır. Ancak yukarıdaki a dizisi yalnızca 5 öğeye sahiptir. Matristeki tamsayılar a dizisinin öğesi olan dizilerin öğeleridir. a dizisinin öğeleri 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 öğesi olan 10 öğeli int diziye erişilir.

ifadesiyle de ar dizisinin 3 indisli öğesi olan 10 öğeli int dizinin 4 indisli öğesine 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 öğelerine de küme parantezi içinde virgüllerle ayrılan bir liste (comma separated list) ile ilk değer verilir:

a dizisinin öğeleri de 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, öğe olan dizilerin öğeleri 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 öğelerinin 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 öğesi a[0]‘dır. a[0], 64 öğeli int türden bir dizidir. Şimdi dizinin ilk öğesinin 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 öğesinin adresine 1 topladığımızda dizinin ikinci öğesinin adresini elde ederiz, değil mi? Bu durumda, a dizisinin ilk öğesinin adresini tutacak bir gösterici de 1 arttırıldığında a dizisinin 2. öğesinin 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 öğesinin 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 öğesinin 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 öğeli bir diziyi gösteren gösterici değişkendir. pa herhangi bir boyuttaki int türden bir diziyi değil, yalnızca 10 öğeli 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 öğeli 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 öğesi olan 64 öğeli 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 öğesi olan dizinin ilk öğesinin 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 öğesinin 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 öğelerinin bellekte ardışık (contiguous) olmasından faydalanıyor. ptr göstericisi a dizisinin ilk öğesi olan dizinin son öğesini yani

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

nesnesini gösteriyor olacak. Buradan şu anlaşılmalıdır: Çok boyutlu dizinin öğeleri 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 öğesinin 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ığı, öğeleri 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, öğelerini 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ı, öğeleri char dizi olan iki boyutlu bir dizi içinde tutulabilir:

Yukarıda tanımlanan names isimli dizinin 10 öğesi vardır. names isimli dizinin her bir öğesi char türden 50 öğeli 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) öğelerinin gösterdiği isimler, iki boyutlu names dizisinin öğeleri 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 ilkdeğer verilebileceğine göre, iki boyutlu bir dizinin öğeleri olan char türden tek boyutlu dizilere de benzer biçimde ilk değer verilebilir:

Yukarıda, names isimli iki boyutlu dizinin öğeleri olan, 10 öğeli char türden dizilere, dizgelerle ilkdeğ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 öğeleri

türdendir. Bu dizinin öğeleri 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 öğelerinin 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 dizge sabitleri statik ömürlüdür. Yani programın sonuna kadar bellekte tutulurlar.
Ancak names dizisinin öğeleri göstericiler değil char dizilerdir. names dizisi const anahtar sözcüğüyle tanımlanmadığı için bu dizinin öğelerinde turulan yazılar değiştirilebilir. names dizisine ilk değer vermede kullanılan dizgelerden 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.

Necati Ergin

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

Bunlar da ilginizi çekebilir

çok boyutlu diziler” için bir yorum

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Kod Eklemek İçin Okuyun