C

23 Ekim 2006

C Programlama Dersi - XI

Bu yazıda öğrenecekleriniz:


Diziler

Bir bilgisayar programı yaptığınızı düşünün. Kullanıcının 100 değer girmesi isteniyor. Girilen bütün bu sayıların farklı aşamalardan geçeceğini ve bu yüzden hepsini ayrı bir değişkende tutmamız gerektiğini varsayalım. Bu durumda ne yapardınız? a0, a1, a2, ..., a99 şeklinde 100 tane değişken tanımlamak elbette mümkün; ama oldukça zahmetli olurdu. Sırf değişkenleri tanımlarken kaybedeceğiniz zamanı düşünürseniz ne demek istediğimi anlarsınız. Bunun için alternatif bir çözümün gerektiği mutlak!

Çok sayıda değişkenin gerektiği durumlarda, diziler imdadımıza yetişir. (Dizi, İngilizce kaynaklarda array olarak geçer.) 100 değişken tanımlamamızın gerektiği yukardaki örneğe dönelim. Tek tek a0, ..., a100 yaparak bunu nasıl yazacağınızı zaten biliyorsunuz. Şimdi tek satırda dizi tanımlayarak, bunu nasıl yapacağımızı görelim:

int a[100];

Yukardaki tek satır, bellek bloğunda 100 adet int değişken yeri ayırır. Tek satır kod elbette açıklayıcı değil, o yüzden bunu aşağıdaki şekille açıklayalım:

[Array Definiton]

Her şeyin başında dizideki elemanların değişken tipini yazıyoruz; buradaki örneğimizde tam sayı gerektiği için 'int' yazdık. Daha sonra diziye 'a' yazarak bir isim veriyoruz. Değişkenleri nasıl isimlendiriyorsak, aynı kurallar diziler için de geçerli... Son aşamada bu dizinin kaç eleman içereceğini belirtiyoruz. Köşeli parantezler ( [  ] ) içinde yazdığımız 100 sayısı, 100 adet int tipinde değişkenin oluşturulmasını sağlıyor.

Bir değişkene ulaşırken, onun adını yazarız. Dizilerde de aşağı yukarı böyle sayılır. Fakat ufak farklar vardır. Bir dizi, birden çok elemandan oluşur. Bu yüzden sadece dizi adını yazmaz, ulaşmak istediğimiz elemanı da yer numarasını yazarak belirtiriz. Örneğin a dizisinin, 25.elemanı gerekiyorsa, a[24] ile çağrılır. Sanırım 25 yerine 24 yazıldığını fark etmişsinizdir. C programlama dilinde, dizilerin ilk elemanı 0'dır. Diziler 0'dan başladığı için, ulaşmak istenilen dizi elemanı hangisiyse bir eksiğini yazarız. Yani a dizisinin 25.elemanı, a[24]; 100.elemanı a[99] ile çağırırız.

Şimdi basit bir örnek yapalım. Bu örnekte, her aya ait güneşli gün sayısı sorulsun ve sonunda yıllık güneşli gün sayısı yazılsın.

#include<stdio.h>
int main( void )
{
	// Aylari temsil etmesi icin 
	// aylar adinda 12 elemanli 
	// bir dizi olusturuyoruz. 
	int aylar[ 12 ];
	int toplam = 0;
	int i;

	// Birinci dongu, deger atamak icindir
	for( i = 0; i < 12; i++ ) {
		printf( "%2d.Ay: ", (i+1) );
		// aylara deger atıyoruz:
		scanf( "%d", &aylar[ i ] );
	}

	// Az evvel girilen degerleri gostermek icin 
	// ikinci bir dongu kurduk
	printf( "\nGİRDİĞİNİZ DEĞERLER\n\n" );
	for( i = 0; i < 12; i++ ) {
		printf( "%2d.Ay için %d girdiniz\n", (i+1), aylar[i] );
		toplam += aylar[ i ];
	}

	printf( "Toplam güneşli gün sayısı: %d\n", toplam );
	return 0;
}

