c programlama

Visual basic , Delphi , C , C++ - c programlama ...

Cevapla
c programlama
Dram-Like isimli Üye şimdilik offline konumundadır

Dram-Like

Webmaster

Standart
Alt 23-02-2009 #11
Sponsorlu Bağlantılar

Bir değişken tanımladığımızda, bellekte gereken alan onun adına rezerve edilir. Örneğin 'int num1' yazılması, bellekte uygun bir yer bulunup, 2 byte'ın, num1 değişkeni adına tutulmasını sağlıyor. Daha sonra num1 değişkenine değer atarsak, ayrılan hafıza alanına 5 sayısı yazılıyor. Aslında, num1 ile ilgili yapacağınız bütün işlemler, 4300 adresiyle 4302 adresi arasındaki bellek hücrelerinin değişmesiyle alakalıdır. Değişken dediğimiz; uygun bir bellek alanının, bir isme revize edilip, kullanılmasından ibarettir.

Bir parantez açıp, küçük bir uyarı da bulunalım. Şeklimizin temsili olduğunu unutmamak gerekiyor. Değişkenlerin bellekteki yerleşimi bu kadar 'uniform' olmayabilir. Ayrıca başlangıç adresini 4300 olarak belirlememiz keyfiydi. Sayılar ve tutulan alanlar değişebilir. Ancak belleğin yapısının, aşağı yukarı böyle olduğunu kabul edebilirsiniz.
[size=12pt]Pointer Mekanizması[/size]

Bir değişkene değer atadığımızda, aslında bellek hücrelerini değiştirdiğimizi söylemiştik. Bu doğru bir tanım ama eksik bir noktası var. Bellek hücrelerini değiştermemize rağmen, bunu direkt yapamaz; değişkenleri kullanırız. Bellek hücrelerine direkt müdahâle Pointer'lar sayesinde gerçekleşir.

Pointer, birçok Türkçe kaynakta 'işaretçi' olarak geçiyor. Direkt çevirirseniz mantıklı. Ancak terimlerin özünde olduğu gibi öğrenilmesinin daha yararlı olduğunu düşünüyorum ve ben Pointer olarak anlatacağım. Bazı yerlerde işaretçi tanımı görürseniz, bunun pointer ile aynı olduğunu bilin. Şimdi gelelim Pointer'in ne olduğuna...

Değişkenler bildiğiniz gibi değer (sayı, karakter, vs...) tutar. Pointer'lar ise adres tutan değişkenlerdir. Bellekten bahsetmiştik; küçük hücrelerin oluşturduğu hafıza bloğunun adreslere ayrıldığını ve değişkenlerin bellek hücrelerine yerleştiğini gördük. İşte pointer'lar bu bellek adreslerini tutarlar.

Pointer tanımlamak oldukça basittir. Sadece değişken adının önüne '*' işareti getiririz. Dikkat edilmesi gereken tek nokta; pointer'ı işaret edeceği değişken tipine uygun tanımlamaktır. Yani float bir değişkeni, int bir pointer ile işaretlemeğe çalışmak yanlıştır! Aşağıdaki örneğe bakalım:

#include<stdio.h>
int main( void )
{
// int tipinde değişken
// tanımlıyoruz:
int xyz = 10, k;
// int tipinde pointer
// tanımlıyoruz:
int *p;

// xyz değişkeninin adresini
// pointer'a atıyoruz.
// Bir değişken adresini '&'
// işaretiyle alırız.
p = &xyz;

// k değişkenine xyz'nin değeri
// atanır. Pointer'lar değer tutmaz.
// değer tutan değişkenleri işaret
// eder. Başına '*' koyulduğunda,
// işaret ettiği değişkenin değerini
// gösterir.
k = *p;

return 0;
}

Kod parçasındaki yorumları okuduğunuzda, pointer ile ilgili fikriniz olacaktır. Pointer adres tutan değişkenlerdir. Şimdiye kadar gördüğümüz değişkeninlerin saklayabildiği değerleri tutamazlar. Sadece değişkenleri işaret edebilirler. Herhangi bir değişkenin adresini pointer içersine atamak isterseniz, değişken adının önüne '&' getirmeniz gerekir. Bundan sonra o pointer, ilgili değişkeni işaret eder. Eğer bahsettiğimiz değişkenin sahip olduğu değeri pointer ile göstermek veya değişken değerini değiştirmek isterseniz, pointer başına '*' getirerek işlemlerinizi yapabilirsiniz. Pointer başına '*' getirerek yapacağınız her atama işlemi, değişkeni de etkileyecektir. Daha kapsamlı bir örnek yapalım:

#include<stdio.h>
int main( void )
{
int x, y, z;
int *int_addr;
x = 41;
y = 12;
// int_addr x degiskenini
// isaret ediyor.
int_addr = &x;
// int_addr'in isaret ettigi
// degiskenin sakladigi deger
// aliniyor. (yani x'in degeri)
z = *int_addr;
printf( "z: %d\n", z );
// int_addr, artik y degiskenini
// isaret ediyor.
int_addr = &y;
// int_addr'in isaret ettigi
// degiskenin sakladigi deger
// aliniyor. (yani y'nin degeri)
z = *int_addr;
printf( "z: %d\n" ,z );

return 0;
}

Bir pointer'in işaret ettiği değişkeni program boyunca sürekli değiştirebilirsiniz. Yukardaki örnekte, int_addr pointer'i, önce x'i ve ardından y'yi işaret etmiştir. Bu yüzden, z değişkenine int_addr kullanarak yaptığımız atamalar, her seferinde farklı sonuçlar doğurmuştur. Pointer kullanarak, değişkenlerin sakladığı değerleri de değiştirebiliriz. Şimdi bununla ilgili bir örnek inceleyelim:

#include<stdio.h>
int main( void )
{
int x, y;
int *int_addr;
x = 41;
y = 12;
// int_addr x degiskenini
// isaret ediyor
int_addr = &x;
// int_addr'in isaret ettigi
// degiskenin degerini
// degistiriyoruz
*int_addr = 479;
printf( "x: %d y: %d\n", x, y );
int_addr = &y;

return 0;
}

Kodu derleyip, çalıştırdığınızda, x'in değerinin değiştiğini göreceksiniz. Pointer başına '*' getirip, pointer'a bir değer atarsanız; aslında işaret ettiği değişkene değer atamış olursunuz. Pointer ise hiç değişmeden, aynı adres bilgisini tutmaya devam edecektir.



[size=12pt]Pointer tutan Pointer'lar[/size]

Pointer'lar, gördüğümüz gibi değişkenleri işaret ederler. Pointer'da bir değişkendir ve onu da işaret edecek bir pointer yapısı kullanılabilir. Geçen sefer ki bildirimden farkı, pointer değişkenini işaret edecek bir değişken tanımlıyorsanız; başına '**' getirmeniz gerekmesidir. Buradaki yıldız sayısı değişebilir. Eğer, pointer işaret eden bir pointer'i işaret edecek bir pointer tanımlamak istiyorsanız, üç defa yıldız ( *** ) yazmanız gerekir. Evet, cümle biraz karmaşık, ama kullanım oldukça basit! Pointer işaret eden pointer'ları aşağıdaki örnekte bulabilirsiniz:

#include<stdio.h>
int main( void )
{
int r = 50;
int *p;
int **k;
int ***m;
printf( "r: %d\n", r );
p = &r;
k = &p;
m = &k;
***m = 100;
printf( "r: %d\n", r );

return 0;
}

Yazmış olduğumuz kod içersinde kimin neyi gösterdiğini grafikle daha iyi anlayabiliriz:


Birbirini gösteren Pointer'ları ilerki derslerimizde, özellikle dinamik bellek tahsis ederken çok ihtiyaç duyacağımız bir yapı. O yüzden iyice öğrenmek gerekiyor.
Referansla Argüman Aktarımı

Fonksiyonlara nasıl argüman aktaracağımızı biliyoruz. Hatırlayacağınız gibi parametrelere değer atıyorduk. Bu yöntemde, kullandığınız argümanların değeri değişmiyordu. Fonksiyona parametre olarak yollanan argüman hep aynı kalıyordu. Fonksiyon içinde yapılan işlemlerin hiçbiri argüman değişkeni etkilemiyordu. Sadece değişken değerinin aktarıldığı ve argümanın etkilenmediği bu duruma, "call by value" veya "pass by value" adı verilir. Bu isimleri bilmiyor olsanız dahi, şu ana kadar ki fonksiyon çalışmaları böyleydi.

Geriye birden çok değer dönmesi gereken veya fonksiyonun içersinde yapacağınız değişikliklerin, argüman değişkene yansıması gereken durumlar olabilir. İşte bu gibi zamanlarda, "call by reference" veya "pass by reference" olarak isimlendirilen yöntem kullanılır. Argüman değer olarak aktarılmaz; argüman olan değişkenin adres bilgisi fonksiyona aktarılır. Bu sayede fonksiyon içersinde yapacağınız her türlü değişiklik argüman değişkene de yansır.

var GGAff = {id:15145,width:728,height:90,direction:"Vertical" ,performance:"high"};GGAff.errorMessage = '';




vbmenu_register("postmenu_243138", true);

Söylediklerimizi uygulamaya dökelim ve kendisine verilen iki sayının yerlerini değiştiren bir fonksiyon yazalım. Yani kendisine a ve b adında iki değişken yollanıyorsa, a'nın değerini b; b'nin değeriniyse a yapsın.

#include<stdio.h>
// Kendisine verilen iki degiskenin
// degerlerini degistirir.
// Parametreleri tanimlarken baslarina
// '*' koyuyoruz.
void swap( int *x, int *y )
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main( void )
{
int a, b;
a = 12;
b = 27;
printf( "a: %d b: %d\n", a, b );
// Argumanları aktarırken, baslarina
// '&' koyuyoruz.
swap(&a, &b);
printf( "a: %d b: %d\n", a, b );

return 0;
}

Referans yoluyla aktarım olmasaydı, iki değişkenin değerlerini fonksiyon kullanarak değiştiremezdik. Eğer yazdığınız fonksiyon birden çok değer döndürmek zorundaysa, referans yoluyla aktarım zorunlu hâle geliyor. Çünkü daha önce işlediğimiz return ifadesiyle sadece tek bir değer döndürebiliriz. Örneğin bir bölme işlemi yapıp, bölüm sonucunu ve kalanı söyleyen bir fonksiyon yazacağımızı düşünelim. Bu durumda, bölünen ve bölen fonksiyona gidecek argümanlar olurken; kalan ve bölüm geriye dönmelidir. return ifadesi geriye tek bir değer vereceğinden, ikinci değeri alabilmek için referans yöntemi kullanmamız gerekir.

#include<stdio.h>
int bolme_islemi( int bolunen, int bolen, int *kalan )
{
*kalan = bolunen % bolen;
return bolunen / bolen;
}
int main( void )
{
int bolunen, bolen;
int bolum, kalan;
bolunen = 13;
bolen = 4;
bolum = bolme_islemi( bolunen, bolen, &kalan );
printf( "Bölüm: %d Kalan: %d\n", bolum, kalan );

return 0;
}


[size=10pt]Fonksiyon Prototipleri[/size]

Bildiğiniz gibi fonksiyonlarımızı, main( ) üzerine yazıyoruz. Tek kısa bir fonksiyon için bu durum rahatsız etmez; ama uzun uzun 20 adet fonksiyon olduğunu düşünün. main( ) fonksiyonu sayfalar dolusu kodun altında kalacak ve okunması güçleşecektir. Fonksiyon prototipleri burada devreye girer.

Bir üstte yazdığımız programı tekrar yazalım. Ama bu sefer, fonksiyon prototipi yapısına uygun olarak bunu yapalım:

#include<stdio.h>
int bolme_islemi( int, int, int * );
int main( void )
{
int bolunen, bolen;
int bolum, kalan;
bolunen = 13;
bolen = 4;
bolum = bolme_islemi( bolunen, bolen, &kalan );
printf( "Bölüm: %d Kalan: %d\n", bolum, kalan );

return 0;
}
int bolme_islemi( int bolunen, int bolen, int *kalan )
{
*kalan = bolunen % bolen;
return bolunen / bolen;
}

bolme_islemi( ) fonksiyonunu, main( ) fonksiyonundan önce yazmadık. Sadece böyle bir fonksiyon olduğunu ve alacağı parametre tiplerini bildirdik. ( İsteseydik parametre adlarını da yazabilirdik ama buna gerek yok. ) Daha sonra main( ) fonksiyonu altına inip, fonksiyonu yazdık.

Öğrendiklerimizi pekiştirmek için yeni bir program yazalım. Fonksiyonumuz, kendisine argüman olarak gönderilen bir pointer'i alıp; bu pointer'in bellekteki adresini, işaret ettiği değişkenin değerini ve bu değişkenin adresini göstersin.

#include<stdio.h>
void pointer_detayi_goster( int * );
int main( void )
{
int sayi = 15;
int *pointer;
// Degisken isaret ediliyor.
pointer = &sayi;
// Zaten pointer oldugu icin '&'
// isaretine gerek yoktur. Eger
// bir degisken olsaydi, basina '&'
// koymamiz gerekirdi.
pointer_detayi_goster( pointer );

return 0;
}
void pointer_detayi_goster( int *p )
{
// %p, bellek adreslerini gostermek icindir.
// 16 tabaninda (Hexadecimal) sayilar icin kullanilir.
// %p yerine, %x kullanmaniz mumkundur.
printf( "Pointer adresi\t\t\t: %p\n", &p );
printf( "İşaret ettiği değişkenin adresi\t: %p\n", p );
printf( "İşaret ettiği değişkenin değeri\t: %d\n", *p );
}

