Amiga C

6. Bölüm

Erdem Taylan

Nerede kalmıştık? Aaaa, evet! Fonksiyonları inceliyorduk.

Son olarak, bir fonksiyonun kendisini çağıran fonksiyona bilgiyi geri gönderme metodlarını incelemiştik. Bu sayıda ise, fonksiyonların argümanlarıyla ilgili biraz teknik ve açıklayıcı bilgiden sonra karakter dizilerini inceleyeceğimizi belirteyim.

ARGÜMANLAR VE DEĞER OLARAK ÇAĞRILMALARI

C dilindeki fonksiyonların en önemli parçası olarak gösterebileceğimiz argümanların, diğer dillere yabancı bir özellikleri var. Bu özellik diğer birçok dilin aksine, C'de argümanların değer olarak fonksiyona çağrılmaları. Değer olarak çağrılmayla anlatılmak istenen olgu şu: Bir fonksiyona bir argüman geçirildiğinde, o argümanın geçici bir kopyası alınır ve işlemler bu kopya üzerinde yapılır. Böylece argümanın kendisi değil, değeri işlemlere girer. Diğer birçok dilde ise argümanların adresleri fonksiyonlara geçirildiğinden, onlar üzerinde yapılan her işlem argümanın kendi değerini de etkiler. Diğer dillerin uyguladığı yönteme, adresle çağırılma denmekte.

Bu iki metod arasındaki en önemli fark, C dilinde hiçbir fonksiyonun argümanları üzerinde bir değişiklik yapamaması. Fonksiyonlar sadece o argümanın kopyası üzerinde değişiklik yapabildiklerinden, bir argüman fonksiyona hangi değerle yollandıysa (Yollayan kim mi? Tabii ki fonksiyonu çağıran diğer fonksiyon -hatırlarsanız C dilinde bütün programlar fonksiyonlardan oluşmaktaydı ve main bile aslında özel adlı bir fonksiyondu-), o değerle geri döner.

Değerle çağırma ilk bakışta anlamsız gibi görünse de genellikle programların daha kısa, daha az değişken kullanılarak yapılabilmesine olanak tanır. Çünkü fonksiyonlardaki argümanlar, değerlerinde bir değişme olacağı korkusu olmadan aynı isim altında kullanılabilirler. Örneğin aşağıdaki satırlarda power fonksiyonunun değer ile çağrılma yöntemini kullanan bir versiyonu bulabilirsiniz.

power(x,n) 
int x,n;

{

int p;

for(p=l;n>0;--n)
	p=p*x;
return(p);

}
Burada n adlı argümanın bir kopyası işlemlerde kullanılmıştır. İşlem sırasında, n sıfır olana kadar devamlı bir eksiltilmiştir. Dolayısıyla fonksiyonun içinde sayaç görevini üstlenmek için yeni bir değişken tanımlamaya gerek kalmamıştır. Çünkü fonksiyonun değişkeninin asıl değerini etkileyenleyeceğinden, n kullanılması değerin yitirilmesi gibi bir sonuca yol açmayacaktır.