Örneğimizi inceleyelim. En başta 12 elemanlı aylar dizisini, "int aylar[12]" yazarak tanımlıyoruz. Her ay için değer girilmesini gerekiyor. Klavyeden girilen sayıların okunması için elbette scanf(  ) fonksiyonunu kullanacağız ama ufak bir farkla! Eğer 'a' isimde bir değişkene atama yapıyor olsaydık, hepinizin bileceği şekilde "scanf("%d", &a )" yazardık. Fakat dizi elemanlarına atama yapılacağından komutu, "scanf( "%d", &aylar[ i ] )" şeklinde yazmamız gerekiyor. Döngü içindeki i değişkeni, 0'dan 11'e kadar sırasıyla artıyor. Bu sayede, döngünün her adımında farklı bir dizi elemanına değer atıyabiliyoruz. ( i değişkeni, bir nevi indis olarak düşünülebilir. ) Klavyeden okunan değerlerin dizi elemanlarına atanmasından sonra, ikinci döngü başlıyor. Bu döngüde girmiş olduğunuz değerler listelenip, aynı esnada toplam güneşli gün sayısı bulunuyor. Son aşamada, hesaplanan toplam değerini yazdırıp, programı bitiriyoruz.

Dikkat ederseniz, değerlerin alınması veya okunması gibi işlemler döngüler aracılığıyla yapıldı. Bunları döngüler aracılığı ile yapmak zorunda değildik. Mesela "scanf("%d", &aylar[5] )" yazıp, 6.ayın değerini; "scanf("%d", &aylar[9] )" yazıp, 10.ayın değerini klavyeden alabilirdik. Ancak böyle yapmak, döngü kullanmaktan daha zahmetlidir. Yanılgıya düşmemeniz için döngüleri kullanmanın kural olmadığını, sadece işleri kolaylaştırdığını hatırlatmak istedim. Gerek tek tek, gerekse örnekte yaptığımız gibi döngülerle çözüm üretebilirsiniz.

Başka bir örnek yapalım. Kullanıcımız, float tipinde 10 adet değer girsin. Önce bu değerlerin ortalaması bulunsun, ardından kaç adet elemanın ortalamanın altında kaldığı ve kaç adet elemanın ortalamanın üstünde olduğu gösterilsin.

#include<stdio.h>
int main( void )
{
	// Degerleri tutacagimiz 'dizi'
	// adinda bir dizi olusturuyoruz.
	float dizi[ 10 ];
	float ortalama, toplam = 0;
	int ortalama_ustu_adedi = 0;
	int ortalama_alti_adedi = 0;
	int i;

	// Kullanici dizinin elemanlarini giriyor:
	for( i = 0; i < 10; i++ ) {
		printf( "%2d. elemanı giriniz> ", (i+1) );
		scanf( "%f", &dizi[ i ] );
		toplam += dizi[ i ];
	}
	
	// dizinin ortalamasi hesaplaniyor.
	ortalama = toplam / 10.0;
	
	// ortalamadan kucuk ve buyuk elemanlarin
	// kac adet oldugu belirleniyor.
	for( i = 0; i < 10; i++ ) {
		if( dizi[ i ] < ortalama )
			ortalama_alti_adedi++;
		else if( dizi[ i ] > ortalama )
			ortalama_ustu_adedi++;
	}

	// raporlama yapiliyor.
	printf( "Ortalama: %.2f\n", ortalama );
	printf( "Ortalamadan düşük %d eleman vardır.\n", ortalama_alti_adedi ); 
	printf( "Ortalamadan yüksek %d eleman vardır.\n", ortalama_ustu_adedi );

	return 0;
}

Program pek karmaşık değil. Dizi elemanlarını alıyor, ortalamalarını hesaplıyor, elemanları ortalamayla karşılaştırıp, ortalamadan büyük ve küçük elemanların adedini veriyoruz. Anlaşılması güç bir şey bulacağınızı sanmıyorum. Tek karmaşık gelecek nokta, ikinci döngüde neden bir else olmadığı olabilir. Oldukça geçerli bir sebebi var ve if else-if yapısını iyice öğrenenler böyle bırakılmasını anlayacaklardır. Bilmeyenlere gelince... her şeyi ben söylersem, işin tadı tuzu kalmaz; eski konuları gözden geçirmelisiniz.

Dizilere İlk Değer Atama