Fonksiyon prototipi, "Function Prototype"dan geliyor. Bunun güzel bir çeviri olduğunu düşünmüyorum. Ama aklıma daha uygun bir şey gelmedi. Öneriniz varsa değiştirebiliriz.

[size=12pt]Rekürsif Fonksiyonlar[/size]

Bir fonksiyon içersinden, bir diğerini çağırabiliriz. Rekürsif fonksiyonlar, fonksiyon içersinden fonksiyon çağırmanın özel bir hâlidir. Rekürsif fonksiyon bir başka fonksiyon yerine kendisini çağırır ve şartlar uygun olduğu sürece bu tekrarlanır. Rekürsif, Recursive kelimesinden geliyor ve tekrarlamalı, yinelemeli anlamını taşıyor. Kelimenin anlamıyla, yaptığı iş örtüşmekte.

Rekürsif fonksiyonları aklımızdan çıkartıp, bildiğimiz yöntemle 1, 5, 9, 13 serisini oluşturan bir fonksiyon yazalım:

#include<stdio.h>
void seri_olustur( int );
int main( void )
{
seri_olustur( 1 );
}
void seri_olustur( int sayi )
{
while( sayi <= 13 ) {
printf("%d ", sayi );
sayi += 4;
}
}

Bu fonksiyonu yazmak oldukça basitti. Şimdi aynı işi yapan rekürsif bir fonksiyon yazalım:

#include<stdio.h>
void seri_olustur( int );
int main( void )
{
seri_olustur( 1 );
}
void seri_olustur( int sayi )
{
if( sayi <= 13 ) {
printf("%d ", sayi );
sayi += 4;
seri_olustur( sayi );
}
}

Son yazdığımız programla, bir önce yazdığımız program aynı çıktıları üretir. Ama birbirlerinden farklı çalışırlar. İkinci programın farkını akış diyagramına bakarak sizler de görebilirsiniz. Rekürsif kullanım, fonksiyonun tekrar tekrar çağrılmasını sağlamıştır.


Daha önce faktöriyel hesabı yapan program yazmıştık. Şimdi faktöriyel hesaplayan fonksiyonu, rekürsif olarak yazalım:

#include<stdio.h>
int faktoriyel( int );
int main( void )
{
printf( "%d\n", faktoriyel(5) );
}
int faktoriyel( int sayi )
{
if( sayi > 1 )
return sayi * faktoriyel( sayi-1 );
return 1;
}

Yukardaki programın detaylı bir şekilde akış diyagramını vermeyeceğim. Ancak faktöriyel hesaplaması yapılırken, adımları görmenizi istiyorum. Adım olarak geçen her kutu, fonksiyonun bir kez çağrılmasını temsil ediyor. Başlangıç kısmını geçerseniz fonksiyon toplamda 5 kere çağrılıyor.


Rekürsif yapılar, oldukça karmaşık olabilir. Fakat kullanışlı oldukları kesin. Örneğin silme komutları rekürsif yapılardan yararlanır. Bir klasörü altında bulunan her şeyle birlikte silmeniz gerekiyorsa, rekürsif fonksiyon kaçınılmazdır. Ya da bazı matematiksel işlemlerde veya arama ( search ) yöntemlerinde yine rekürsif fonksiyonlara başvururuz. Bunların dışında rekürsif fonksiyonlar, normal fonksiyonlara göre daha az kod kullanılarak yazılır. Bunlar rekürsif fonksiyonların olumlu yönleri... Ancak hiçbir şey mükemmel değildir.

Rekürsif fonksiyon kullanmanın bilgisayarınıza bindereceği yük daha fazladır. Faktoriyel örneğine bakın; tam 5 kez aynı fonksiyonu çağırıyoruz ve bu sırada bütün değerler bellekte tutuluyor. Eğer çok sayıda iterasyondan söz ediyorsak, belleğiniz hızla tükenecektir. Rekürsif yapılar, bellekte ekstra yer kapladığı gibi, normal fonksiyonlara göre daha yavaştır. Üstelik kısa kod yazımına karşın, rekürsif fonksiyonların daha karmaşık olduklarını söyleyebiliriz. Anlamak zaman zaman sorun olabiliyor. Kısacası bir programda gerçekten rekürsif yapıya ihtiyacınız olmadığı sürece, ondan kaçınmanız daha iyi!
Örnek Sorular

Soru 1: Aşağıdaki programa göre, a, b ve c'nin değerleri nedir?

#include<stdio.h>
int main( void )
{
float a, b, c;
float *p;
a = 15.4;
b = 48.6;
p = &a;
c = b = *p = 151.52;
printf( "a: %f, b: %f, c: %f\n", a, b, c );
return 0;
}

Cevap :
a: 151.52, b: 151.52, c: 151.52

Soru 2: Fibonnacci serisinde herhangi bir seri elemanın değerini bulmak için f( n ) = f( n - 1 ) + f( n - 2 ) fonksiyonu kullanılır. Başlangıç değeri olarak f( 0 ) = 0 ve f( 1 ) = 1'dir. Bu bilgiler ışığında, verilen n sayısına göre, seride karşılık düşen değeri bulan fonksiyonu rekürsif olarak yazınız.
Fibonacci yi hatırlayalım:
0, +1+1+2+3+5+8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584

1+1=2
1+2=3
2+3=5
3+5=8
5+8=13
8+13=21
13+21=34
21+34=55
........................
bu şekilde toplanıp katlanarak giden sayılarara fibonacci yentemi denir.





Cevap:

#include<stdio.h>
int fibonacci( int );
int main( void )
{
int i;
// Fibonacci serisinin ilk 10 elemani
// yazilacaktir.
for( i = 0; i < 10; i++ ) {
printf( "f(%d)= %d\n", i, fibonacci( i ) );
}
return 0;
}
int fibonacci( int eleman_no )
{
if( eleman_no > 1 ) {
return fibonacci( eleman_no - 1 ) +
fibonacci( eleman_no - 2 ) ;
}
else if( eleman_no == 1 )
return 1;
else
return 0;
}

[size=14pt]C Programlama Dersi - XI[/size]

- Diziler
- Dizilere ilk değer atama

- Dizilerin fonksiyonlara aktarımı

- Dizilere Pointer ile erişim

- Fonksiyondan dizi döndürmek

- Sıralama (Sorting)

- Konuyla ilgili örnek sorular


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:


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;
}
Bu mesajdan alıntı yap
Dram-Like isimli Üye şimdilik offline konumundadır

Dram-Like

Webmaster

Standart
Alt 23-02-2009 #12
                             Sponsorlu Bağlantılar
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, 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, 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, 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.


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[ 3 ] 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 iki güzel demo 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...

#include<stdio.h>
void diziyi_ters_cevir( int[], int[], int );
int main( void )
{
int i;
int liste_1[] = { 6, 7, 8, 9, 10 };
int liste_2[5];

diziyi_ters_cevir( liste_1, liste_2, 5 );

for( i = 0; i < 5; i++ ) {
printf("%d ", liste_2[i]);
}
printf("\n");
}
void diziyi_ters_cevir( int dizi_1[], int dizi_2[], int boyut )
{
int i, k;
for( i = 0, k = boyut - 1; i < boyut; i++, k-- )
dizi_2[k] = dizi_1[i];
}


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...

#include<stdio.h>
void dizi_mutlak_deger( int[], int );
int main( void )
{
int i;
int liste[] = { -16, 71, -18, -4, 10, 0 };

dizi_mutlak_deger( liste, 6 );

for( i = 0; i < 6; i++ ) {
printf("%d ", liste[i]);
}
printf("\n");
}
void dizi_mutlak_deger( int dizi[], int boyut )
{
int i;
for( i = 0; i < boyut; i++ ) {
if( dizi[i] < 0 )
dizi[i] *= -1;
}
}

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...

b, d, e ve f şıkları, *ptr'ye eşittir.

[size=14pt]C Programlama Dersi - XII[/size]
Bu yazıda öğrenecekleriniz:

- Çok boyutlu diziler

- Çok boyutlu dizilere ilk değer atama

- Fonksiyonlara 2 boyutlu dizileri aktarmak

- 2 boyutlu dizilerin hafıza yerleşimi

- Pointer dizileri

- Dinamik bellek yönetimi

- Konuyla ilgili örnek sorular

Çok Boyutlu Diziler

Önceki derslerimizde dizileri görmüştük. Kısaca özetleyecek olursak, belirlediğimiz sayıda değişkeni bir sıra içinde tutmamız, diziler sayesinde gerçekleşiyordu. Bu dersimizde, çok boyutlu dizileri inceleyip, ardından dinamik bellek konularına gireceğiz.


Şimdiye kadar gördüğümüz diziler, tek boyutluydu. Bütün elemanları tek boyutlu bir yapıda saklıyorduk. Ancak dizilerin tek boyutlu olması gerekmez; istediğiniz boyutta tanımlayabilirsiniz. Örneğin 3x4 bir matris için 2 boyutlu bir dizi kullanırız. Ya da üç boyutlu Öklid uzayındaki x, y, z noktalarını saklamak için 3 boyutlu bir diziyi tercih ederiz.

Hemen bir başka örnek verelim. 5 kişilik bir öğrenci grubu için 8 adet test uygulansın. Bunların sonuçlarını saklamak için 2 boyutlu bir dizi kullanalım:

#include<stdio.h>
int main( void )
{
// 5 adet ogrenci icin 8 adet sinavi
// temsil etmesi icin bir ogrenci tablosu
// olusturuyoruz. Bunun icin 5x8 bir matris
// yaratilmasi gerekiyor.
int ogrenci_tablosu[ 5 ][ 8 ];
int i, j;
for( i = 0; i < 5; i++ ) {
for( j = 0; j < 8; j++ ) {
printf( "%d no.'lu ogrencinin ", ( i + 1 ) );
printf( "%d no.'lu sınavı> ", ( j + 1 ) );
// Tek boyutlu dizilerdeki gibi deger
// atiyoruz
scanf( "%d", &ogrenci_tablosu[ i ][ j ] );
}
}

return 0;
}

Bu programı çalıştırıp, öğrencilere çeşitli değerler atadığımızı düşünelim. Bunu görsel bir şekle sokarsak, aşağıdaki gibi bir çizelge oluşur:

