4. Bölüm
Erdem Taylan
Şimdi geçen ay kaldığımız yerden devam edelim ve geçen ay beşinci kutuda verdiğim programı inceleyelim:
Programda while döngüsünün içinde yeralan ! = işareti C dilinde "eşit değildir" anlamına gelir.
Programdaki asıl sorun, girişin bittiğinin belirtilebilmesidir. Genel olarak bu sorun, getchar'ın programa ilettiği değerin, anlamlı bir karakter olmamasını sağlayarak yapılır. Böylece programımız girişin sona erdiğini anlayabilir. Fakat burada bir sorun ortaya çıkmakta: Acaba standart olarak hangi sayı kullanılmalı? Bu soru, şu anda iki değişik biçimde yanıtlanıyor; bazıları değer olarak "0", bazıları ise "-1" kullanmakta. AmigaDos'un kullandığı sayı bunlardan "-1". Programın başındaki:
define EOF -1satırı, bu sayının tanımlanmasını sağlamakta. EOF terimi (EndOfFile = Tk. Dosyanın Sonu), bu değeri belirtmek için kullanılan standart bir terim. Buradan da anlaşılabileceği gibi, C'de programın içinde sembolik sabitler tanımlamak programın başka makinelere uyarlanabilirliğini artırmakta. Çünkü eğer başka bir makinada EOF'un değeri farklı bir sayıysa bu durumda değiştirmemiz gereken, sadece programın başındaki tanımlama satırıdır. Fakat sembolik sabit kullanmasaydık, programdaki değiştirilecek değerin geçtiği her satırı değiştirmek zorunda kalacaktır.
Dikkat edilecek başka bir konu ise, getchar fonksiyonunun çıktısının bir integer (Tk. Tamsayı) olan C değişkenine eşlenmiş olması. Demek ki, getchar fonksiyonu çıktı olarak bir karakter değil, o karaktere karşılık gelen bir tamsayı vermekte.
Daha önce de belirttiğim gibi, bir program birkaç değişik yoldan yazılabilir. Hele C gibi syntax'ı (Tk. Yazım kuralları) çok katı olmayan ve bu açıdan oldukça esnek olan bir dil elinizin altındaysa. Örneğin text dosyaları kopyalama programımız oldukça kısaltılabilir. Çünkü herhangi bir eşleme, C'de program içindeki bir işlem şırasında kullanılabilir. Örneğin
c = getchar( )eşlemesini bir while işlemi sırasında kullanırsak programımız birinci kutudaki gibi olur.
Programın çalışması şöyledir:
Önce girişten bir karakter alınır ve c değişkenine eşlenir. Daha sonra c değişkeninin EOF'a eşit olup olmadığı kontrol edilir. Eğer değilse, while döngüsü çalıştırılır. Sonra while tekrar edilir, yani yeni bir karakter alınır, EOF'a eşit olup olmadığı kontrol edilir ve değilse ekrana basılır. Bu işlem c değişkeninin EOF'a eşit olmasıyla son bulur ve program durur.
Görüldüğü gibi C'de syntax fazla disipline edilmemiştir. Dolayısıyla C, bu açıdan oldukça esnek bir dildir. Fakat bu esneklik kötü tarafa kullanılıp anlaşılması zor programlar yazılabilir. Tabii ki bu istenmeyen bir durum.
Programdaki bir başka önemli nokta, koşullu test içindeki parantezlerin gerekliliği. != işaretinin önceliği, = işaretinin önceliğinden daha yüksek olduğundan, parantezleri koymamak yanlış sonuçların ortaya çıkmasını sağlardı. Çünkü, eğer parantezler konulmazsa işlem,
c = getchar( )!=EOFyani,
c = (getchar( )!=EOF)halini alacak ve bu da c değişkenine getchar fonksiyonunun EOF'a eşit olup olmamasına bağlı olarak, 1 ve 0 değerlerini verecekti ki, bu asıl yapmak istediğimiz işlem değil.
KARAKTER SAYMA
Şimdiki programımız kopyalama programının biraz değiştirilmiş hali ve karakterleri sayıyor. Programı ikinci kutuda bulabilirsiniz.
Programın içindeki:
++ nc;satırı bize, bir artır anlamına gelen + + operatörünü tanıtmakta. Burada diğer dillerde olduğu gibi nc = nc + 1'de yazabilirdiniz. Fakat + + nc hem daha kolay anlaşılır hem de genellikle dilin çalışma hızı açısından daha iyidir. C dilinde + + 'ya karşılık olarak, bir eksilt anlamına gelen -- operatörü de vardır. Bu iki operatör hem değiştirilecek sayıdan önce (ing. prefix) (+ + nc gibi), hem de değiştirilecek sayıdan sonra (ing. postfix) (nc + + gibi) yazılabilirler. Bu iki tür yazım değişik sonuçlar verir. Fakat bunları yazı dizimin daha sonraki bölümlerinde inceleyeceğiz.
Karakter sayma programı sayaç olarak long bir sayı kullanmaktadır. Acaba neden integer (Tk. Tamsayı) kullanmadık? Bu sorunun yanıtı şöyle: Amiga'da normal int (int, tam sayının C'deki kısaltmasıdır) sayılar 16 bit'ten oluşur. Bu bitlerin 15'i sayıyı depolamak için, biri ise sayının işaretini belirlemek için kullanılır. Dolayısıyla bir int'in alabileceği en yüksek değer ikinin onbeşinci üssünün bir eksiği, yani 32767'dir. Burada bir eksiğini almamızın sebebi sayılara sıfır'ın da dahil olmasıdır. Eğer programa verdiğimiz veri'de 32767'den fazla karakter varsa, programımız hatalı çalışacak ve yanlış cevaplar üretecektir. Bu durumu engellemek için Amiga'nın 32 bit'ten oluşan long int'lerinden (Tk. uzun tamsayı) yararlanabiliriz. Bu şekilde tanımladığımız bir sayaç ikinin 31'inci üssünün bir eksiğine kadar hatasız çalışabilir. Böylece programımızın karakter sınırı çok çok fazla artırılmış olur.
Doğal olarak programda while döngüsü yerine for döngüsü kullanılabilirdi, for döngüsünün kullanımını önce kendiniz yazın ve daha sonra üçüncü kutudakiyle karşılaştırın.
Görüldüğü gibi for döngüsünün içi boş. Çünkü bütün işlemler for döngüsünün test ve yeniden eşleme bölümlerinde yapılmakta (bu bölümler sırasıyla getchar( )!=EOF ve + + nc bölümleridir). Döngünün içi boş olmasına karşın C'nin gramer özelliklerinden dolayı bu boşluğu belirtmek için sadece boş işlem anlamına gelen ;'ü kullandık. Bu noktalı virgül istenilen yere konabilir. Örneğin hemen for döngüsünden sonra aynı satıra yazılabilir. Fakat ben burada en açık şekilde görülenini verdim.
Son olarak dikkat edileceği gibi, eğer programa giriş olarak hiçbir şey vermezsek program hemen çalışmasını kesecektir. Çünkü while ve for döngüleri önce test koşulunu kontrol ederler. Eğer bu daha ilk kontrolde yanlışsa döngünün içine hiç girilmeden döngüden çıkılır. Böylece bilgisayarın hiç zamanı çalınmayacaktır.
SATIR SAYMA PROGRAMI
Bu program girişindeki satırları sayar. Programda giriş satırlarının sonundan, yani yeni satır karakterinin olduğu varsayılmıştır. Şimdi dördüncü kutudaki programı yazın ve compile edin.
Görüldüğü gibi while döngüsünün içi, bir if komutunu içermektedir. Buradaki if komutunun görevi, nl değişkeninin artırılmasının kontrolüdür, if komutu parantezin içindeki koşulu test eder ve bu doğruysa hemen sonraki komut satırını (veya süslü parantezler içindeki birkaç satırı) işletir. Eğer koşul yanlışsa gövdesini (bir komutun kendine bağlı ve kendinden sonra gelebilecek komutlarına verilen ad, gövdedir. Bunlar bazen tek bir komut olabileceği gibi, bazen de süslü parantezler arasında sınırlanmış bir komut dizini olabilir.) işletmeden bir sonraki (gövdesinden sonraki) komuta geçer.
Programdaki = = operatörü, C dilinde eşitlik testi anlamına gelen operatördür. Bu sembol programlardaki eşitlik testlerini eşleme amacıyla kullanılan = işaretinden ayırmak için kullanılmaktadır. Eşleme, bir değişkenin bir başka değişkene veya sabit değere eşlenmesine verilen addır, örneğin c = 5 veya c = nl gibi. = = operatörü ise sadece eşitlik testlerinde kullanılır, örneğin c = = 5 gibi bir ifade, ancak başka komutların test bölümlerinde kullanılabilir (bu komutlar arasında while, for, if sayılabilir). c= =5'in kullanıldığı bir yerde eğer c, 5'e eşitse c= =5'in sonucu doğru anlamına gelen 1; eğer c, 5'e eşit değilse c= = 5'in sonucu yanlış anlamına gelen 0 olacaktır. C dilinin yaratıcıları, programlarda eşleme işleminin eşitlik testlerinden genellikle iki kat daha fazla kullanılmasından dolayı eşleme operatörünü (=) eşitlik testi operatörünün (= =) yarısı uzunluğunda yapmışlardır!
C dilinde herhangi bir karakter, kesme işaretinin (') arasında yazılırsa, bu, o karakterin bilgisayarın karakter setindeki numarasını içeren değerin alınacağı anlamına gelir. Örneğin Amiga'da kullanılan ASCII karakter setinde A harfinin karşılığı 65'tir. Eğer biz programımızda 'A' yazarsak bu değeri 65 olan bir sayı üretir. Doğal olarak bu gibi yazımlar, ancak başka operatörlerle beraber kullanılır. Örneğin c = 'A' demek, c değişkenini 65'e eşlemek demektir.
Daha önce de gördüğümüz gibi, n, tek bir karakter olan yeni satır karakterine karşılık gelen bir semboldür. Dolayısıyla, programda incelenen her yeni karakterin ' n' değerine eşit olup olmadığı kontrol edilmekte, eğer eşitse, n1 değişkeni bir artırılmakta (+ + n1), eşit değilse bir sonraki karaktere geçilmektedir.
KELİME SAYMA PROGRAMI
Bu ayki son programımız, girişindeki kelimeleri, satırları ve karakterleri sayan bir program. Önce beşinci kutudaki programı yazın ve compile edin.
Programın çalışma mantığı şöyle: Program bir kelimenin ilk karakteriyle karşılaştığında, onu sayar. Keimeiçi adlı değişken o anda programın bir kelimenin içinde olup olmadığını belirler. Başlangıçta bu kelime, dışında anlamına gelen NO'ya eşlenmiştir. Burada sıfır ve bir yerine NO ve YES kullanmamızın sebebi, programın okunabilirliğini artırmak ve ileride yapılacak değişikliklerin daha kısa yapılabilmesine olanak tanımaktır. Doğal olarak bu kadar kısa bir programda bunun önemi pek açık değildir. Fakat program genişledikçe önem gitgide artacaktır.
nl = nw = nc = 0 satırı, her üç değişkeni de sıfıra eşlemektedir. Bu özel bir durum değildir. Çünkü C dilinde eşlemeler sağdan sola doğru yapılır. Yani yukarıdaki satır aslında
nc = (n1 = nw = 0));satırı ile aynıdır.
II operatörü veya (İng. or) anlamına gelir. "I" İşareti klavyenin sağ üst köşesindeki backspace tuşunu hemen yanında yeralan tuşuna shift tuşu ile basılarak yazılır. Tabii ki II işaretini yapmak için bu kombinasyona iki kere basmanız gerekli). Bundan dolayı
if(c= =' '11c= =' n' 11c= =' t')satırı; "eğer c boşluk veya yeni satır veya tab ise..." anlamına gelir. C dilinde II işaretine karşılık gelen bir ve (İng. and) operatörü de, &&'dir. && ve II operatörleriyle birbirine bağlanan koşullar soldan sağa doğru hesaplanırlar ve C dili bize hesaplamanın yanlış veya doğru olduğu bilindiği anda işlemin kesileceğini garanti eder. Örneğin c değişkeni, boşluk karakterine (' ') eşitse, diğer koşulların test edilmesine gerek kalmadığından, bunlar test edilmez. Bu özellik daha karmaşık durumlarda çok avantajlı ve programın çalışma hızını artırıcı bir unsurdur.
Program C dilinin başka bir komutu olan else'i de göstermektedir, else komutu, if komutunun test koşulunun yanlış olması durumunda yapılacak alternatif işlemleri bilgisayara vermeye yarar. Genel olarak bir if, else komut dizini şöy ledir:
if(koşul) komut 1 else komut2
C dili bu iki komuttan yalnızca birinin yapılacağını bize garanti eder. Çünkü koşul doğruysa komut1, koşul yanlışsa komut2 çalıştırılacaktır. Bu komutlar tek bir komuttan oluşabileceği gibi, süslü parantezler arasındaki birkaç komuttan da oluşabilir.
Şimdi size altıncı kutuda birkaç alıştırma vereyim.
Bu aylık C yazım bu kadar. Gelecek ay dizileri ve fonksiyonları inceleyeceğimizi belirterek hoşçakalın diyorum.
KUTU l 1> Aşağıdaki programı yazıp Ramdisk'e kaydedin. /* K&R'ın kitabından alınarak Amiga'ya uyarlanmıştır*/ #define EOF -1 main() /* girişi çıkışa kopyala ikinci versiyon */ { intc; while((c=getchar()) != EOF) putchar(c); } 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ırırsanız geçen sayıdaki son programla aynı işi yaptığını görürsünüz.
KUTU 2 1> Aşağıdaki programı yazıp RamDisk'e kaydedin. /* K&R'ın kitabından alınarak Amiga'ya uyarlanmıştır*/ #define EOF-1 main() /* karakter sayma birinci versiyon */ { long nc; nc=0; while(getchar() != EOF) ++nc; printf("%ld",nc); } 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 karakterleri sayacaktır. Bu moddan çıkmak için Ctrl-\ tuşlarına basın. Eğer programa bir text dosyasından giriş redirection'ı (Tk. yeniden yönlendirme. Programa giriş ve çıkış için yönler verme işlemi) verirseniz, program otomatik olarak dosyanın bitimine kadar olan karakterleri sayar ve dosya bitince sonucu size yazarak durur.
KUTU 3 1> Aşağıdaki programı yazıp RamDisk'e kaydedin. /* K&R'ın kitabından alınarak Amiga'ya uyarlanmıştır*/ #define EOF -1 main() /* karakter sayma ikinci versiyon */ { long nc; for(nc=0;getchar() != EOF; ++nc) printf("%ld",nc); } 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 karakterleri sayacaktır. Bu moddan çıkmak için Ctrl-\ tuşlarına basın. Eğer programa bir text dosyasından giriş redirection'ı verirseniz, program otomatik olarak dosyanın bitimine kadar olan karakterleri sayar ve dosya bitince sonucu size yazarak durur.
KUTU 4 1> Aşağıdaki programı yazıp RamDisk'e kaydedin. /* K&R'ın kitabından alınarak Amiga'ya uyarlanmıştır*/ #define EOF-1 main() /* satır sayma*/ { int c,nl; nl=0; while((c=getchar())!=EOF) if(c==") ++nl; printf("%d",nl); } 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 karakterleri inceleyecektir. Sizin return tuşuna her basışınız ise satır sayısının bir artmasına neden olacaktır. Bu moddan çıkmak için Ctrl-\ tuşlarına basın. Eğer programa bir text dosyasından giriş redirection'ı verirseniz, program otomatik olarak dosyanın bitimine kadar olan tüm karakterleri inceleyecek ve satır sayısını size yazarak duracaktır.
KUTU 5 1> Aşağıdaki programı yazıp RamDisk'e kaydedin. /* K&R'ın kitabından alınarak Amiga'ya uyarlanmıştır*/ #define EOF -1 #define YES 1 #define NO 0 main() /* kelime, satır, karakter sayma */ { int c,nl,nw,nc,kelimeiçi; kelimeiçi=NO; nl=nw=nc=0; while ((c=getchar())!=EOF) { ++nc; if(c==") ++nl; if(c==' '||C==''||c=='\t') kelimeiçi=NO; else if(kelimeiçi==NO) { kelimeiçi=YES; ++nw; } } printf("%d %d %d",nl,nw,nc); } 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 karakterleri inceleyecektir. Sizin return tuşuna her basışınız ise satır sayısının bir artmasına neden olacaktır. Bu moddan çıkmak için Ctrl-\ tuşlarına basın. Eğer programa bir text dosyasından giriş redirection'ı, program otomatik olarak dosyanın bitimine kadar olan tüm karakteleri inceleyecek ve satır kelime ve karakter sayılarını dosya bitince yazarak duracaktır.
KUTU 6 1> Girişteki boşlukları, tabları ve yeni satırları sayan bir program yazın. 2> Girişteki kelimeleri teker teker ayrı satırlara yazan bir program yazın.