Değişken tanımı yaparken, ilk değer atamayı biliyoruz. Örneğin "int a = 5;" şeklinde yazacağınız bir kod, a değişkenini oluşturacağı gibi, içine 5 değerini de atayacaktır. ( Bu değişkene, tanımladıktan sonra farklı değerler atayabilirsiniz. ) Benzer şekilde, bir diziyi tanımlarken, dizinin elemanlarına değer atayabilirsiniz. Aşağıda bunu nasıl yapabileceğinizi görebilirsiniz:

int dizi1[ 6 ] = { 4, 8, 15, 16, 23, 42 };
float dizi2[ 5 ] = { 11.5, -1.6, 46.3, 5, 21.56 };

Küme parantezleri içinde gördüğünüz her değer, sırasıyla bir elemana atanmıştır. Örneğin dizi1'in ilk elemanı 4 olurken, son elemanı 42'dir.

Yukardaki tanımlamalarda farkedeceğiniz gibi dizi boyutlarını 6 ve 5 şeklinde belirttik. İlk değer ataması yapacağımız durumlarda, dizinin boyutunu belirtmeniz gerekmez; dizi boyutunu yazıp yazmamak size bağlıdır. Dilerseniz dizi boyutunu belirtmeden aşağıdaki gibi de yazabilirdiniz:

int dizi1[ ] = { 4, 8, 15, 16, 23, 42 };
float dizi2[ ] = { 11.5, -1.6, 46.3, 5, 21.56 };

Derleyici, atanmak istenen değer sayısına bakar ve dizi1 ile dizi2'nin boyutunu buna göre belirler. dizi1 için 6, dizi2 için 5 tane değer belirtmişiz. Bu dizi1 dizisinin 6, dizi2 dizisinin 5 elemanlı olacağını işaret eder.

Değer atamayla ilgili ufak bir bilgi daha aktarmak istiyorum. Aşağıda iki farklı ilk değer atama yöntemi bulunuyor. Yazım farkı olmasına rağmen, ikisi de aynı işi yapar.

int dizi[ 7 ] = { 0, 0, 0, 0, 0, 0, 0 };
int dizi[ 7 ] = { 0 };

Bir diziyi tanımlayın ve hiçbir değer atamadan, dizinin elemanlarını printf(  ) fonksiyonuyla yazdırın. Ortaya anlamsız sayılar çıktığını göreceksiniz. Bir dizi tanımlandığında, hafızada gerekli olan yer ayrılır. Fakat daha önce bu hafıza alanında ne olup olmadığıyla ilgilenilmez. Ortaya çıkan anlamsız sayılar bundan kaynaklanır. Önceden hafızada bulunan değerlerin yansımasını görürsünüz. Modern programlama dillerinin bir çoğunda, dizi tanımladığınızda, dizinin bütün elemanları 0 değeriyle başlar; sizin herhangi bir atama yapmanıza gerek yoktur. C programlama dilindeyse, kendiliğinden bir başlangıç değeri atanmaz. Bunu yapıp yapmamak size kalmıştır. Kısacası işlerin daha kontrolü gitmesini istiyorsanız, dizileri tanımlarken "dizi[ 7 ] = { 0 };" şeklinde tanımlamalar yapabilirsiniz.

İlk değer atanmasıyla ilgili bir örnek yapalım. 10 elemanlı bir diziye atadığımız ilk değerin maksimum ve minimum değerleri gösterilsin:

#include<stdio.h>
int main( void ) 
{
	// dizi'yi tanitirken, ilk deger 
	// atiyoruz
	int dizi[ ] = { 15, 54, 1, 44, 55,
			40, 60, 4, 77, 45 };
	int i, max, min;

	// Dizinin ilk elemanini, en kucuk
	// ve en buyuk deger kabul ediyoruz. 
	// Bunun yanlis olmasi onemli degil; 
	// sadece bir noktadan kontrole baslamamiz 
	// gerektiginden boyle bir secim yaptik. 
	min = dizi[ 0 ];
	max = dizi[ 0 ];

	for( i = 1; i < 10; i++ ) {
		// min'in degeri, dizi elemanindan 
		// buyukse, min'in degerini degistiririz.
		// Kendisinden daha kucuk sayi oldugunu 
		// gosterir. 
		if( min > dizi[i] )
			min = dizi[i];

		// max'in degeri, dizi elemanindan 
		// kucukse, max'in degerini degistiririz. 
		// Kendisinden daha buyuk sayi oldugunu 
		// gosterir. 
		if( max < dizi[i] )
			max = dizi[i];
	}

	printf( "En Küçük Değer: %d\n", min );
	printf( "En Büyük Değer: %d\n", max ); 

	return 0;
}