(Matris Bilgisini Hatırlayalım:
Aynı sayıda değişkene sahip denklemlerin, değişken sayısı kadar farklı denklemi varsa bunların katsayılarını değişken türlerine göre sıralayıp yazdıktan sonra, eşitliklerini de karşı tarafa alt alta yazarak kurulan eşitlikte çözüm kümesini bulmaya yarayan bir yöntemdir.
Daha fazla bilgi için tıklayınız ---> Dizey - Vikipedi
Bu mesajdan alıntı yap
Dram-Like isimli Üye şimdilik offline konumundadır

Dram-Like

Webmaster

Standart
Alt 23-02-2009 #13
Ve matris burda katsayıların oluşturduğu görüntüye verilen adıdır

Tabloya bakarsak, 1.öğrenci sınavlardan, 80, 76, 58, 90, 27, 60, 85 ve 95 puan almış gözüküyor. Ya da 5.öğrencinin, 6.sınavından 67 aldığını anlıyoruz. Benzer şekilde diğer hücrelere gerekli değerler atanıp, ilgili öğrencinin sınav notları hafızada tutuluyor.

Çok Boyutlu Dizilere İlk Değer Atama
Çok boyutlu bir diziyi tanımlarken, eleman değerlerini atamak mümkündür. Aşağıdaki örneği inceleyelim:

int tablo[3][4] = { 8, 16, 9, 52, 3, 15, 27, 6, 14, 25, 2, 10 };
Diziyi tanımlarken, yukardaki gibi bir ilk değer atama yaparsanız, elemanların değeri aşağıdaki gibi olur:

Satır 0 : 8 16 9 52
Satır 1 : 3 15 27 6
Satır 2 : 14 25 2 10
Çok boyutlu dizilerde ilk değer atama, tek boyutlu dizilerdekiyle aynıdır. Girdiğiniz değerler sırasıyla hücrelere atanır. Bunun nedeni de basittir. Bilgisayar, çok boyutlu dizileri sizin gibi düşünmez; dizi elemanlarını hafızada arka arkaya gelen bellek hücreleri olarak değerlendirir.

Çok boyutlu dizilerde ilk değer atama yapacaksanız, değerleri kümelendirmek iyi bir yöntemdir; karmaşıklığı önler. Örneğin yukarıda yazmış olduğumuz ilk değer atama kodunu, aşağıdaki gibi de yazabiliriz:

int tablo[3][4] = { {8, 16, 9, 52}, {3, 15, 27, 6}, {14, 25, 2, 10} };
Farkedeceğiniz gibi elemanları dörderli üç gruba ayırdık. Bilgisayar açısından bir şey değişmemiş olsa da, kodu okuyacak kişi açısından daha yararlı oldu. Peki ya dört adet olması gereken grubun elemanlarını, üç adet yazsaydık ya da bir-iki grubu hiç yazmasaydık n'olurdu? Deneyelim...

int tablo[3][4] = { {8, 16}, {3, 15, 27} };
Tek boyutlu dizilerde ilk değer ataması yaparken, eleman sayısından az değer girerseniz, kalan değerler 0 olarak kabul edilir. Aynı şey çok boyutlu diziler için de geçerlidir; olması gerektiği sayıda eleman ya da grup girilmezse, bu değerlerin hepsi 0 olarak kabul edilir. Yani üstte yazdığımız kodun yaratacağı sonuç, şöyle olacaktır:

Satır 0 : 8 16 0 0
Satır 1 : 3 15 27 0
Satır 2 : 0 0 0 0

Belirtmediğimiz bütün elemanlar 0 değerini almıştır. Satır 2'ninse bütün elemanları direkt 0 olmuştur; çünkü grup tanımı hiç yapılmamıştır.

Fonksiyonlara 2 Boyutlu Dizileri Aktarmak
İki boyutlu bir diziyi fonksiyona parametre göndermek, tek boyutlu diziyi göndermekten farklı sayılmaz. Tek farkı dizinin iki boyutlu olduğunu belirtmemiz ve ikinci boyutun elemanını mutlaka yazmamızdır. Basit bir örnek yapalım; kendisine gönderilen iki boyutlu bir diziyi matris şeklinde yazan bir fonksiyon oluşturalım:

#include<stdio.h>
/* Parametre tanimlamasi yaparken, iki boyutlu dizinin
satir boyutunu girmemize gerek yoktur. Ancak sutun
boyutunu girmek gerekir.
*/
void matris_yazdir( int [ ][ 4 ], int );
int main( void )
{
// Ornek olmasi acisindan matrise keyfi
// degerler atiyoruz. Matrisimiz 3 satir
// ve 4 sutundan ( 3 x 4 ) olusuyor.
int matris[ 3 ][ 4 ] = {
{10, 15, 20, 25},
{30, 35, 40, 45},
{50, 55, 60, 65} };

// Matris elemanlarini yazdiran fonksiyonu
// cagriyoruz.
matris_yazdir( matris, 3 );

return 0;
}
void matris_yazdir( int dizi[ ][ 4 ], int satir_sayisi )
{
int i, j;
for( i = 0; i < satir_sayisi; i++ ) {
for( j = 0; j < 4; j++ ) {
printf( "%d ", dizi[ i ][ j ] );
}
printf( "\n" );
}
}

Kod içersinde bulunan yorumlar, iki boyutlu dizilerin fonksiyonlara nasıl aktarıldığını göstermeye yetecektir. Yine de bir kez daha tekrar edelim... Fonksiyonu tanımlarken, çok boyutlu dizinin ilk boyutunu yazmak zorunda değilsiniz. Bizim örneğimizde int dizi[ ][ 4 ] şeklinde belirtmemiz bundan kaynaklanıyor. Şayet 7 x 6 x 4 boyutlarında dizilerin kullanılacağı bir fonksiyon yazsaydık tanımlamamızı int dizi[ ][ 6 ][ 4 ] olarak değiştirmemiz gerekirdi. Kısacası fonksiyonu tanımlarken dizi boyutlarına dair ilk değeri yazmamakta serbestsiniz; ancak diğer boyutların yazılması zorunlu! Bunun yararını merak ederseniz, sütun sayısı 4 olan her türlü matrisi bu fonksiyona gönderebileceğinizi hatırlatmak isterim. Yani fonksiyon her boyutta matrisi alabilir, tabii sütun sayısı 4 olduğu sürece...

2 Boyutlu Dizilerin Hafıza Yerleşimi
Dizilerin çok boyutlu olması sizi yanıltmasın, bilgisayar hafızası tek boyutludur. İster tek boyutlu bir dizi, ister iki boyut ya da isterseniz 10 boyutlu bir dizi içersinde bulunan elemanlar, birbiri peşi sıra gelen bellek hücrelerinde tutulur. İki boyutlu bir dizide bulunan elemanların, hafızada nasıl yerleştirildiğini aşağıdaki grafikte görebilirsiniz.
c programlama

Görüldüğü gibi elemanların hepsi sırayla yerleştirilmiştir. Bir satırın bittiği noktada ikinci satırın elemanları devreye girer. Kapsamlı bir örnekle hafıza yerleşimini ele alalım:

#include<stdio.h>
void satir_goster( int satir[ ] );
int main( void )
{
int tablo[5][4] = {
{4, 3, 2, 1},
{1, 2, 3, 4},
{5, 6, 7, 8},
{2, 5, 7, 9},
{0, 5, 9, 0} };
int i, j;

// Dizinin baslangic adresini yazdiriyoruz
printf( "2 boyutlu tablo %p adresinden başlar\n\n", tablo );

// Tablo icersinde bulunan dizi elemanlarinin adreslerini
// ve degerlerini yazdiriyoruz.
printf( "Tablo elemanları ve hafıza adresleri:\n");
for( i = 0; i < 5; i++ ) {
for( j = 0; j < 4; j++ ) {
printf( "%d (%p) ", tablo[i][j], &tablo[i][j] );
}
printf( "\n" );
}

// Cok boyutlu diziler birden fazla dizinin toplami olarak
// dusunulebilir ve her satir tek boyutlu bir dizi olarak
// ele alinabilir. Once her satirin baslangic adresini
// gosteriyoruz. Sonra satirlari tek boyutlu dizi seklinde
// satir_goster( ) fonksiyonuna gonderiyoruz.
printf( "\nTablo satırlarının başlangıç adresleri: \n");
for( i = 0; i < 5; i++ )
printf( "tablo[%d]'nin başlangıç adresi %p\n", i, tablo[i] );

printf( "\nsatir_goster( ) fonksiyonuyla, "
"tablo elemanları ve hafıza adresleri:\n");
for( i = 0; i < 5; i++ )
satir_goster( tablo[i] );
}
// Kendisine gonderilen tek boyutlu bir dizinin
// elemanlarini yazdirir.
void satir_goster( int satir[ ] )
{
int i;
for( i = 0; i < 4; i++ ) {
printf( "%d (%p) ", satir[i], &satir[i] );
}
printf( "\n" );
}
Örnekle ilgili en çok dikkat edilmesi gereken nokta, çok boyutlu dizilerin esasında, tek boyutlu dizilerden oluşmuş bir bütün olduğudur. Tablo isimli 2 boyutlu dizimiz 5 adet satırdan oluşur ve bu satırların her biri kendi başına bir dizidir. Eğer tablo[2] derseniz bu üçüncü satırı temsil eden bir diziyi ifade eder. satir_goster( ) fonksiyonunu ele alalım. Esasında fonksiyon içersinde satır diye bir kavramın olmadığını söyleyebiliriz. Bütün olan biten fonksiyona tek boyutlu bir dizi gönderilmesidir ve fonksiyon bu dizinin elemanlarını yazar.

Dizi elemanlarının hafızadaki ardışık yerleşimi bize başka imkanlar da sunar. İki boyutlu bir diziyi bir hamlede, tek boyutlu bir diziye dönüştürmek bunlardan biridir.

#include<stdio.h>
int main( void )
{
int i;
int tablo[5][4] = {
{4, 3, 2, 1},
{1, 2, 3, 4},
{5, 6, 7, 8},
{2, 5, 7, 9},
{0, 5, 9, 0} };

// Cok boyutlu dizinin baslangic
// adresini bir pointer'a atiyoruz.
int *p = tablo[0];

// p isimli pointer'i tek boyutlu
// bir dizi gibi kullanabiliriz.
// Ayni zamanda p uzerinde yapacagimiz
// degisikler, tablo'yu da etkiler.
for( i = 0; i < 5*4; i++ )
printf( "%d\n", p[i] );

return 0;
}

Daha önce sıralama konusunu işlemiştik. Ancak bunu iki boyutlu dizilerde nasıl yapacağımızı henüz görmedik. Aslında görmemize de gerek yok! İki boyutlu bir diziyi yukardaki gibi tek boyuta indirin ve sonrasında sıralayın. Çok boyutlu dizileri, tek boyuta indirmemizin ufak bir faydası...

Pointer Dizileri

Çok boyutlu dizilerin tek boyutlu dizilerin bir bileşimi olduğundan bahsetmiştik. Şimdi anlatacağımız konuda çok farklı değil. Dizilerin, adresi göstermeye yarayan Pointer'lardan pek farklı olmadığını zaten biliyorsunuz. Şimdi de pointer dizilerini göreceğiz. Yani adres gösteren işaretçi saklayan dizileri...

#include<stdio.h>
int main( void )
{
int i, j;

// Dizi isimleri keyfi secilmistir.
// alfa, beta, gama gibi baska isimler de
// verebilirdik.
int Kanada[8];
int ABD[8];
int Meksika[8];
int Rusya[8];
int Japonya[8];

// Bir pointer dizisi tanimliyoruz.
int *tablo[5];
// Yukarda tanimlanan dizilerin adreslerini
// tablo'ya aktiriyoruz.
tablo[0] = Kanada;
tablo[1] = ABD;
tablo[2] = Meksika;
tablo[3] = Rusya;
tablo[4] = Japonya;

// Tablo elemanlarinin adreslerini gosteriyor
// gibi gozukse de, gosterilen adresler Kanada,
// ABD, Meksika, Rusya ve Japonya dizilerinin
// eleman adresleridir.
for( i = 0; i < 5; i++ ) {
for( j = 0 ; j < 8; j++ )
printf( "%p\n", &tablo[i][j] );
}
return 0;
}

Ülke isimlerini verdiğimiz 5 adet dizi tanımladık. Bu dizileri daha sonra tabloya sırayla atadık. Artık her diziyle tek tek uğraşmak yerine tek bir diziden bütün ülkelere ulaşmak mümkün hâle gelmiştir. İki boyutlu tablo isimli matrise atamasını yaptığımız şey değer veya bir eleman değildir; dizilerin başlangıç adresleridir. Bu yüzden tablo dizisi içersinde yapacağımız herhangi bir değişiklik orijinal diziyi de (örneğin Meksika) değiştirir.
Bu mesajdan alıntı yap
Dram-Like isimli Üye şimdilik offline konumundadır

Dram-Like

Webmaster

Standart
Alt 23-02-2009 #14
Atama işlemini aşağıdaki gibi tek seferde de yapabilirdik:

int *tablo[ ] = { Kanada, ABD, Meksika, Rusya, Japonya };

Şimdi de bir pointer dizisini fonksiyonlara nasıl argüman olarak göndereceğimize bakalım.

#include<stdio.h>
void adresleri_goster( int *[ ] );
int main( void )
{
int Kanada[8];
int ABD[8];
int Meksika[8];
int Rusya[8];
int Japonya[8];

int *tablo[ ] = { Kanada, ABD, Meksika, Rusya, Japonya };

// Adresleri gostermesi icin adresleri_goster( )
// fonksiyonunu cagriyoruz.
adresleri_goster( tablo );

return 0;
}
void adresleri_goster( int *dizi[ ] )
{
int i, j;
for( i = 0; i < 5; i++ ) {
for( j = 0 ; j < 8; j++ )
printf( "%p\n", &dizi[ i ][ j ] );
}
}

Dinamik Bellek Yönetimi

Dizileri etkin bir biçimde kullanmayı öğrendiğinizi ya da öğreneceğinizi umuyorum. Ancak dizilerle ilgili işlememiz gereken son bir konu var: Dinamik Bellek Yönetimi...

Şimdiye kadar yazdığımız programlarda kaç eleman olacağı önceden belliydi. Yani sınıf listesiyle ilgili bir program yazacaksak, sınıfın kaç kişi olduğunu biliyormuşuz gibi davranıyorduk. Programın en başında kaç elemanlık alana ihtiyacımız varsa, o kadar yer ayırıyorduk. Ama bu gerçek dünyada karşımıza çıkacak problemler için yeterli bir yaklaşım değildir. Örneğin bir sınıfta 100 öğrenci varken, diğer bir sınıfta 50 öğrenci olabilir ve siz her ortamda çalışsın diye 200 kişilik bir üst sınır koyamazsınız. Bu, hem hafızanın verimsiz kullanılmasına yol açar; hem de karma eğitimlerin yapıldığı bazı fakültelerde sayı yetmeyebilir. Statik bir şekilde dizi tanımlayarak bu sorunların üstesinden gelemezsiniz. Çözüm dinamik bellek yönetimindedir.

Dinamik bellek yönetiminde, dizilerin boyutları önceden belirlenmez. Program akışında dizi boyutunu ayarlarız ve gereken bellek miktarı, program çalışırken tahsis edilir. Dinamik bellek tahsisi için calloc( ) ve malloc( ) olmak üzere iki önemli fonksiyonumuz vardır. Bellekte yer ayrılmasını bu fonksiyonlarla sağlarız. Her iki fonksiyon da stdlib kütüphanesinde bulunur. Bu yüzden fonksiyonlardan herhangi birini kullanacağınız zaman, programın başına #include<stdlib.h> yazılması gerekir.

calloc( ) fonksiyonu aşağıdaki gibi kullanılır:

isaretci_adi = calloc( eleman_sayisi, her_elemanin_boyutu );

calloc( ) fonksiyonu eleman sayısını, eleman boyutuyla çarparak hafızada gereken bellek alanını ayırır. Dinamik oluşturduğunuz dizi içersindeki her elemana, otomatik olarak ilk değer 0 atanır.

malloc( ) fonksiyonu, calloc( ) gibi dinamik bellek ayrımı için kullanılır. calloc( ) fonksiyonundan farklı olarak ilk değer ataması yapmaz. Kullanımıysa aşağıdaki gibidir:

isaretci_adi = malloc( eleman_sayisi * her_elemanin_boyutu );
Bu kadar konuşmadan sonra işi pratiğe dökelim ve dinamik bellekle ilgili ilk programımızı yazalım:

#include<stdio.h>
#include<stdlib.h>
int main( void )
{
// Dinamik bir dizi yaratmak icin
// pointer kullaniriz.
int *dizi;

// Dizimizin kac elemanli olacagini
// eleman_sayisi isimli degiskende
// tutuyoruz.
int eleman_sayisi;
int i;

// Kullanicidan eleman sayisini girmesini
// istiyoruz.
printf( "Eleman sayısını giriniz> ");
scanf( "%d", &eleman_sayisi );

// calloc( ) fonksiyonuyla dinamik olarak
// dizimizi istedigimiz boyutta yaratiyoruz.
dizi = calloc( eleman_sayisi, sizeof( int ) );

// Ornek olmasi acisindan dizinin elemanlarini
// ekrana yazdiriliyor. Dizilerde yapabildiginiz
// her seyi hicbir fark olmaksizin yapabilirsiniz.
for( i = 0; i < eleman_sayisi; i++ )
printf( "%d\n", dizi[i] );

// Dinamik olan diziyi kullandiktan ve isinizi
// tamamladiktan sonra free fonksiyonunu kullanip
// hafizadan temizlemelisiniz.
free( dizi );

return 0;
}

Yazdığınız programların bir süre sonra bilgisayar belleğini korkunç bir şekilde işgal etmesini istemiyorsanız, free( ) fonksiyonunu kullanmanız gerekmektedir. Gelişmiş programlama dillerinde ( örneğin, Java, C#, vb... ) kullanılmayan nesnelerin temizlenmesi otomatik olarak çöp toplayıcılarla ( Garbage Collector ) yapılmaktadır. Ne yazık ki C programlama dili için bir çöp toplayıcı yoktur ve iyi programcıyla, kötü programcı burada kendisini belli eder.

Programınızı bir kereliğine çalıştırıyorsanız ya da yazdığınız program çok ufaksa, boş yere tüketilen bellek miktarını farketmeyebilirsiniz. Ancak büyük boyutta ve kapsamlı bir program söz konusuysa, efektif bellek yönetiminin ne kadar önemli olduğunu daha iyi anlarsınız. Gereksiz tüketilen bellekten kaçınmak gerekmektedir. Bunun için fazla bir şey yapmanız gerekmez; calloc( ) fonksiyonuyla tahsis ettiğiniz alanı, işiniz bittikten sonra free( ) fonksiyonuyla boşaltmanız yeterlidir. Konu önemli olduğu için tekrar ediyorum; artık kullanmadığınız bir dinamik dizi söz konusuysa onu free( ) fonksiyonuyla kaldırılabilir hâle getirmelisiniz!

Az evvel calloc( ) ile yazdığımız programın aynısını şimdi de malloc( ) fonksiyonunu kullanarak yazalım:

#include<stdio.h>
#include<stdlib.h>
int main( void )
{
// Dinamik bir dizi yaratmak icin
// pointer kullaniriz.
int *dizi;
// Dizimizin kac elemanli olacagini
// eleman_sayisi isimli degiskende
// tutuyoruz.
int eleman_sayisi;
int i;

printf( "Eleman sayısını giriniz> ");
scanf( "%d", &eleman_sayisi );

// malloc( ) fonksiyonuyla dinamik olarak
// dizimizi istedigimiz boyutta yaratiyoruz.
dizi = malloc( eleman_sayisi * sizeof( int ) );

for( i = 0; i < eleman_sayisi; i++ )
printf( "%d\n", dizi[i] );

// Dinamik olan diziyi kullandiktan ve isinizi
// tamamladiktan sonra free fonksiyonunu kullanip
// hafizadan temizlemelisiniz.
free( dizi );

return 0;
}

Hafıza alanı ayırırken bazen bir problem çıkabilir. Örneğin bellekte yeterli alan olmayabilir ya da benzeri bir sıkıntı olmuştur. Bu tarz problemlerin sık olacağını düşünmeyin. Ancak hafızanın gerçekten ayrılıp ayrılmadığını kontrol edip, işinizi garantiye almak isterseniz, aşağıdaki yöntemi kullanabilirsiniz:

dizi = calloc( eleman_sayisi, sizeof( int ) );
// Eger hafiza dolmussa dizi pointer'i NULL'a
// esit olacak ve asagidaki hata mesaji cikacaktir.
if( dizi == NULL )
printf( "Yetersiz bellek!\n" );

Dinamik hafıza kullanarak dizi yaratmayı gördük. Ancak bu diziler tek boyutlu dizilerdi. Daha önce pointer işaret eden pointer'ları görmüştük. Şimdi onları kullanarak dinamik çok boyutlu dizi oluşturacağız:

#include<stdio.h>
#include<stdlib.h>
int main( void )
{
int **matris;
int satir_sayisi, sutun_sayisi;
int i, j;
printf( "Satır sayısı giriniz> " );
scanf( "%d", &satir_sayisi );
printf( "Sütun sayısı giriniz> " );
scanf( "%d", &sutun_sayisi );

// Once satir sayisina gore hafizada yer ayiriyoruz.
// Eger gerekli miktar yoksa, uyari veriliyor.
matris = (int **)malloc( satir_sayisi * sizeof(int) );
if( matris == NULL )
printf( "Yetersiz bellek!" );

// Daha sonra her satirda, sutun sayisi kadar hucrenin
// ayrilmasini sagliyoruz.
for( i = 0; i < satir_sayisi; i++ ) {
matris[i] = malloc( sutun_sayisi * sizeof(int) );
if( matris[i] == NULL )
printf( "Yetersiz bellek!" );
}

// Ornek olmasi acisindan matris degerleri
// gosteriliyor. Dizilerde yaptiginiz butun
// islemleri burada da yapabilirsiniz.
for( i = 0; i < satir_sayisi; i++ ) {
for( j = 0; j < sutun_sayisi; j++ )
printf( "%d ", matris[i][j] );
printf( "\n" );
}

// Bu noktada matris ile isimiz bittiginden
// hafizayi bosaltmamiz gerekiyor. Oncelikle
// satirlari bosaltiyoruz.
for( i = 0; i < satir_sayisi; i++ ) {
free( matris[i] );
}
// Satirlar bosaldiktan sonra, matrisin
// bos oldugunu isaretliyoruz.
free( matris );

return 0;
}

Yukardaki örnek karmaşık gelebilir; tek seferde çözemeyebilirsiniz. Ancak bir iki kez üzerinden geçerseniz, temel yapının aklınıza yatacağını düşünüyorum. Kodun koyu yazılmış yerlerini öğrendiğiniz takdirde, sorun kalmayacaktır.


Örnek Sorular
Soru 1: Kendisine gönderilen iki diziyi birleştirip geriye tek bir dizi döndüren fonksiyonu yazınız.
Cevap için tıklayınız...

#include<stdio.h>
#include<stdlib.h>
/* Kendisine verilen iki diziyi birlestirip
sonuc dizisini geriye dondurur */
int *dizileri_birlestir( int [], int,
int [], int );
int main( void )
{
int i;
// 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
// sonucun dondurulmesi icin pointer tanimliyoruz
int *ptr;

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

// ptr isimli pointer'i bir dizi olarak dusunebiliriz
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 = calloc( boyut_1+boyut_2, sizeof(int) );
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;
}

Soru 2: Sol aşağıda bulunan 4x4 boyutundaki matrisi saat yönünde 90o döndürecek fonksiyonu yazınız. ( Sol matris döndürüldüğü zaman sağ matrise eşit olmalıdır. )

12 34 22 98
88 54 67 11
90 91 92 93
38 39 40 41
=========
38 90 88 12
39 91 54 34
40 92 67 22
41 93 11 98

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

#include<stdio.h>
void elemanlari_goster( int [][4] );
void saat_yonunde_cevir( int [][4] );
int main( void )
{
int matris[4][4] = {
{12, 34, 22, 98},
{88, 54, 67, 11},
{90, 91, 92, 93},
{38, 39, 40, 41} };
elemanlari_goster( matris );
printf("\n");
saat_yonunde_cevir( matris );
}
void elemanlari_goster( int dizi[][4] )
{
int i, j;
for( i = 0; i < 4; i++ ) {
for( j = 0; j < 4; j++ )
printf( "%d ", dizi[i][j] );
printf( "\n" );
}
}
void saat_yonunde_cevir( int dizi[][4] )
{
int i, j;
for( i = 0; i < 4; i++ ) {
for( j = 0; j < 4 ; j++ )
printf( "%d ", dizi[3-j][i] );
printf( "\n" );
}
}

[size=14pt]C Programlama Dersi - XIII[/size]

Bu yazıda öğrenecekleriniz:

- Katarlar ( String )

- Katarlarda printf( ) ve scanf( ) kullanımı

- gets( ) ve puts( ) fonksiyonları

- Katarlara ilk değer atama

- Biçimlendirilmiş ( formatlı ) gösterim

- Standart katar fonksiyonları

- main( ) fonksiyonuna argüman aktarımı

- Konuyla ilgili örnek sorular


[glow=yellow,2,300]Katarlar ( String )[/glow]
Dizileri ve çok boyutlu dizileri gördük. Katar dediğimiz şey de aslında bir dizidir. Değişken tipi char yani karakter olan diziler, 'katar' ya da İngilizce adıyla 'string' olarak isimlendirilirler.

Katarları, şimdiye kadar gördüğümüz dizilerden ayıran, onları farklı kılan özellikleri yoktur. Örneğin bir tam sayı ( int ) dizisinde, tam sayıları saklarken; bir karakter dizisinde -yani katarda- karakterleri ( char ) saklarız. Bunun dışında bir fark bulunmaz. Ancak sık kullanılmalarına paralel olarak, katarlara ayrı bir önem vermek gerekir. Yaptığınız işlemler bilimsel ve hesaplama ağırlıklı değilse, hangi dili kullanırsanız kullanın, en çok içli dışlı olacağınız dizi tipi, karakter dizileridir. İsimler, adresler, kullanıcı adları, telefonlar vs... sözle ifade edilebilecek her şey için karakter dizilerini kullanırız. Katarlar işte bu yüzden önemlidir!

Karakter dizilerine İngilizce'de String dendiğini belirtmiştik. String; ip, bağ, kordon gibi anlamlar taşıyor. İlk olarak katar adını kim münasip gördü bilmiyorum. Muhtemelen bellek hücrelerine peşi sıra dizilen karakterlerin, vagonlara benzetilmesiyle, String değişken tipi Türkçe'ye katar olarak çevrildi. ( Arapça kökenli Türkçe bir kelime olan katar, 'tren' anlamına gelmektedir. ) Daha uygun bir isim verilebilirdi ya da sadece 'karakter dizisi' de diyebilirdik. Fakat madem genel kabul görmüş bir terim var; yazımız içersinde biz de buna uyacağız. String, katar ya da karakter dizisi hiç farketmez; hepsi aynı kapıya çıkıyor: Değişken tipi karakter olan dizi...

[glow=yellow,2,300]Katarlarda printf( ) ve scanf( ) Kullanımı[/glow]
Katarlarla, daha önce gördüğümüz diziler arasında bir farkın olmadığını söylemtiştik. Bu sözümüz, teorik olarak doğru olsa da, pratikte ufak tefek farkları kapsam dışı bırakıyor. Hatırlayacaksınız, dizilerde elemanlara değer atama ya da onlardan değer okuma adım adım yapılan bir işlemdi. Genellikle bir döngü içersinde, her dizi elemanı için scanf( ) veya printf( ) fonksiyonunu çağırmamız gerekiyordu. Katarlar için böyle bir mecburiyet bulunmuyor. Tek bir kelimeyi, tek bir scanf( ) fonksiyonuyla okutabilir ve elemanlara otomatik değer atayabilirsiniz. Yani "Merhaba" şeklinde bir girdi-input gelirse, 3.dizi elemanı 'r' olurken; 6.dizi elemanı 'b' olur. Önceki dizilerde gördüğümüzün aksine, eleman atamaları kendiliğinden gerçekleşir. Aşağıdaki örneği inceleyelim:

[glow=yellow,2,300]#include<stdio.h>

[glow=yellow,2,300]int main( void )
{
char isim[30];
printf( "İsim giriniz> ");
scanf( "%s", isim );
printf( "Girdiğiniz isim: %s\n", isim );
return 0;
}[/glow][/glow]

Örneğimizde 30 karakterlik bir karakter dizisi tanımlayarak işe başladık. Bunun anlamı girdileri saklayacağımız 'isim' katarının 30 karakter boyutunda olacağıdır. Ancak bu katara en fazla 29 karakterlik bir kelime atanabilir. Çünkü katarlarda, kelime bitiminden sonra en az bir hücre boş bırakılmalıdır. Bu hücre 'Boş Karakter' ( NULL Character ) tutmak içindir. Boş karakter "\0" şeklinde ifade edilir. C programlama dilinde, kelimelerin bittiğini boş karakterlerle anlarız. Herhangi bir katarı boş karakterle sonlandırmaya, 'null-terminated' denmektedir.

Bu arada katarlara değer atarken ya da katarlardan değer okurken, sadece katar adını yazmamızın yettiğini farketmişsinizdir. Yani scanf( ) fonksiyonu içersine & işareti koymamız gerekmiyor. Çünkü scanf( ), katarın ilk adresinden başlayarak aşağıya doğru harfleri tek tek ataması gerektiğini biliyor. ( Aslında biliyor demek yerine, fonksiyonun o şekilde yazıldığını söylememiz daha doğru olur. )

Katarların, esasında bir dizi olduğundan bahsetmiştik. Şimdi bunun uygulamasını yapalım. Katara değer atamak için yine aynı kodu kullanırken; katardan değer okumak için kodumuzu biraz değiştirelim:

[glow=yellow,2,300]#include<stdio.h>
int main( void )
{
char isim[30];
int i;
printf( "İsim giriniz> ");
scanf( "%s", isim );

printf( "Girdiğiniz isim: ");
for( i = 0; isim[i]!='\0'; i++ )
printf( "%c", isim[i] );
printf("\n");

return 0;
}[/glow]

Daha önce tek bir printf( ) fonksiyonuyla bütün katarı yazdırabilirken, bu sefer katar elemanlarını tek tek, karakter karakter yazdırmayı tercih ettik. Çıkan sonuç aynı olacaktır fakat gidiş yolu biraz farklılaştı. Özellikle for döngüsü içersinde bulunan " isim[i]!='\0' " koşuluna dikkat etmek gerekiyor. İsteseydik, " i < 30 " yazar ve katarın bütün hücrelerini birer birer yazdırabilirdik. Fakat bu mantıklı değil! 30 karakterlik bir dizi olsa bile, kullanıcı 10 harften oluşan bir isim girebilir. Dolayısıyla kalan 20 karakteri yazdırmaya gerek yoktur. Kelimenin nerede sonlandığını belirlemek için "isim[i]!='\0'" koşulunu kullanıyoruz. Bunun anlamı; isim katarının elemanları, "\0" yani boş karakterere ( NULL Character ) eşit olmadığı sürece yazdırmaya devam edilmesidir. Ne zaman ki kelime biter, sıradaki elemanın değeri "\0" olur; işte o vakit döngüyü sonlandırmamız gerektiğini biliriz.

Yukardaki örneğimize birden çok kelime girdiyseniz, sadece ilk kelimenin alındığını farketmişsinizidir. Yani "Bugün hava çok güzel." şeklinde bir cümle girdiğiniz zaman, katara sadece "Bugün" kelimesi atanır. Eğer aynı anda birden fazla kelime almak istiyorsanız, ayrı ayrı belirtilmesi gerekir.

[glow=yellow,2,300]#include<stdio.h>
int main( void )
{
char isim[25], soyad[30];
printf( "Ad ve soyad giriniz> ");
scanf( "%s%s", isim, soyad );
printf( "Sayın %s %s, hoş geldiniz!\n", isim, soyad );
return 0;
}
gets( ) ve puts( ) Fonksiyonları
Gördüğünüz gibi aynı anda iki farklı kelime alıp, ikisini birden yazdırdık. Fakat scanf( ) fonksiyonu "Bugün hava çok güzel." cümlesini tek bir katara alıp, atamak için hâlen yetersizdir. Çünkü boşluk gördüğü noktada, veriyi almayı keser ve sadece "Bugün" kelimesinin atamasını yapar. Boşluk içeren bu tarz cümleler için puts( ) ve gets( ) fonksiyonları kullanılmaktadır. Aşağıdaki örnek program, 40 harfi geçmeyecek her cümleyi kabul edecektir:

#include<stdio.h>
int main( void )
{
char cumle[40];
printf( "Cümle giriniz> ");
gets( cumle );
printf( "Girdiğiniz cümle:\n" );
puts( cumle );
return 0;
}[/glow]

[glow=red,2,300]gets( ) [/glow] isminden anlayacağınız ( get string ) gibi katara değer atamak için kullanılır. puts( ) ( put string ) ise, bir katarın içeriğini ekrana yazdırmaya yarar. gets( ) atayacağı değerin ayrımını yapabilmek için '\n' aramaktadır. Yani klavyeden Enter'a basılana kadar girilen her şeyi, tek bir katara atayacaktır. puts( ) fonksiyonuysa, printf( ) ile benzer çalışır. Boş karakter ( NULL Character ) yani '\0' ulaşana kadar katarı yazdırır; printf( ) fonksiyonundan farklı olarak sonuna '\n' koyarak bir alt satıra geçer. Oldukça açık ve basit kullanımlara sahip olduklarından, kendiniz de başka örnekler deneyebilirsiniz.
Bu mesajdan alıntı yap
Dram-Like isimli Üye şimdilik offline konumundadır

Dram-Like

Webmaster

Standart
Alt 23-02-2009 #15
[glow=yellow,2,300]Katarlara İlk Değer Atama[/glow]
Bir katar tanımı yaptığınız anda, katarın bütün elemanları otomatik olarak '\0' ile doldurulur. Yani katarın bütün elemanlarına boş karakter (NULL Character) atanır. Dilerseniz, katarı yaratırken içine farklı değerler atayabilirsiniz. Katarlarda ilk değer ataması iki şekilde yapılır.

Birinci yöntemle değer ataması yapacaksanız, istediğiniz kelimeyi bir bütün olarak yazarsınız:

[glow=red,2,300]#include<stdio.h>
int main( void )
{
// Her iki katarada ilk deger
// atamasi yapiliyor. Ancak
// isim katarinda, boyut
// belirtilmezken, soyad katarinda
// boyutu ayrica belirtiyoruz.
char isim[] = "CAGATAY";
char soyad[5] = "CEBI";
printf( "%s %s\n", isim, soyad );

return 0;
}[/glow]
İkinci yöntemdeyse, kelime bütün olarak yazılmaz. Bunun yerine harf harf yazılır ve sonlandırmak için en sonuna boş karakter ( NULL ) eklenir:

[glow=red,2,300]#include<stdio.h>
int main( void )
{
char isim[] = { 'C', 'A', 'G', 'A',
'T', 'A', 'Y', '\0' };
char soyad[5] = { 'C', 'E', 'B', 'I', '\0' };
printf( "%s %s\n", isim, soyad );
return 0;
}[/glow]
Ben ilk değer ataması yapacağım durumlarda, ilk yolu tercih ediyorum. İkinci yöntem, daha uzun ve zahmeti...

Biçimlendirilmiş ( Formatlı ) Gösterim
Daha önce float tipindeki bir sayının, noktadan sonra iki basamağını göstermek türünden şeyler yapmıştık. Örneğin printf( ) fonksiyonu içersinde, sayıyı %.2f şeklinde ifade ederseniz, sayının virgülden sonra sadece iki basamağı gösterilir. Yada %5d yazarak tam sayıları gösterdiğiniz bir durumda, sayı tek bir rakamdan dahi oluşsa, onun için 5 rakamlık gösterim yeri ayrılır. Aynı şekilde biçimlendirilmiş ( formatlı ) gösterim, katarlarda da yapılmaktadır.

Katarları biçimlendirilmiş şekilde göstermeyi, örnek üzerinden anlatmak daha uygun olacaktır:

[glow=yellow,2,300]#include<stdio.h>
int main( void )
{
char cumle[20] = "Denemeler";

// Cumleyi aynen yazar:
printf( "%s\n", cumle );

// 20 karakterlik alan ayirir
// ve en saga dayali sekilde yazar.
printf( "%20s\n", cumle );

// 20 karakterlik alan ayirir
// ve en saga dayali sekilde,
// katarin ilk bes kelimesini
// yazar
printf( "%20.5s\n", cumle );

// 5 karakterlik alan ayirir
// ve en saga dayali sekilde yazar.
// Eger girilen kelime 5 karakterden
// buyukse, kelimenin hepsi yazilir.
printf( "%5s\n", cumle );

// 20 karakterlik alan ayirir
// ve sola dayali sekilde yazar.
// Sola dayali yazilmasi icin
// yuzde isaretinden sonra, -
// (eksi) isareti konulur.
printf( "%-20s\n", cumle );

return 0;
}[/glow]

Örneğimizde bulunan formatlama biçimlerini gözden geçirirsek:

%20s, ekranda 20 karakter alan ayrılacağı anlamına gelir. Katar, en sağa dayanır ve "Denemeler" yazılır.
%.5s olursa 5 karakterlik boşluk ayrılır. Yüzde işaretinden sonra nokta olduğu için katarın sadece ilk beş harfi yazdırılır. Yani sonuç "Denem" olacaktır. %20.5s yazıldığında, 20 karakterlik boşluk ayrılması istenmiş ancak katarın sadece ilk 5 harfi bu boşluklara yazılmıştır.
%5s kullanırsanız, yine 5 karakterlik boşluk ayrılacaktır. Ancak yüzdeden sonra nokta olmadığı için, katarın hepsi yazılır. Belirtilen boyutu aşan durumlarda, eğer noktayla sınır konmamışsa, katar tamamen gösterilir. Dolayısıyla çıktı, "Denemeler" şeklinde olacaktır.
Anlattıklarımızın hepsi, sağa dayalı şekilde çıktı üretir. Eğer sola dayalı bir çıktı isterseniz, yüzde işaretinden sonra '-' (eksi) işareti koymanız gerekir. Örneğin %-20.5s şeklinde bir format belirlerseniz, 20 karakterlik boşluk ayarlandıktan sonra, sola dayalı olarak katarın ilk 5 harfi yazdırılacaktır. İmleç ( cursor ), sağ yönde 20 karakter sonrasına düşecektir.
Standart Katar Fonksiyonları
Katarlarla daha kolay çalışabilmek için, bazı hazır kütüphane fonksiyonlarından bahsedeceğiz. Bu fonkisyonlar, string kütüphanesinde bulunuyor. Bu yüzden, programınızın başına, #include<string.h> eklemeniz gerekiyor.
[glow=red,2,300]
* strlen( )[/glow] fonksiyonuyla katar boyutu bulma
Dizi boyutuyla, katar uzunluğunun farklı şeyler olduğundan bahsetmiştik. Dizi boyutu, 40 karakter olacak şekilde ayarlanmışken, dizi içinde sadece 7 karakterlik "Merhaba" kelimesi tutulabilir. Bu durumda, dizi boyutu 40 olmasına rağmen, katar boyutu yalnızca 7'dir. Katarların boyutunu saptamak için, boş karakter ( NULL Character ) işaretinin yani "\0" simgesinin konumuna bakılır. Her seferinde arama yapmanıza gerek kalmasın diye strlen( ) fonksiyonu geliştirilmiştir. strlen( ) kendisine argüman olarak gönderilen bir katarın boyutunu geri döndürür. Aşağıdaki gibi kullanılmaktadır:

[glow=yellow,2,300]#include<stdio.h>
#include<string.h>
int main( void )
{
printf( "Katar Uzunluğu: %d\n", strlen("Merhaba") );
return 0;
}
* strcpy( ) ve strncpy( ) ile katar kopyalama
Bir katarı, bir başka katara kopyalamak için strcpy( ) fonksiyonunu kullanırız. Katarlar aynı boyutta olmak zorunda değildir. Ancak kopya olacak katar, kendisine gelecek kelimeyi alacak boyuta sahip olmalıdır. Fonksiyon prototipi aşağıdaki gibidir, geriye pointer döner.

char *strcpy( char[ ], char[ ] );
strcpy( ) fonksiyonunu bir örnekle görelim:

#include<stdio.h>
#include<string.h>
int main( void )
{
char kaynak[40]="Merhaba Dünya";
char kopya[30] = "";
strcpy( kopya, kaynak );
printf( "%s\n", kopya );

return 0;
}[/glow]

[glow=yellow,2,300]strncpy( ) [/glow] fonksiyonu, yine kopyalamak içindir. Fakat emsalinden farklı olarak, kaç karakterin kopyalanacağı belirtilir. Protopi aşağıda verilmiştir:

char *strncpy( char[ ], char[ ], int );
Yukardaki örneği strncpy( ) fonksiyonuyla tekrar edelim:

#include<stdio.h>
#include<string.h>
int main( void )
{
char kaynak[40]="Merhaba Dünya";
char kopya[30] = "";
strncpy( kopya, kaynak, 9 );
printf( "%s\n", kopya );

return 0;
}Yukardaki programı çalıştırırsanız, kopya isimli katara sadece 9 karakterin aktarıldığını ve ekrana yazdırılan yazının "Merhaba D" olduğunu görebilirsiniz.

* strcmp( ) ve strncmp( ) ile katar karşılaştırma
strcmp( ) fonksiyonu, kendisine verilen iki katarı birbiriyle karşılaştırır. Katarlar birbirine eşitse, geriye 0 döner. Eğer ilk katar alfabetik olarak ikinciden büyükse, geriye pozitif değer döndürür. Şayet alfabetik sırada ikinci katar birinciden büyükse, geriye negatif değer dönmektedir. Bu dediklerimizi, daha iyi anlaşılması için bir tabloya dönüştürelim:

Dönen Değer Açıklama
< 0 Katar1, Katar2'den küçüktür.
0 Katar1 ve Katar2 birbirine eşittir.
> 0 Katar1, Katar2'den büyüktür.

strncmp( ) için de aynı kurallar geçerlidir. Tek fark, karşılatırılacak karakter sayısını girmemizdir. strcmp( ) fonksiyonunda iki katar, null karakter işareti çıkana kadar karşılaştırılır. Fakat strncmp( ) fonksiyonunda, başlangıçtan itibaren kaç karakterin karşılaştırılacağına siz karar verirsiniz.

Her iki fonksiyonu da kapsayan aşağıdaki örneği inceleyelim:

#include<stdio.h>
#include<string.h>
int main( void )
{
int sonuc;
char ilk_katar[40]="Maymun";
char ikinci_katar[40]="Maytap";
sonuc = strcmp( ilk_katar, ikinci_katar );
printf( "%d\n", sonuc );
sonuc = strncmp( ilk_katar, ikinci_katar, 3 );
printf( "%d\n", sonuc );

return 0;
}
İlk önce çağrılan strcmp( ), null karakterini görene kadar bütün karakterleri karşılaştıracak ve geriye negatif bir değer döndürecektir. Çünkü "Maymum" kelimesi alfabede "Maytap" kelimesinden önce gelir; dolayısıyla küçüktür. Fakat ikinci olarak çağırdığımız strncmp( ) geriye 0 değeri verecektir. Her iki katarın ilk üç harfi aynıdır ve fonksiyonda sadece ilk üç harfin karşılaştırılmasını istediğimizi belirttik. Dolayısıyla karşılaştırmanın sonucunda 0 döndürülmesi normaldir.

* strcat( ) ve strncat( ) ile katar birleştirme
strcat( ) ve strncat( ) fonksiyonları, bir katarı bir başka katarla birleştirmeye yarar. Fonksiyon adlarında bulunan cat, İngilizce bir kelime olan ve birleştirme anlamına gelen 'concatenate'den gelmiştir. strcat( ) kendisine verilen katarları tamamen birleştirirken, strncat( ) belirli bir eleman sayısına kadar birleştirir. strcat ile ilgili basit bir örnek yapalım.

#include<stdio.h>
#include<string.h>
int main( void )
{
char ad[30], soyad[20];
char isim_soyad[50];
printf( "Ad ve soyadınızı giriniz> " );
scanf( "%s%s", ad, soyad );
// isim_soyad <-- ad
strcat( isim_soyad, ad );
// isim_soyad <-- ad + " "
strcat( isim_soyad, " " );
// isim_soyad <-- ad + " " + soyad
strcat( isim_soyad, soyad );
printf( "Tam İsim: %s\n", isim_soyad );
return 0;
}
Dilerseniz, strncat( ) fonksiyonunu da siz deneyebilirsiniz.

* strstr( ) fonksiyonuyla katar içi arama yapma
Bir katar içinde, bir başka katarı aradığınız durumlarda, strstr( ) fonksiyonu yardımınıza yetişir. strstr( ) fonksiyonu, bir katar içinde aradığınız bir katarı bulduğu takdirde bunun bellekteki adresini geriye döndürür. Yani dönen değer çeşidi bir pointer'dır. Eğer herhangi bir eşleşme olmazsa geriye bir sonuç dönmez ve pointer null olarak kalır. Elbette insanlar için hafıza adreslerinin veya pointer değerlerinin pek bir anlamı olmuyor. Bir katar içinde arama yapıyorsanız, aradığınız yapının katarın neresinde olduğunu tespit etmek için aşağıdaki kodu kullanabilirsiniz:

/* strstr( ) fonksiyon ornegi */
#include<stdio.h>
#include<string.h>
int main( void )
{
char adres[] = "Esentepe Caddesi Mecidiyekoy Istanbul";
char *ptr;
// 'adres' katari icinde, 'koy' kelimesini
// ariyoruz. Bu amacla strstr( ) fonksiyonunu
// kullaniyoruz. Fonksiyon buyuk-kucuk harf
// duyarlidir. Eger birden fazla eslesme varsa,
// ilk adres degeri doner. Hic eslesme olmazsa,
// pointer degeri NULL olur.
ptr = strstr( adres, "koy" );
if( ptr != NULL )
printf( "Başlangıç notkası: %d\n", ptr - adres );
else
printf( "Eşleşme bulunamadı.\n" );
return 0;
}

* strchr( ) ve strrchr( ) fonksiyonları
strchr( ) ve strrchr( ) fonksiyonları, tıpkı strstr( ) gibi arama için kullanılır. Ancak strstr( ) fonksiyonu katar içinde bir başka katarı arayabilirken, strchr( ) ve strrchr( ) fonksiyonları katar içinde tek bir karakter aramak için kullanılır. strchr( ), karakterin katar içindeki ilk konumunu gösterirken; strrchr( ) fonksiyonu, ilgili karakterin son kez geçtiği adresi verir.

#include<stdio.h>
#include<string.h>
int main( void )
{
char adres[] = "Esentepe Caddesi Mecidiyekoy Istanbul";
char *ilk_nokta, *son_nokta;
ilk_nokta = strchr( adres, 'e' );
son_nokta = strrchr( adres, 'e' );
if( ilk_nokta != NULL ) {
printf( "Ilk gorundugu konum: %d\n", ilk_nokta - adres );
printf( "Son gorundugu konum: %d\n", son_nokta - adres );
}
else
printf( "Eşleşme bulunamadı.\n" );
return 0;
}
* atoi( ) ve atof( ) ile katar dönüşümü
Verilen katarı, sayıya çevirmek gerekebilir. Eğer elinizdeki metni, bir tam sayıya ( int ) çevirecekseniz, atoi( ) fonksiyonunu kullanmanız gerekir. Şayet dönüşüm sonunda elde etmek istediğiniz değişken tipi, virgüllü sayı ise ( float ), atof( ) fonksiyonu kullanılır. Her iki fonksiyon stdlib.h kütüphanesi içindedir. Bu fonksiyonları kullanırken,
#include<stdlib.h> komutunu program başlangıcına yazmalısınız.

#include<stdio.h>
#include<stdlib.h>
int main( void )
{
char kok_iki[] = "1.414213";
char pi[] = "3.14";
char tam_bir_sayi[] = "156";
char hayatin_anlami[] = "42 is the answer";

printf( "%d\n", atoi( tam_bir_sayi ) );
printf( "%d\n", atoi( hayatin_anlami ) );
printf( "%f\n", atof( kok_iki ) );
printf( "%f\n", atof( pi ) );
return 0;
}Her iki fonksiyonda rakam harici bir şey görene kadar çalışır. Eğer nümerik ifadeler dışında bir karakter çıkarsa, fonksiyon o noktada çalışmayı keser.

main( ) Fonksiyonuna Argüman Aktarımı

İşlediğimiz bütün derslerde main( ) fonksiyonu vardı. main( ) fonksiyonuyla ilgili incelememizi de, fonksiyonlarla ilgili dokuzuncu dersimizde yapmıştık. Ancak main( ) fonksiyonuna hiçbir zaman parametre aktarmadık; aksine parametre almayacağını garantilemek için sürekli olarak main( void ) şeklinde yazmıştık. Artık main( ) fonksiyonuna nasıl parametre verileceğini göreceğiz. Aşağıdaki kod, parametresi olan bir main( ) fonksiyonunu göstermektedir:

#include<stdio.h>
int main( int argc, int *arg[] )
{
int i;
for( i = 0; i < argc; i++ ) {
printf( "%d. argüman: %s\n", i, arg[i] );
}
return 0;
}

Bu kodu yazıp, "yeni_komut.c" adıyla kaydedin. Ardından eğer Linux ve gcc kullanıyrsanız, aşağıdaki komutu kullanarak kodun derlemesini yapın.

$ gcc yeni_komut.c -o yeni_komut
Yukardaki komut, "yeni_komut" adında çalıştırılabilir bir program dosyası oluşturacak. Windows ve Dev-C++ kullanıyorsanız böyle bir komuta gerek yok. Kodu kaydedip, derlediğiniz zaman, çalışma klasörünüzde "yeni_komut.exe" adında bir dosya zaten oluşacaktır.

İkinci aşamada, programa parametre göndererek çalıştıracağız. Bunun için gerek Linux gerekse Windows kullanıcılarının yapacağı şey birbirine çok benziyor. Linux kullanıcıları aşağıdaki gibi bir komut girecekler:

$ ./yeni_komut Merhaba Dünya Hello World
Windows kullanıcılarınınsa, DOS komut istemini açıp, programın kayıtlı olduğu klasöre gelmeleri gerekiyor. Diyelim ki, "yeni_komut.exe" "C:\Belgelerim" içinde kayıtlı... O hâlde aşağıdaki komutu giriyoruz:

C:\Belgelerim> yeni_komut Merhaba Dünya Hello World
Her iki işletim sisteminde elde edeceğiniz sonuç aynı olacaktır:

0. argüman: ./yeni_komut
1. argüman: Merhaba
2. argüman: Dünya
3. argüman: Hello
4. argüman: World
Dışardan gelen argümanla çalışan bir başka main( ) fonksiyonu oluşturalım. Toplama ve çıkartma işlemini alacağı argümanlara göre yapan bir programı aşağıda bulabilirsiniz:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main( int argc, char *arg[] )
{
// Eger eksik arguman soz konusuysa,
// program calismamalidir.
if( argc < 4 ) {
printf( "Hata: Eksik argüman!\n");
return;
}

float sayi_1, sayi_2;
char islem_tipi[2];
sayi_1 = atof( arg[1] );
strcpy( islem_tipi, arg[2] );
sayi_2 = atof( arg[3] );

// Verilen sembolun neye esit oldugu asagidaki
// if-else if merdiveniyle saptaniyor.
if( !strcmp( islem_tipi, "+" ) )
printf( "Toplam: %.2f\n", sayi_1 + sayi_2 );
else if( !strcmp( islem_tipi, "-" ) )
printf( "Fark: %.2f\n", sayi_1 - sayi_2 );
else
printf( "Hatalı işlem!\n" );
return 0;
}
Bu mesajdan alıntı yap
Dram-Like isimli Üye şimdilik offline konumundadır

Dram-Like

Webmaster

Standart
Alt 23-02-2009 #16
Programı çalıştırmak için şu tarz bir komut verdiğimizi düşünelim:

$ ./hesapla 4 + 12
Programı bu şekilde çalıştırdığınız zaman argümanların, parametrelere atanması aşağıdaki gibi olur:

arg[ 0 ] arg[ 1 ] arg[ 2 ] arg[ 3 ]
./hesapla 4 + 12

Bütün fonksiyonlara, program içersinden argüman aktarımı yaparken; main( ) fonksiyonuna program dışından değer gönderebiliyoruz. Unix komutlarının hemen hemen hepsi bu şekildedir. DOS komutlarının birçoğu da böyle yazılmıştır. main( ) fonksiyonun parametre alıp almaması gerektiğine, ihtiyacınıza göre sizin karar vermeniz gerekir.

Örnek Sorular
Soru 1: Kendisine verilen bir katarın boyutunu bulan fonksiyonu yazınız. ( Çözüm için strlen( ) fonksiyonunu kullanmayınız. )
Cevap için tıklayınız...

#include<stdio.h>
#include<string.h>
int katar_boyutu_bul( char [] );
int main( void )
{
char test_katari[50];
strcpy( test_katari, "ABCDEF" );
printf( "Katar boyutu: %d\n", katar_boyutu_bul( test_katari ) );
return 0;
}
int katar_boyutu_bul( char katar[] )
{
int i;
for( i = 0; katar[ i ]!='\0'; i++ );

return i;
}Soru 2: Tersinden de aynı şekilde okunabilen kelime, cümle veya mısraya 'palindrome' denmektedir. Adı palindrome( ) olan ve verilen katarın tersinin kendisine eşit olduğu durumda geriye 1; aksi hâlde 0 döndüren fonksiyonu yazınız.

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

#include<stdio.h>
#include<string.h>
int palindrome( char [] );
int main( void )
{
char test_katari[50];
strcpy( test_katari, "ABBA" );
printf( "%d\n", palindrome( test_katari ) );
return 0;
}
int palindrome( char katar[] )
{
int boyut =0 , i;
// Once katar boyutu bulunuyor
for( boyut = 0; katar[ boyut ]!='\0'; boyut++ );


for( i = 0; i < boyut/2; i++ ) {
if( katar[i] != katar[ boyut - i - 1 ] )
return 0;
}
return 1;
}
Soru 3: Aşağıdaki gibi çalışıp, çıktı üretebilecek "ters_cevir" programını oluşturunuz.
$ ./ters_cevir Merhaba Dunya Nasilsin?
abahreM aynuD ?nislisaN
Cevap için tıklayınız...

#include<stdio.h>
#include<string.h>
void ters_cevir( char [] );
int main( int argc, int arg[] )
{
int i;
for( i = 1; i < argc; i++ ) {
ters_cevir( arg[i] );
}
printf("\n");
return 0;
}
void ters_cevir( char katar[] )
{
int i, boyut;
for( boyut = 0; katar[ boyut ]!='\0'; boyut++ );

for( i = 0; i < boyut; i++ )
printf("%c", katar[ boyut - 1 - i ] );
printf(" ");
}

[size=14pt]C Programlama Dersi - XIV[/size]
Bu yazıda öğrenecekleriniz:

- Yeni Değişken Tipi Oluşturma ( enum )

- typedef Kullanımı

- Yapılar ( Structures )

- Yapı Etiketleri

- Yapı Dizileri

- Yapılar ve Fonksiyonlar

- Dinamik Yapılar

Yeni Değişken Tipi Oluşturma
Kullandığımız birçok değişken tipi oldu. Tam sayıları, karakterleri, virgüllü sayıları, katarları gördük. Ancak kullanabileceğimiz değişken tipleri bunlarla sınırlı değildir. Kendi değişken tiplerimizi, yaratabiliriz. Örneğin boolean diye yeni bir tip yaratarak, bunun alabileceği değerleri true ve false olarak belirleyebiliriz; üçüncü bir ihtimal olmaz. Ya da mevsimler diye bir değişken tipi belirleyip, alabileceği değerleri aylar olarak kısıtlayabiliriz. İşte bu tarz işlemleri yapmak için enum kullanılır. enum kelimesi, enumerator yani 'sayıcı', 'numaralandırmacı'dan gelmektedir.

Hayat, rakamlarla ifade edilebilir. Bunun en bariz uygulamalarını programlama yaparken görürsünüz. Bir karakter olan A harfi, ASCII Tablo'da 65 sayısına denk düşer; büyük B harfiyse 66'dır ve bu böyle devam eder. Bilgisayarınız işaretlerden, sembollerden, karakterlerden anlamaz. Onun için tek gerçeklik sayılardır. İşte enum bu felsefeye hizmet ediyor. Örneğin doğruyu göstermek için 1, yanlış içinse 0'ı seçersek; yeni bir değişken tipi belirlemiş oluruz. Bilgisayar doğrunun ya da yanlışın ne olduğunu bilmez, onun için sadece 0 ve 1 vardır. Ancak insanların yararına, okunurluğu artan programlar ortaya çıkar. İsterseniz, boolean diye tabir ettiğimiz değişken tipini oluşturalım:

#include<stdio.h>
int main( void )
{
// Degisken tipinin nasil olacagini tanimliyoruz
enum boolean {
false = 0,
true = 1
};
// Simdi de 'dogru_mu' adinda bir degisken
// tanimliyoruz
enum boolean dogru_mu;
// Tanimladigimiz 'dogru_mu' degiskenine
// deger atayip, bir alt satirda da
// kontrol yapiyoruz.
dogru_mu = true;
if( dogru_mu == true )
printf( "Doğru\n" );
return 0;
}
Daha önce boolean diye bir veri tipi bulunmuyordu. Şimdiyse, iki farklı değeri olabilen, doğru ve yanlışları göstermekte kullanabileceğimiz yeni bir değişken tipi oluşturduk. Yanlışı göstermek için 0; doğruyu ifade etmek içinse 1 rakamları kullandık. Yanlışın ve doğrunun karşılığını belirtmemiz gerekmiyordu; boolean veri tipini tanımlarken, 0 ve 1 yazmadan sadece false ya da true da yazabilirdik. Programınız derlenirken, karşılık girilmeyen değerlere sırayla değer atanmaktadır. İlla ki sizin bir eşitlik oluşturmanız gerekmez. Mesela üç ana rengi ( Kırmızı, Sarı ve Mavi )alabilecek ana_renkler veri tipini oluşturalım:

#include<stdio.h>
int main( void )
{
// Degisken tipinin nasil olacagini tanimliyoruz
enum ana_renkler {
Kirmizi,
Mavi,
Sari
};

// Degiskeni tanimliyoruz.
enum ana_renkler piksel;

// Degisken degerini Mavi olarak belirliyoruz.
// Dilersek Sari ve Kirmizi da girebiliriz.
piksel = Mavi;

// Degisken degeri karsilastiriliyor.
if( piksel == Kirmizi )
printf( "Kırmızı piksel\n" );
else if( piksel == Mavi )
printf( "Mavi piksel\n" );
else
printf( "Sarı piksel\n" );
return 0;
}

Kirmizi, Mavi ya da Sari'nin nümerik değerini bilmiyoruz; muhtemelen birden başlamış ve sırayla üçe kadar devam etmişlerdir. Değerlerin nümerik karşılığını bilmesek bile, bu onlarla işlem yapmamızı engellemiyor. Bir önceki örnekte olduğu gibi rahatça kullanabiliyoruz.

Oluşturduğumuz yeni veri tiplerinden, değişken tanımlarken her defasında enum koyduğumuzu görmüşsünüzdür. Bunu defalarca yazmak yerine iki alternatif biçim bulunuyor. Birincisi yeni veri tipini oluştururken, değişkeni tanımlamak şeklinde... boolean örneğimize geri dönüp, farklı şekilde nasıl tanımlama yapabileceğimizi görelim:

#include<stdio.h>
int main( void )
{
// Yeni veri tipini olusturuyoruz
// Ayrica yeni veri tipinden,
// bir degisken tanimliyoruz.
enum boolean {
false = 0,
true = 1
} dogru_mu;

dogru_mu = true;
if( dogru_mu == true )
printf( "Doğru\n" );
return 0;
}
Yukarda gördüğünüz yöntem, yeni veri tipini oluşturduğunuz anda, bu veri tipinden bir değişken tanımlamanızı sağlar. Her seferinde enum yazmanızdan kurtaracak diğer yöntemse, typedef kullanmaktan geçer. typedef kullanımı şu şekildedir:

typedef veri_tipi_eski_adi veri_tipi_yeni_adi
Kullanacağınız typedef ile herhangi bir değişken tipini, bir başka isimle adlandırabilirsiniz. Örneğin yazacağınız "typedef int tam_sayi;" komutuyla, değişken tanımlarken int yerine tam_sayi da yazabilirsiniz. Bunun enum için uygulamasına bakalım:

#include<stdio.h>
int main( void )
{
// Yeni veri tipini olusturuyoruz
// Ayrica yeni veri tipinden,
// bir degisken tanimliyoruz.
enum boolean {
false = 0,
true = 1
};
// Alttaki komut sayesinde, boolean
// veri tipini tek adimda yaratabiliyoruz.
typedef enum boolean bool;

bool dogru_mu;

dogru_mu = true;
if( dogru_mu == true )
printf( "Doğru\n" );
return 0;
}
Özel Değişken Tipleri ve Fonksiyonlar
enum konusu genişletilebilir. Örneğin enum tanımlamasını, global olarak yaparsanız, fonksiyon parametresi olarak kullanabilirsiniz. Çok basit bir fonksiyon oluşturalım. Alacağı değişken bilgisine göre, ekrana ona dair bilgi yazdırılsın:

#include<stdio.h>
// Ay listesini olusturuyoruz. Ocak
// ayi 1 olacak sekilde, aylar sirayla
// numerik degerler aliyor.
enum ay_listesi {
ocak = 1, subat, mart, nisan,
mayis, haziran, temmuz, agustos,
eylul, ekim, kasim, aralik
};
// Degisken tanimlamasini kolaylastirmak
// icin typedef kullaniliyoruz. aylar diyerek
// tanimlama yapmak mumkun hale geliyor.
typedef enum ay_listesi aylar;

void ay_ismini_yazdir( aylar );
int main( void )
{
// aylar tipinde bir degisken
// yaratip, 'kasim' degerini atiyoruz.
aylar bu_ay = kasim;
// kasim, numerik olarak 11'i ifade edecektir.
printf( "%d. ay: ", bu_ay );
// fonksiyonumuzu cagiriyoruz.
ay_ismini_yazdir( bu_ay );
return 0;
}
// Kendisine verilen aylar tipindeki degiskene gore
// hangi ayin oldugunu ekrana yazmaktadir.
void ay_ismini_yazdir( aylar ay_adi )
{
switch( ay_adi ) {
case ocak: printf( "Ocak\n" );break;
case subat: printf( "Şubat\n" );break;
case mart: printf( "Mart\n" );break;
case nisan: printf( "Nisan\n" );break;
case mayis: printf( "Mayıs\n" );break;
case haziran: printf( "Haziran\n" );break;
case temmuz: printf( "Temmuz\n" );break;
case agustos: printf( "Ağustos\n" );break;
case eylul: printf( "Eylül\n" );break;
case ekim: printf( "Ekim\n" );break;
case kasim: printf( "Kasım\n" );break;
case aralik: printf( "Aralık\n" );break;
}
}
Gördüğünüz gibi enum ile oluşturacağınız özel veri tiplerini fonksiyonlara aktarmak mümkün. enum aracılığı ile yeni bir değişken tipi yaratmak, birçok konuda işinizi basit hâle getirir. Özellikle gruplandırılması/tasnif edilmesi gereken veriler varsa, enum kullanmak yararlıdır. Örnek olması açısından aşağıda enum ile tanımlanmış bazı veri tiplerini bulabilirsiniz:

enum medeni_durum { bekar, evli, dul };
enum medeni_durum ayse = bekar;
enum egitim_durumu { ilkokul, ortaokul, lise, universite, master };
enum egitim_durumu ogrenci;
enum cinsiyet { erkek, kadin };
enum cinsiyet kisi;
Yapılar ( Structures )
Yapılar ( structures ); tam sayı, karakter vb. veri tiplerini gruplayıp, tek bir çatı altında toplar. Bu gruplandırma içinde aynı ya da farklı veri tipinden dilediğiniz sayıda eleman olabilir. Yapılar, nesne tabanlı programlama ( Object Oriented Programming ) dilleri için önemli bir konudur. Eğer Java, C# gibi modern dillerle çalışmayı düşünüyorsanız, bu konuya daha bir önem vermeniz gerekir.

Vakit kaybetmeden bir örnekle konumuza girelim. Doğum günü bilgisi isteyip, bunu ekrana yazdıran bir program oluşturalım:

#include<stdio.h>
int main( void )
{
struct {
int yil;
int ay;
int gun;
} dogum_gunu;

printf( "Doğum gününüzü " );
printf( "GG-AA-YYYY olarak giriniz> ");
scanf( "%d-%d-%d", &dogum_gunu.gun,
&dogum_gunu.ay,
&dogum_gunu.yil );
printf( "Doğum gününüz: " );
printf( "%d/%d/%d\n", dogum_gunu.gun,
dogum_gunu.ay,
dogum_gunu.yil );
return 0;
}
Bir kullanıcının doğum gününü sorup, gün, ay ve yıl bilgilerini üç farklı int değişken içersinde tutabilirdik. Ancak gruplandırmak her zaman daha iyidir. Hem yaptığınız işlerin takibi kolaylaşır, hem de hata yapma riskinizi azaltır. Bunu daha iyi anlatmak için aynı anda sizin ve iki kardeşinizin doğum günlerini soran bir program yazalım:

#include<stdio.h>
int main( void )
{
struct {
int yil;
int ay;
int gun;
} siz, kiz_kardes, erkek_kardes;

printf( "Doğum gününüzü giriniz> ");
scanf( "%d-%d-%d", &siz.gun,
&siz.ay,
&siz.yil );
printf( "Kız kardeşiniz> " );
scanf( "%d-%d-%d", &kiz_kardes.gun,
&kiz_kardeselamun aleykümy,
&kiz_kardes.yil );
printf( "Erkek kardeşiniz> " );
scanf( "%d-%d-%d", &erkek_kardes.gun,
&erkek_kardeselamun aleykümy,
&erkek_kardes.yil );
return 0;
}
Eğer yapılardan ( structures ) yararlanmasaydık; üç kişinin doğum günü bilgilerini tutmak için toplamda 9 adet farklı değişken tanımlamak gerekecekti. Tanımlama zahmeti bir yana, değişkenlerin karıştırılma ihtimali de ayrı bir sıkıntı yaratacaktı. Sadece üç değişken olarak düşünmeyelim; nüfus cüzdanı bilgilerini soracağımız bir program, yirminin üzerinde değişkene ihtiyaç duyar. Bu kadar çok değişken barındırıp, yapıları kullanmadan hazırlanacak bir programı görmek bile istemezsiniz.

Yapıları kullanmanın bir diğer avantajı, kopyalama konusundadır. Örneğin, sizin bilgilerinizi, erkek kardeşinize kopyalamak için tek yapmanız gereken, "erkek_kardes = siz" yazmaktır. Bu basit işlem ilgili bütün değişkenlerin kopyalamasını yapar.

İç İçe Yapılar
Bir yapı içersine tıpkı bir değişken koyar gibi, bir başka yapı da koyulabilir. Örneğin kullanıcı bilgisi alan bir programda, isim, boy ve doğum tarihi bilgilerini aynı yapı altına toplayabilirsiniz. Ancak doğum tarihi bilgilerini daha alt bir yapı içersinde tutmak yararlı olabilir. Bunu koda dökersek şöyle olur:

#include<stdio.h>
int main( void )
{
struct {
char isim[40];
int boy;
struct {
int yil;
int ay;
int gun;
} dogum_bilgileri;
} kisi;

printf( "Adınız: " );
scanf( "%s", kisi.isim );
printf( "Boyunuz: " );
scanf( "%d", &kisi.boy );
printf( "Doğum tarihi: ");
scanf( "%d-%d-%d", &kisi.dogum_bilgileri.gun,
&kisi.dogum_bilgileri.ay,
&kisi.dogum_bilgileri.yil );

printf( "Girilen bilgiler:\n" );
printf( "İsim: %s\n", kisi.isim );
printf( "Boy: %d\n", kisi.boy );
printf( "Doğum tarihi: %d/%d/%d\n", kisi.dogum_bilgileri.gun,
kisi.dogum_bilgileri.ay,
kisi.dogum_bilgileri.yil );
return 0;
}
Bu mesajdan alıntı yap
Dram-Like isimli Üye şimdilik offline konumundadır

Dram-Like

Webmaster

Standart
Alt 23-02-2009 #17
Alt yapıya ulaşmak için nokta kullanıp, ardından yapının adını yazdığımızı görüyorsunuz. Yapıları kullanarak, bir arada durması yararlı olan değişkenleri gruplarız. İç içe yapıları kullanaraksa, bu gruplandırmayı daha da ufak boyutlara indirgemekteyiz. Kısacası her şey, daha derli toplu çalışma için yapılıyor. Yoksa programın temelinde değişen bir şey yok.

Yapı Etiketleri
Yapılara etiket koyabilir ve etiketleri kullanarak ilgili yapıyı temel alan değişkenler tanımlayabilirsiniz. Az evvel yaptığımıza benzer bir örnek yapalım:

#include<stdio.h>
#include<string.h>
int main( void )
{
// sahis_bilgileri, yapimizin
// etiketidir.
struct sahis_bilgileri {
char isim[40];
int boy;
};

// Yapidan iki adet degisken
// tanimliyoruz.
struct sahis_bilgileri kisi_1;
struct sahis_bilgileri kisi_2;

// Birinci sahsin bilgilerini
// kaydediyoruz.
strcpy( kisi_1.isim, "AHMET" );
kisi_1.boy = 170;

// Ikinci sahsin bilgilerini
// kaydediyoruz.
strcpy( kisi_2.isim, "MEHMET" );
kisi_2.boy = 176;

return 0;
}
Yapıların etiket konarak tanımlanması, daha mantıklıdır. Aksi hâlde sadece yapıyı oluştururken tanımlama yaparsınız. Etiket koyduğunuz zamansa, programın herhangi bir yerinde istediğiniz kadar yapı değişkeni tanımlayabilirsiniz. Önemli bir noktayı belirtmek isterim: yapılarda etiket kullandığınız zaman elinizde sadece bir şablon vardır. O etiketi kullanarak yapıdan bir değişken yaratana kadar, üzerinde işlem yapabileceğiniz bir şey olmaz. Yapı ( structure ) bir kalıptır; bu kalıbın etiketini kullanarak değişken tanımlamanız gerekir.

Yapılarda İlk Değer Atama
Yapılarda da ilk değer ataması yapabilirsiniz. Aşağıdaki örnek etiket kullanmadan oluşturduğunuz yapılarda, ilk değer atamasının nasıl olduğunu göstermektedir. 'kisi' isimli yapı içersinde bulunan isim ve boy değişkenlerine sırasıyla Ali ve 167 değerleri atanmaktadır.

#include<stdio.h>
int main( void )
{
// kisi adinda bir yapi olusturulup
// baslangic degerleri 'Ali' ve '167'
// olacak sekilde atanir.
struct {
char isim[40];
int boy;
} kisi = { "Ali", 167 };

return 0;
}
Etiket kullanarak oluşturduğunuz yapılarda, ilk değer ataması değişkenlerin tanımlanması aşamasında gerçekleşir. Önce yapıyı kurar ve ardından değişken tanımlarken, ilk değerleri atarsınız. Kullanımı aşağıdaki kod içersinde görülmektedir:

#include<stdio.h>
int main( void )
{
// sahis_bilgileri adinda bir yapi
// olusturuyoruz
struct sahis_bilgileri {
char isim[40];
int boy;
};

// sahis_bilgileri yapisindan kisi adinda
// bir degisken tanimliyoruz. Tanimlama
// esnasinda atanacak ilk degerler belirleniyor.
struct sahis_bilgileri kisi = { "Ali", 167 };

return 0;
}
Bir yapı değişkenine, ilk değer ataması yapıyorsanız sıra önemlidir. Atayacağınız değerlerin sırası, ilgili değişkenlere göre olmalıdır. Yani ilk yazacağınız değer, ilk yapı içi değişkene; ikinci yazacağınız değer, ikinci yapı içi değişkene atanır. Sırayı şaşırmadığınız sürece bir problem yaşamazsınız. Aksi durumda, yanlış değer yanlış değişkene atanacaktır. Sırayı şaşırmak için, ekstra ayraç işaretleri kullanabilirsiniz. Örneğin { "Mehmet", 160, 23, 3, 1980 } yerine { "Mehmet", 160, {23, 3, 1980} } yazmakta mümkündür.

Yapı Dizileri
Veri tiplerine ait dizileri nasıl oluşturacağımızı biliyoruz. Bir tam sayı dizisi, bir karakter dizisi rahatlıkla oluşturabiliriz. Benzer şekilde yapı ( structure ) dizileri de tanımlanabilir. 3 kişilik bir personel listesi tutacağımızı düşünüp, ona göre bir program oluşturalım. Her eleman için ayrı ayrı değişken tanımlamaya gerek yoktur; yapılardan oluşan bir dizi yaratabiliriz.

#include<stdio.h>
int main( void )
{
int i;
// Dogum tarihi tutmak icin
// 'dogum_tarihi' adinda bir yapi
// olusturuyoruz
struct dogum_tarihi {
int gun;
int ay;
int yil;
};

// Kisiye ait bilgileri tutmak
// icin 'sahis_bilgileri' adinda
// bir yapi kuruluyor.
struct sahis_bilgileri {
char isim[40];
int boy;
// Yapi icinde bir baska yapiyi
// kullanmak mumkundur. dogum_tarihi
// yapisindan 'tarih' adinda bir
// degisken tanimlaniyor.
struct dogum_tarihi tarih;
};

// Dizi elemanlarina ilk deger atamasi yapiyoruz. Dilerseniz
// klavyeden deger girmeyi tercih edebilirsiniz.
struct sahis_bilgileri kisi[3] = { "Ali", 170, { 17, 2, 1976 },
"Veli", 178, { 14, 4, 1980 },
"Cenk", 176, { 4, 11, 1983 } };

// Yapi dizisi yazdiriliyor:
for( i = 0; i < 3; i++ ) {
printf( "Kayıt no.: %d\n", ( i + 1 ) );
printf( "Ad: %s\n", kisi[i].isim );
printf( "Boy: %d\n", kisi[i].boy );
printf( "Doğum Tarihi: %d/%d/%d\n\n", kisi[i].tarih.gun,
kisi[i].tarih.ay,
kisi[i].tarih.yil );
}

return 0;
}
Tek bir yapı değişkeniyle, bir yapı dizisi arasında büyük fark bulunmuyor. Sadece köşeli parantezlerle eleman indisini belirtmek yetiyor. Yoksa, değer okuma, değer yazma... bunların hepsi tıpatıp aynı. Bu yüzden ayrıca detaya inmiyorum.

Yapı Dizilerine Pointer ile Erişim
Kambersiz düğün olmaz. Aynı şekilde, dizilerden bahsettiğimiz bir yerde pointer'lardan bahsetmemek mümkün değil. Bir yapı dizisinin başlangıç adresini pointer'a atadığınız takdirde, elemanlara bu işaretçi üzerinde de ulaşabilirsiniz. Bir üstteki örneğimizi pointer'larla kullanalım:

#include<stdio.h>
int main( void )
{
int i;
// Dogum tarihi tutmak icin
// 'dogum_tarihi' adinda bir yapi
// olusturuyoruz
struct dogum_tarihi {
int gun;
int ay;
int yil;
};

// Kisiye ait bilgileri tutmak
// icin 'sahis_bilgileri' adinda
// bir yapi kuruluyor.
struct sahis_bilgileri {
char isim[40];
int boy;
// Yapi icinde bir baska yapiyi
// kullanmak mumkundur. dogum_tarihi
// yapisindan 'tarih' adinda bir
// degisken tanimlaniyor.
struct dogum_tarihi tarih;
};

struct sahis_bilgileri *ptr;

// Dizi elemanlarina ilk deger atamasi yapiyoruz. Dilerseniz
// klavyeden deger girmeyi tercih edebilirsiniz.
struct sahis_bilgileri kisi[3] = { "Ali", 170, { 17, 2, 1976 },
"Veli", 178, { 14, 4, 1980 },
"Cenk", 176, { 4, 11, 1983 } };

// Yapi dizisi yazdiriliyor:
for( i = 0, ptr = &kisi[0]; ptr <= &kisi[2]; ptr++, i++ ) {
printf( "Kayıt no.: %d\n", ( i + 1 ) );
printf( "Ad: %s\n", ptr->isim );
printf( "Boy: %d\n", ptr->boy );
printf( "Doğum Tarihi: %d/%d/%d\n\n", ptr->tarih.gun,
ptr->tarih.ay,
ptr->tarih.yil );
}

return 0;
}
Pointer'ın tanımlamasını yaparken, 'sahis_bilgileri' şablonundan türetilen değişkenlerin işaret edileceğini bildirmemiz gerekiyor. Yazmış olduğumuz "struct sahis_bilgileri *ptr;" kodu bundan kaynaklanmaktadır. for döngüsüne gelirsek, kisi isimli yapı dizisinin ilk elemanının adresini, ptr işaretçisine atadığımızı görmüşsünüzdür. Her seferinde de, ptr değeri bir adres bloğu kadar artmaktadır. Döngünün devamı, adresin son dizi elemanından küçük olmasına bağlıdır. Kullandığımız -> operatörüyse, pointer ile dizi elemanlarını göstermemizi sağlar. Bu cümleler size muhtemelen karışık gelecektir -ki bu kesinlike normal... İnanıyorum ki kodu incelerseniz, durumu daha basit kavrarsınız.

Yapılar ve Fonksiyonlar
enum ile yarattığımız değişken tiplerini, fonksiyonlarda kullanmak için global olarak tanımlıyorduk. Yapıları, fonksiyonlarda kullanılmak için izlenecek yöntem aynıdır; yine global tanımlanması gerekir. Çok basit bir örnekle yapıların fonksiyonlarla kullanımını görelim:

#include<stdio.h>
#include<string.h>
struct sahis_bilgileri {
char isim[40];
int boy;
};

struct sahis_bilgileri bilgileri_al( void );
void bilgileri_goster( struct sahis_bilgileri );

int main( void )
{
struct sahis_bilgileri kisi;
kisi = bilgileri_al( );
bilgileri_goster( kisi );

return 0;
}
struct sahis_bilgileri bilgileri_al( void )
{
struct sahis_bilgileri sahis;
printf( "İsim> " );
gets( sahis.isim );
printf( "Boy> " );
scanf( "%d", &sahis.boy );
return sahis;
}
void bilgileri_goster( struct sahis_bilgileri sahis )
{
printf( "Ad: %s\n", sahis.isim );
printf( "Boy: %d\n", sahis.boy );
}

Dinamik Yapılar
Dinamik bellek tahsis etmenin ne olduğunu, niçin bunu kullandığımızı açıklamaya gerek duymuyorum. Daha önceki derslerimizde bu konuya yer vermiştik. Çok basit bir örnekle dinamik yapıların kullanımı göstermek yeterli olacaktır:

struct sahis_bilgileri *ptr;
ptr = calloc( 1, sizeof( struct sahis_bilgileri ) );
free( ptr );

Üç adımda, yapıları dinamik kullanmayı görüyorsunuz. En başta ptr adında bir pointer tanımlıyoruz. İkinci aşamada, bellek ayrımı yapılıyor. Bu örnekte, sadece tek değişkenlik yer ayrılıyor. ptr ile işimiz bittikten sonra, free( ) fonksiyonuyla, belleği boşaltıyoruz. Sadece üç temel adımla, yapılarda dinamik bellek kullanımını sağlayabilirsiniz. Yalnız calloc( ) ( ya da malloc( ) ) fonksiyonunun stdlib.h altında olduğunu unutmayın. ( Bu yüzden kodun başına #include<stdlib.h> yazmak gerekmektedir. )

Yapılarda typedef Kullanımıenum konusuna tekrar dönüyoruz. Hatırlayacağınız üzere, typedef orada da geçmişti. typedef kullanarak her seferinde fazladan enum yazma zahmetinden kurtuluyorduk. typedef, yapılar için de kullanılmaktadır. Her defasında tekrar tekrar struct yazmak yerine, bir kereye mahsus typedef kullanarak bu zahmetten kurtulabilirsiniz. Aşağıdaki gibi yazacağınız kodla, tekrar tekrar struct kelimesi kullanmanıza gerek kalmayacaktır.

typedef struct sahis_bilgileri kisi_bilgileri;
Noktalarken...
C programlama diline dair anlatımlarımız burada bitiyor. Bu demek değildir ki; C üzerine her şeyi anlattık; aksine daha birçok konu bulunuyor. ( Örneğin dosya işlemleri, union kullanımı vb... konulara hiç değinilmedi. ) Ancak şimdiye kadar öğrendikleriniz, bundan sonrasını öğrenebilmeniz için size temel teşkil edecektir. Programlama dili öğrenmek, yabancı dil öğrenmekle hemen hemen aynıdır. İngilizce üzerine dersler aldığınızda, kimse Shakespeare olacağınızı söyleyemez. Ama çok çalışıp kendinizi geliştirmek size bağlıdır. Programlama dilleri de aynen böyle... Burada ya da bir başka kaynakta anlatılanlarla işin duayeni olamazsınız; fakat işi anlar duruma gelirsiniz. Bundan sonrası sizin elinizdedir... Lütfen bol bol pratik yapıp, olabildiğince çok algoritma kurun. Sizlere temel programlama gramerini vermeye çalıştım; umarım sonunda hepiniz birer "Macbeth" yazarsınız!

[glow=red,2,300][shadow=red,left]son[/shadow][/glow]

var GGAff = {id:15145,width:728,height:90,direction:"Vertical" ,performance:"high"};GGAff.errorMessage = '';
Bu mesajdan alıntı yap
emre_hrk isimli Üye şimdilik offline konumundadır

emre_hrk

Öğrenci (Grafik) / Ankara

Standart
Alt 08-08-2009 #18
kardeş bu güzel anlatımın için teşekkürler .fakat ben ta ilk örnekte takıldım!
-printf- ile başlayan satırda hata veriyor
kopyala yapıştır yapınca oluyo fakat kendim yazınca hata veriyor
üstelik yanlış yapmadığıma da eminim
neden olabilir acaba?
Bu mesajdan alıntı yap
d3niz isimli Üye şimdilik offline konumundadır

d3niz

UI Pilot

Grafiker / İstanbul

Standart
Alt 08-08-2009 #19
Yanlış tırnak koyuyor olabilirsiniz.
Bu mesajdan alıntı yap
Cevapla

Tags
c programlama dersi

Benzer Konular
Konu Konu Bilgileri Forum Cevaplar Son Mesaj
İstanbul' da web tasarımı web programlama ile ilgili iş aramaktayım asit Sonuçlanan İlanlar 11 09-05-2011 13:31:07
C Programlama Dilinin Tarihi Dram-Like Visual basic , Delphi , C , C++ 0 22-02-2009 23:10:37
Web programlama, web tasarım, gazete ve dergi tasarımları işleri arıyorum incidesign Sonuçlanan İlanlar 1 12-12-2008 18:15:52
Freelance web tasarım ve programlama yapıyoruz include Sonuçlanan İlanlar 1 28-11-2008 12:25:32
PIc Basic Pro ile Programlama graphic00 Visual basic , Delphi , C , C++ 4 23-02-2008 21:59:10

Kapat
Şifremi Unuttum?