Doğal olarak, gerektiğinde argümanlarının değerini değiştirebilecek fonksiyonlar yazmakla olasıdır. Değişkeni çağırılan fonksiyona geçiren program parçası eğer değişken yerine, değişkenin adresini verirse (teknik olarak bir değişkeninin adresine verilen isim pointer'dır (İng. gösterici)), program bu adres üzerinden giderek o değişken üzerinde değişiklik yapabilir. Doğal olarak eğer değişken yerine adresi yollanıyorsa fonksiyonda tanımlamalar sırasında o argüman bir değişkene pointer olarak tanımlanmalıdır. Herhangi bir fonksiyon, bir değişkenin adresini bilirse bu adres yardımıyla onun değeriyle dolaylı yoldan oynayabilir.

Eğer fonksiyon argümanlarından biri bir dizinin adıysa, fonksiyona getirilen değer aslında o dizinin adresidir. Dolayısıyla fonksiyonlara geçirilen bir dizi için değer olarak çağrılma değil, adres ile çağrılma durumu geçerlidir. O zaman eğer fonksiyon dizinin elemanları üzerinde bir değişiklik yaparsa, değişiklikler dizinin elemanlarını etkileyerek değerlerini değiştirebilirler.

KARAKTER DİZİLERİ

C dilinde en çok kullanılan dizi türü, belki de karakter dizileridir. Bu tür dizileri ve fonksiyonların onlar üzerindeki etkilerini görmek için, okuduğu satırlar içinden en uzun olanını seçen bir program hazırlayalım. Programın temel hatları oldukça kolay tasarlanabilir:

while(bir satır daha varken)
 if(o anki satır o ana kadarki en uzun satırdan daha uzunsa)
o satırı ve uzunluğunu sakla en uzun satın ekrana yaz

Bu temel adımlardan da anlaşılabileceği gibi program parçalara kolaylıkla ayrılabilir. Bir parça yeni satırı okumakla, diğer bir parça onu test etmekle, bir sonraki onu bir yerde saklamakla ve kalan parçada işleyişi kontrol etmekle görevlendirilebilir.

Parçalar oldukça güzel bir biçimde birbirinden ayrılabildiğinden, programı parçalara ayırarak yazmak da oldukça iyi olacaktır. Bunun için önce girişteki bir sonraki satırı okuyan getline adlı bir fonksiyon -ki bu fonksiyon getchar fonksiyonunun genelleştirilmiş halidir- yazalım. Fonksiyonu başka programlarda da kullanabilmek için yazım sırasında fonksiyonun birkaç amaca uygulanabilir şekilde yazılmasına dikkat etmek gerekir. Bu fonksiyon en azından dosya bitimini sezebilmeli, daha genel olarak da okuduğu satırın uzunluğunu çağrıldığı yere geri gönderebilmelidir. Eğer yeni bir satır okuma sırasında dosya birimi mesajı (EOF) alınırsa, program satır uzunluğu olarak sıfır'ı çağırıcıya göndereceklir. Sıfır hiçbir zaman olası bir satır uzunluğu olamaz, çünkü bir satır en azından yeni satır karakterini ("\n") içermek zorundadır. Sadece "\n" karakterinden oluşan bir satırın uzunluğu bile bir olacaktır. Buradan da anlaşıldığı gibi karakterler arasında görülemeyen olanlarda sayılmaktadır ("\n"-yeni satır. "\t"-tab karakterleri gibi).

O ana kadarki en uzun satırdan daha uzun bir satır bulduğumuzda, o satın mutlaka bir yerlere kaydetmeliyiz (burada kaydetmeyle diskete kaydetmek değil, bellekte bir bölgeye yerleştirme işlemini anlatmak istiyorum.). Bu gereklilik, copy isimli, görevi o anki en uzun satırı güvenilir bir yerde saklamak olan bir fonksiyon yardımıyla çözülebilir.

Son olarak tüm bu parçaların toplanacağı ve programın çalışmasının kontrol edileceği ana bir programa gerek duyulmaktadır.

Bu gerekliliklere göre yazılan programı birinci kutuda bulabilirsiniz.

Programdaki main ve getline fonksiyonları birbirleriyle bir çift argüman ve return komutuyla yollanan bir değer yardımıyla haberleşmektedir, getline fonksiyonunda, argümanlar

char s[]; 
int lim;
deklarasyonları ile tanımlanmaktadır. Burada birinci argüman bir dizi, ikinci argüman ise bir integer (Tk. tamsayı) olarak tanımlanmıştır, s dizisinin uzunluğu deklarasyonda yeralmamıştır. Çünkü bu dizinin uzunluğu main fonksiyonunda belirlenmektedir, getline return komutunu main'e bir değer yollamak için kullanmaktadır. Görüldüğü gibi bazı fonksiyonlar bazı yararlı değerleri çağırıcılarına yollarken, copy fonksiyonu örneğinde olduğu gibi sadece yaptığı iş için kullanılan ve kullanışlı hiçbir değeri çağırıcısına göndermeyen fonksiyonlar da vardır.

getline fonksiyonu 0 karakterini (null karakteri ki bu karakterin değeri sıfırdır), yarattığı dizinin sonuna; dizinin bittiğini belirtmek için koymaktadır. Bu metodu C compiler'ının kendisi de kullanmaktadır. Öyle ki, eğer compiler programın içinde,

"Amiga"
şeklinde bir string (Tk. karakter dizisi) görürse, otomatik olarak string'in elemanlarından oluşan bir karakter dizisi tanımlar ve sonuna \0 karakterini ekler. Yani compiler'ın bellekte oluşturduğu dizi aşağıdaki gibidir.

A m i g a \n \0

Compiler'ın böyle bir uygulama yapmasının nedeni, printf gibi fonksiyonların ekrana (genellikle ekrana, printer gibi diğer çevresel birimlere de olabilir) yazacakları string'lerin sonunu bulabilmesini sağlamaktır, printf tanımlamalarından biri olan %s, basacağı karakler dizisinin sonunda 0 karakterinin yani sıfır değerinin bulunmasını ister. Başka bir örnek olarak; eğer copy fonksiyonunu incelerseniz, s1 dizisine s2'den kopyalanan elemanın \0 yani null karakter olmasıyla fonksiyonun kopyalama işlemini durdurduğunu görürsünüz. Bütün bu işlemlerde string'lerin sonunda 0 karakteri bulunduğu ilkesinin kullanıldığı anlaşılabilir.

Bu ayki yazımı bitirmeden, bir kaç alıştırma için ikinci kutuya bir gözatmanızı öneriyorum.

Bir sonraki ay, dışsal (İng. external) değişkenleri inceleyerek yazı dizimizin ilk bölümü olan C diline giriş'i bitireceğimizi belirteyim. İyi çalışmalar.


KUTU l 1> Aşağıdaki programı yazıp Ram Disk'e kaydedin. /* K&R'ın kitabından alınarak Amiga'ya uyarlanmıştır. */ #define EOF-1 #define MAXLINE 1000 main() { int len; /*O anki satır uzunluğu*/ int max; /*O ana kadarki en büyük uzunluk*/ char line[MAXLINE]; /*O anki satır*/ char save[MAXLINE]; /*O ana kadar en uzun satır*/ max=0; while((len=getline(line,MAXLINE))>0) if (len>max) { max=len; copy(line,save); } if(max>0) printf("%s",save); } getline(s,lim) char s[]; int lim; { int c; int i; for(i=0;i<lim-1&&(c=getchar())!=EOF&&c!='\n';++i) s[i]=c; if(c=="){ s[i]=c; ++i; } s[i]='\0'; return(i); } copy(s1,s2) char s1[]; char s2[]; { int i; i=0; while((s2[i]=s1[i])!='\0') ++i; } 2> Şimdi programı "cc ram:program-ismi.c" ile compile edin. 3> Şimdi de "ln ram:program-ismi.o -lc" ile link edin. 4> Programı çalıştırmak için ram:program-ismi yazın. Bu durumda program sizin klavyeden girdiğiniz karakterler üzerinde işlem yapacaktır. Bu moddan çıkmak için Ctrl-\ (Control ve ters bolü tuşlarına birlikte) tuşlarına basmalısınız. Eğer programa bir text dosyasından redirection (Tk. yeniden yönlendirme) verirseniz program text dosyasının bitimiyle beraber size yaptığı işlemlerin sonuçlarını yazacaktır.


KUTU 2 1> Girişindeki satırların 80 karakterden fazla olanlarını ekrana basan bir program yazın. 2> Kendisine verilen string'i ters çeviren ters(s) adlı bir fonksiyon yazın ve çalışırlığını kontrol için kısa bir programda fonksiyonu deneyin.