Dizilerin fonksiyonlara aktarımı

Dizileri fonksiyonlara aktarmak, tıpkı değişkenleri aktarmaya benzemektedir. Uzun uzadıya anlatmak yerine, örnek üstünden gitmenin daha fayda olacağını düşünüyorum. Bir fonksiyon yazalım ve bu fonksiyon kendisine gönderilen dizinin elemanlarını ekrana yazsın.

#include<stdio.h>
void elemanlari_goster( int [ 5 ] );
int main( void )
{
	int dizi[ 5 ] = { 55, 414, 7, 210, 15 };
	elemanlari_goster( dizi );
	return 0;
}
void elemanlari_goster( int gosterilecek_dizi[ 5 ] )
{
	int i;
	for( i = 0; i < 5; i++) 
		printf( "%d\n", gosterilecek_dizi[ i ] );
}

Fonksiyon prototipini yazarken, dizinin tipini ve boyutunu belirttiğimizi fark etmişsinizdir. Fonksiyonu tanımlama aşamasında, bunlara ilaveten parametre olarak dizinin adını da yazıyoruz. ( Bu isim herhangi bir şey olabilir, kendisine gönderilecek dizinin adıyla aynı olması gerekmez. ) Bir dizi yerine sıradan bir değişken kullansaydık, benzer şeyleri yapacaktık. Farklı olan tek nokta; dizi eleman sayısını belirtmemiz. Şimdi main(  ) fonksiyonuna dönelim ve elemanlari_goster(  ); fonksiyonuna bakalım. Anlayacağınız gibi, "dizi" ismindeki dizinin fonksiyona argüman olarak gönderilmesi için sadece adını yazmamız yeterli.

Fonksiyonlarla ilgili bir başka örnek yapalım. Bu sefer üç fonksiyon oluşturalım. Birinci fonksiyon kendisine gönderilen dizideki en büyük değeri bulsun; ikinci fonksiyon, dizinin en küçük değerini bulsun; üçüncü fonksiyon ise elemanların ortalamasını döndürsün.

#include<stdio.h>
float maksimum_bul( float [ 8 ] );
float minimum_bul( float [ 8 ] );
float ortalama_bul( float [ 8 ] );
int main( void )
{
	// 8 boyutlu bir dizi olusturup buna 
	// keyfi degerler atiyoruz.
	float sayilar[ 8 ] = {  12.36, 4.715, 6.41, 13,
				1.414, 1.732, 2.236, 2.645 };
	float max, min, ortalama;
	// Ornek olmasi acisindan fonksiyonlar 
	// kullaniliyor.
	max = maksimum_bul( sayilar );
	min = minimum_bul( sayilar );
	ortalama = ortalama_bul( sayilar );
	printf( "Maksimum: %.2f\n", max );
	printf( "Minimum: %.2f\n", min );
	printf( "Ortalama: %.2f\n", ortalama );

	return 0;
}
/* Dizi icindeki maksimum degeri bulur */
float maksimum_bul( float dizi[ 8 ] )
{
	int i, max;
	max = dizi[0];
	for( i = 1; i < 8; i++ ) {
		if( max < dizi[ i ] )
			max = dizi[ i ];
	}
	return max;
}
/* Dizi icindeki minimum degeri bulur */ 
float minimum_bul( float dizi[ 8 ] )
{
	int i, min;
	min = dizi[ 0 ];
	for( i = 1; i < 8; i++ ) {
		if( min > dizi[ i ] ) {
			min = dizi[ i ];
		}
	}
	return min;
}
/* Dizi elemanlarinin ortalamasini bulur */
float ortalama_bul( float dizi[ 8 ] )
{
	int i, ortalama = 0;
	for( i = 0; i < 8; i++ )
		ortalama += dizi[ i ];

	return ortalama / 8.0;
}

Yukardaki örneklerimizde, dizilerin boyutlarını bilerek fonksiyonlarımızı yazdık. Ancak gerçek hayat böyle değildir; bir fonksiyona farklı farklı boyutlarda diziler göndermeniz gerekir. Mesela ortalama_bul(  ) fonksiyonu hem 8 elemanlı bir diziye hizmet edecek, hem de 800 elemanlı bir başka diziye uyacak şekilde yazılmalıdır. Son örneğimizi her boyutta dizi için kullanılabilecek hâle getirelim ve ortalama_bul(  ), minimum_bul(  ) ve maksimum_bul(  ) fonksiyonlarını biraz değiştirelim.

#include<stdio.h>
float maksimum_bul( float [ ], int );
float minimum_bul( float [ ], int );
float ortalama_bul( float [ ], int );
int main( void )
{
	// 8 boyutlu bir dizi olusturup buna 
	// keyfi degerler atiyoruz.
	float sayilar[ 8 ] = {  12.36, 4.715, 6.41, 13,
				1.414, 1.732, 2.236, 2.645 };
	float max, min, ortalama;
	// Ornek olmasi acisindan fonksiyonlar 
	// kullaniliyor.
	max = maksimum_bul( sayilar, 8 );
	min = minimum_bul( sayilar, 8 );
	ortalama = ortalama_bul( sayilar, 8 );
	printf( "Maksimum: %.2f\n", max );
	printf( "Minimum: %.2f\n", min );
	printf( "Ortalama: %.2f\n", ortalama );

	return 0;
}
/* Dizi icindeki maksimum degeri bulur */
float maksimum_bul( float dizi[ ], int eleman_sayisi )
{
	int i;
	float max;
	max = dizi[0];
	for( i = 1; i < eleman_sayisi; i++ ) {
		if( max < dizi[ i ] )
			max = dizi[ i ];
	}
	return max;
}
/* Dizi icindeki minimum degeri bulur */ 
float minimum_bul( float dizi[ ], int eleman_sayisi )
{
	int i;
	float min;
	min = dizi[ 0 ];
	for( i = 1; i < eleman_sayisi; i++ ) {
		if( min > dizi[ i ] ) {
			min = dizi[ i ];
		}
	}
	return min;
}
/* Dizi elemanlarinin ortalamasini bulur */
float ortalama_bul( float dizi[ ], int eleman_sayisi )
{
	int i;
	float ortalama = 0;
	for( i = 0; i < eleman_sayisi; i++ )
		ortalama += dizi[ i ];

	return ortalama / 8.0;
}

Fonksiyonlara dikkatlice bakın. Geçen sefer sadece dizi adını gönderirken, artık dizi adıyla birlikte boyutunu da yolluyoruz. Böylece dizinin boyutu n'olursa olsun, fark etmiyor. Yeni bir parametre açıp dizinin eleman sayısını isterseniz, fonksiyon her dizi için çalışabilir.

Dizilere Pointer ile Erişim

Pointer'ların değişkenleri işaret etmesini geçen dersimizde işlemiştik. Benzer şekilde dizileri de işaret edebilirler. Örneğin, int dizi[6]; şeklinde tanımlanmış bir diziyi, pointer ile işaret etmek istersek, ptr = dizi; yazmamız yeterlidir. Değişkenlerde, değişken adının başına '&' işareti getiriyorduk, fakat dizilerde buna gerek yoktur. Çünkü dizilerin kendisi de bir pointer'dır. Dizilerin hepsi hafıza alanında bir başlangıç noktası işaret eder. Örnek olması açısından bu noktaya 6066 diyelim. Siz "dizi[0]" dediğiniz zaman 6066 ile 6068 arasında kalan bölgeyi kullanırsınız. Ya da "dizi[4]" dediğiniz zaman 6074 ile 6076 hafıza bölgesi işleme alınır.

Bir diziyi işaret eden pointer aynen dizi gibi kullanılabilir. Yani ptr = dizi; komutunu vermenizden sonra, ptr[0] ile dizi[0] birbirinin aynısıdır. Eğer *ptr yazarsanız, yine dizinin ilk elemanı dizi[0]'ı işaret etmiş olursunuz. Ancak dizi işaret eden pointer'lar genellikle, *( ptr + 0 ) şeklinde kullanılır. Burada 0 yerine ne yazarsanız, dizinin o elemanını elde ederseniz. Diyelim ki, 5.elemanı ( yani dizi[ 4 ] ) kullanmak istiyorsunuz, o zaman *( ptr + 4 ) yazarsanız. Bir resim, bin sözden iyidir... Aşağıdaki resmi incelerseniz, durumu daha net anlayacağınızı düşünüyorum.

[Pointer to Array Sample]

Gördüğünüz gibi dizi, 6066 numaralı hafıza adresini işaret ediyor. ptr isimli pointer ise, dizi üzerinden 6066 numaralı adresi gösteriyor. Kısacası ikisi de aynı noktayı işaret ediyorlar. Şimdi bunu koda dökelim:

#include<stdio.h>
int main( void )
{
	int i;
	// dizi'yi tanimliyoruz.
	int dizi[ 6 ] = { 4, 8, 15, 16, 23, 42 };
	// ptr adinda bir pointer tanimliyoruz.
	int *ptr;
	// ptr'nin dizi'yi isaret etmesini soyluyoruz.
	ptr = dizi;
	// ptr'in degerini artirip, dizi'nin butun 
	// elemanlarini yazdiriyoruz.
	for( i = 0; i < 6; i++ ) 
		printf( "%d\n", *( ptr + i ) );

	return 0;
}

Pointer'lar farklı şekillerde kullanılabilir. Her defasında, dizinin başlangıç elemanını atamanız gerekmez. Örneğin, ptr = &dizi[ 2 ] şeklinde bir komut kullanarak, dizinin 3.elemanının adresini pointer'a atayabilirsiniz. Pointer'larin değişik kullanım çeşitlerini aşağıda görebilirsiniz:

#include<stdio.h>
int main( void )
{
	int elm;
	int month[ 12 ];
	int *ptr; 
	ptr = month; // month[0] adresini atar
	elm = ptr[ 3 ]; // elm = month[ 3 ] değerini atar 
	ptr = month + 3; // month[ 3 ] adresini atar
	ptr = &month[ 3 ]; // month[ 3 ] adresini atar
	elm = ( ptr+2 )[ 2 ]; // elm = ptr[ 4 ] ( = month[ 7 ] )değeri atanır. 
	elm = *( month + 3 );
	ptr = month;
	elm = *( ptr + 2 ); // elm = month[ 2 ] değerini atar
	elm = *( month + 1 ); // elm = month[ 1 ] değerini atar 

	return 0;
}

Dizilerin fonksiyonlara gönderilmesini görmüştük. Parametre kısmına dizinin tipini ve boyutunu yazıyorduk. Ancak bunun yerine pointer da kullanabiliriz. Örneğin aşağıdaki iki komut satırı birbirinin aynısıdır.

int toplam_bul( int dizi[ ], int boyut );  
int toplam_bul( int *dizi, int boyut );

Fonksiyondan Dizi Döndürmek

Fonksiyondan bir diziyi döndürmeden önce önemli bir konuyla başlayalım. Hatırlarsanız fonksiyonlara iki şekilde argüman yolluyorduk. Birinci yöntemde, sadece değer gidiyordu ve değişken üzerinde bir değişiklik olmuyordu. ( Call by Value ) İkinci yöntemdeyse, yollanılan değişken, fonksiyon içersinde yapacağınız işlemlerden etkileniyordu. ( Call by Reference ) Dizilerin aktarılması, referans yoluyla olur. Fonksiyon içersinde yapacağınız bir değişiklik, dizinin aslında da değişikliğe sebep olur. Aşağıdaki örneğe bakalım:

#include<stdio.h>
/* Kendisine verilen dizinin butun 
   elemanlarinin karesini alir */
void karesine_donustur( int [ ], int );
int main( void )
{
	int i;
	int liste[ ] = { 1,2,3,4,5,6,7 };
	for( i = 0; i < 7; i++ ) {
		printf( "%d ", liste[ i ] );
	}
	printf("\n");

	// Fonksiyonu cagiriyoruz. Fonksiyon geriye 
	// herhangi bir deger dondurmuyor. Diziler 
	// referans yontemiyle aktarildigi icin dizinin 
	// degeri bu sekilde degismis oluyor.
	karesine_donustur( liste, 7 );
	for( i = 0; i < 7; i++ ) {
		printf( "%d ", liste[ i ] );
	}
	printf("\n");
	return 0;
}
void karesine_donustur( int dizi[ ], int boyut )
{
	int i;
	for( i = 0; i < boyut; i++ ) {
		dizi[ i ] = dizi[ i ] * dizi[ i ];
	}
}

Gördüğünüz gibi fonksiyon içersinde diziyle ilgili yaptığımız değişikler, dizinin aslına da yansımıştır. Sırada, fonksiyondan dizi döndürmek var.

Aslında fonksiyondan dizi pek doğru bir isimlendirme değil. Gerçekte döndürdüğümüz şey, dizinin kendisi değil, sadece başlangıç adresi oluyor. Dolayısıyla bir dizi döndürdüğümüzü söylemek yerine, Pointer döndürdüğümüzü söyleyebiliriz. Basit bir fonksiyon hazırlayalım; bu fonksiyon kendisine gönderilen iki diziyi birleştirip, tek bir dizi hâline getirsin.

#include<stdio.h>
/* Kendisine verilen iki diziyi birlestirip 
   sonuclari ucuncu bir diziye atar */
int *dizileri_birlestir( int [], int, 
			 int [], int, 
			 int []);
int main( void )
{
	// liste_1, 5 elemanli bir dizidir. 
	int liste_1[5] = { 6, 7, 8, 9, 10 };
	// liste_2, 7 elemanli bir dizidir.
	int liste_2[7] = {13, 7, 12, 9, 7, 1, 14 };
	// sonuclarin toplanacagi toplam_sonuc dizisi
	int toplam_sonuc[13];
	// sonucun dondurulmesi icin pointer tanimliyoruz
	int *ptr;
	int i;

	// fonksiyonu calistiriyoruz.
	ptr = dizileri_birlestir( liste_1, 5, liste_2, 7, 
				  toplam_sonuc );

	// pointer uzerinden sonuclari yazdiriyoruz.
	for( i = 0; i < 12; i++ ) 
		printf("%d ", *(ptr+i) );
	printf("\n");

	return 0;
}
int *dizileri_birlestir( int dizi_1[], int boyut_1, 
			 int dizi_2[], int boyut_2, 
			 int sonuc[] )
{
	int i, k;
	// Birinci dizinin degerleri ataniyor.
	for( i = 0; i < boyut_1; i++ ) 
		sonuc[i] = dizi_1[i];
	
	// Ikinci dizinin degerleri ataniyor.
	for( k = 0; k < boyut_2; i++, k++ ) {
		sonuc[i] = dizi_2[k];
	}
	
	// Geriye sonuc dizisi gonderiliyor.  
	return sonuc;
}

Neyin nasıl olduğunu sanırım anlamışsınızdır. Diziler referans yoluyla gönderilirken ve gönderdiğimiz dizilerin boyutları belliyken, neden bir de işin içine pointer'ları soktuğumuzu sorabilirsiniz. İlerki konumuzda, dinamik yer ayırma konusunu işleyeceğiz. Şimdilik çok lüzumlu gözükmese de, ön hazırlık olarak olarak bunları öğrenmeniz önemli!

Sıralama

Sıralama oldukça önemli bir konudur. Çeşit çeşit algoritmalar ( QuickSort, Insertion, Shell Sort, vs... ) bulunmaktadır. Ben sizlere en basit sıralama yöntemlerinden biri olan, "Bubble Sort" ("Kabarcık Sıralaması") metodundan bahsedeceğim.

Elinizde, {7, 3, 66, 3, -5, 22, -77, 2} elemanlarından oluşan bir dizi olduğunu varsayın. Dizinin en sonuna gidiyorsunuz ve 8.elemanla ( dizi[ 7 ] ), 7.elemanı ( dizi[ 6 ] ) karşılaştırıyorsunuz. Eğer 8.eleman, 7.elemandan küçükse bu ikisi yer değiştiriyor; değilse, bir değişiklik yapmıyorsunuz. Sonra 7.elemanla ( dizi[ 6 ] ) 6.eleman için aynı işlemler yapılıyor. Bu böyle dizinin son elemanına ( dizi[ 0 ] ) kadar gidiyor. Buraya kadar yaptığımız işlemlere birinci aşama diyelim. İkinci aşamada da tamamen aynı işlemleri yapıyorsunuz. Sadece süreç dizinin son elemanına ( dizi[ 0 ] ) kadar değil, ondan bir önceki elemana kadar sürüyor. Kısacası her aşamada, kontrol ettiğiniz eleman sayısını bir azaltıyorsunuz. Aşama sayısı da, dizi eleman sayısının bir eksiği oluyor. Yani bu örneğimizde 7 aşama gerekiyor. Bunu grafik üzerinde anlatmak daha kolay olacağından, linke tıklayın: Bubble Sort Örneği

Konu biraz karmaşık; tek seferde anlaşılmayabilir. Bu dediklerimizi algoritmaya dökelim:

#include<stdio.h>
void dizi_goster( int [ ], int );
void kabarcik_siralamasi( int [ ], int );
int main( void )
{
	int i, j;
	int dizi[ 8 ] = { 7, 3, 66, 3, 
			 -5, 22, -77, 2 };

	// Siralama islemi icin fonksiyonu
	// cagriyoruz.
	kabarcik_siralamasi( dizi, 8 );
	// Sonucu gostermesi icin dizi_gosteri
	// calistiriyoruz.
	dizi_goster( dizi, 8 );
	return 0;
}
// Dizi elemanlarini gostermek icin yazilmis 
// bir fonksiyondur. 
void dizi_goster( int dizi[ ], int boyut )
{
	int i;
	for( i = 0; i < boyut; i++ ) {
		printf("%d ",dizi[i]);
	}
	printf("\n");
}
// Bubble Sort algoritmasina gore, siralama islemi 
// yapar.
void kabarcik_siralamasi( int dizi[ ], int boyut )
{
	int i, j, temp;
	// Ilk dongu asama sayisini temsil ediyor. 
	// Bu donguye gore, ornegin boyutu 8 olan 
	// bir dizi icin 7 asama gerceklesir.
	for( i = 0; i < boyut-1; i++ ) {
		// Ikinci dongu, her asamada yapilan 
		// islemi temsil eder. Dizinin elemanlari 
		// en sondan baslayarak kontrol edilir. 
		// Eger kendisinden once gelen elemandan
		// kucuk bir degeri varsa, elemanlarin 
		// degerleri yer degistirir.
		for( j = boyut - 1; j > i; j-- )  {
			if( dizi[ j ] < dizi[ j - 1 ] ) {
				temp = dizi[ j -1 ];
				dizi[ j - 1 ] = dizi[ j ];
				dizi[ j ] = temp;
			}

		}
	}
}

Çeşitli sıralama algoritmaları CodeCodex Internet sitesinde mevcut. Ayrıca sıralama demoları izlemenizi tavsiye ederim; algoritmaların ne kadar başarılı olduğunu görerek ayırt edebilirsiniz. Burada ve burada güzel bir demo ile bir anlatım sayfası bulunuyor; mutlaka bakın!

Örnek Sorular

Soru 1: Kendisine parametre olarak gelen bir diziyi, yine parametre olarak bir başka diziye ters çevirip atayacak bir fonksiyon yazınız.

Cevap için tıklayınız...

Soru 2: Kendisine parametre olarak gelen bir dizinin bütün elemanlarını, mutlak değeriyle değiştiren programı yazınız.

Cevap için tıklayınız...

Soru 3: int *ptr = &month[ 3 ] şeklinde bir atama yapılıyor. Buna göre, aşağıdakilerden hangileri *ptr değerine eşittir?

a) month;		e) ( month + 3 )[ 0 ];
b) ptr[ 0 ];		f) ( month + 1 )[ 2 ];
c) ptr[ 1 ];		g) *( month + 3 )[ 0 ];
d) *( month + 3 )

Cevap için tıklayınız...

Çağatay ÇEBİ


<< Geri İleri >>