DON'T BELIEVE EVERYTHING YOU C!
Günün sözü: "You don't know what it was, and you didn't know what it looked like, but can you describe it?"
- Amerikalı bir avukatın tanık kürsüsündeki vatandaşa sorusu (ABD resmi mahkeme kayıtlarından alınmış)
Kırk Katır mı Kırk Satır mı? Sanki harf marf saymak çok zevkli bir şeymiş gibi şimdi de satır sayma olayına el atmak vasıl oldu şükürler olsun. Şöyle çok basit bir Cee programı ile klavyeden girilen text'teki satırları saymak mümkün:
#include
/* Adam kac kere
"While" döngüsünün içinde sadece bir "if" dallanması olduğundan bunu
küme işaretleri içine almaya gerek kalmıyor.
Efenim şimdi bu "if" ne işe yarar? Çok basit. Tahmin edeceğiniz
gibi, eğer "if"'ten sonra gelen parantez içindeki önerme doğruysa
(ikili sayı sisteminde "1" değerine sahipse) bu önermeden sonra gelen
komut (ya da küme işaretleri içinde tutulan blok) çalıştırılır; yok
eğer yanlışsa ("0" değerine sahipse), mevzubahis komut (ya da blok)
atlanarak programa devam edilir.
C'deki "==" işlemi, "eşittir" anlamına gelen bir karşılaştırmadır.
Yani Pascal'daki "=" yerine geçer. özellikle Basic'ten C'ye geçenler,
"==" ile karşılaştırma yapacakları yerde alışkanlıktan "="
yazıverirler aman dikkat! Derleyici böyle durumlarda hata vermekten
özellikle kaçınır ve bu yanlışlığı sonradan bulmanız çok uzun
sürebilir. Babam ööle diyoo...
(Halbuki Basic'te karşılaştırma da, atama da aynı şekilde
gösteriliyor ve hiç sorun çıkmıyor; diğer dillerde neden böyle bir
güzellik yok merak konusudur yani...)
(Bu arada StormC bu gidişle AMOS'un pabucunu dama atacak; hepinize
iyice C öğrenip StormC'de yaratıcılığınızı göstermenizi tavsiye
ederim; gerçekten çok profesyonel bir derleyici. Visual C halt etmiş
yani.)
Question: Mrs. Jones, do you believe you are emotionally stable?
Answer: I used to be.
Question: How many times have you commited suicide?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış)
Neyse ne diyorduk? "==" işlemi "eşittir", "!=" işlemi "eşit
değildir", "<" işlemi "küçüktür", ">" işlemi "büyüktür", ">=" işlemi
"büyük veya eşittir", "<=" işlemi ise "küçük veya eşittir" anlamına
geliyor; bunları da böylece halletmiş olduk...
'\n' karakter sabitinin "newline" anlamına geldiğini görmüştük. Tek
tırnak arasına yazılmış '\t', 'A', '6' gibi karakter sabitlerinin
aslında birer integer olduğunu ve o karakterin ASCII koduna karşılık
geldiğini unutmayın. '\n' sabitinin ASCII değeri 10 olduğundan
yukardaki "if" satırını
if (c==10)
olarak da yazabilirdik tabi. Fakat çift tırnak içindeki "%d\n"
string'i, içinde kaç karakter olursa olsun integer olarak
gösterilemez.
Question: She had three children, right?
Answer: yes.
Question: How many were boys?
Answer: none.
Question: Were there girls?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış)
"If" dallanmalarını kullanarak birkaç ilginç programcık yazabiliriz.
örneğin, klavyeden girilen text'teki boşlukları, tab'leri ('\t'), 'A'
harflerini, noktaları, vs. ayrı ayrı saydırmak çocuk oyuncağı. Şu
programa kısaca göz atalım:
#include
main()
Bu minnacık program klavyeden girdiğimiz satırları işliyor ve
tab'leri "T" harfine, boşlukları "S" harfine, backslash'leri ('\\'
olarak gösteriliyor) düzgün slash'e ("/") çevirerek ekrana yeniden
basıyor. Daktilo yazan bir maymundan daha lüzumsuz bir program yani.
Yeni görünen "if-else" yapıları ise "if"'in dallanma özelliğini açıkça
dışa vuruyor. Eğer birinci "if"'ten sonra gelen önerme yanlış ise
sıradaki "else" satırına geçiliyor, bu satırdaki "if"'li önerme test
ediliyor, eğer doğruysa hemen takip eden komut (ya da blok)
çalıştırılıyor, yok yanlışsa bir sonraki "else"'e atlanıyor, vs. Eğer
"if"'li önermelerden hiçbiri doğru değilse en sondaki ("if"'siz)
"else"'in kontrol ettiği blok çalıştırılıyor. Unutulmaması gereken
nokta, bir "if-else" yapısında, ayrı bölümlerde bulunan komut ya da
blokların yalnızca ve yalnızca birinin işleme konulduğu... Tabi "if-
else" içindeki hiçbir komut da çalışmayabilir; eğer siz en sondaki
"else" bölümünü ihmal ederseniz...
Question: Now, doctor, isn't it true that when a person dies in his
sleep, in most cases he just passes quietly away and doesn't know
anything about it until the next morning?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış)
Bu da böylece anlaşıldığına göre (keh keh tabi tabi) "if-else"
kullanan daha karışık bir program yazabiliriz. örneğin programımız,
girilen text'teki karakter, satır ve kelimeleri ayrı ayrı sayabilir:
#include
/* Girdideki karakter, sozcuk ve satirlari sayar bi de cok iyi bulasik
yikar gomlek utuler masallah karin tokluguna calisir Allah kazadan
beladan saklasin */
main()
"||" işlemi "OR"'un C'deki karşılığıdır. "&&" ise "AND"'e tekabül
ediyor. Garip ama gerçek. Yani adam gibi "OR" ve "AND" kullansalar
bi taraflarına batıyor herhalde ki ürkünç semboller kullanma
ihtiyacında hissediyor kendilerini bu C'ciler. Neyse canım, maksat
zaten hava atmak: "Baksana olm, "OR" ile "AND" gibi mantıksal işlemlerin nasıl çalıştığını herhalde
biliyorsunuzdur, zaten adlarından da belli. Yukardaki programda, "c"
değişkeni eğer boşluksa YA DA (||) newline karakteriyse o zaman "if"'i
takip eden komut seti çalıştırılıyor.
"||" ya da "&&" gibi mantıksal işlemler aynı satırda birkaç defa
kullanılabilirler ("if (c==' ' || c=='\n' || c=='\t')" gibi) ve her
zaman soldan sağa doğru hesaplanırlar. önermenin doğruluğu ya da
yanlışlığı kesinlik kazandığı anda hesaplama durur. (Bu kolaylığa
short-circuit evaluation deniyor ve ilerki konularda önem kazanacak)
Yani yukardaki "OR"'lu "if" satırında soldan sağa giderken
karşılaştırmaların herhangi biri doğruysa önermenin doğruluğu
kesinleşir ve geriye kalan kısmı atlanır.
Question: What happened then?
Answer: He told me, he says, "I have to kill you because you can
identify me."
Question: Did he kill you?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış - bence yani)
"OR"'dan MOR'dan geyik açarken yukardaki programın nasıl çalıştığını
anlatmayı unuttuk. Herhalde çözmüşsünüzdür ama, kafası karışanlar
için: Programımız "while"'dan hemen sonra gelen "++karsay" satırı ile
girilen her harf için (
satsay=(sozsay=(karsay=0));
Caghan "fMuadDib" Demirci
#define ENDOFINPUT '!'
main()
{
int c, satsay;
satsay=0;
while ((c=getchar())!=ENDOFINPUT)
if (c=='\n')
++satsay;
printf("%d\n", satsay);
}
#define ENDOFINPUT '!'
{
int c;
while ((c=getchar())!=ENDOFINPUT)
if (c=='\t')
putchar('T');
else if (c==' ')
putchar('S');
else if (c=='\\')
putchar('/');
else
putchar(c);
}
#define ENDOFINPUT '!'
#define SOZCUKDISI 0 /* kelimenin disindaysak */
#define SOZCUKICI 1 /* kelimenin icindeysek */
{
int c, satsay, sozsay, karsay, nerdeyiz;
satsay=sozsay=karsay=0;
nerdeyiz=SOZCUKDISI;
while ((c=getchar()!=ENDOFINPUT)
{
++karsay;
if (c=='\n')
++satsay;
if (c==' ' || c=='\n')
nerdeyiz==SOZCUKDISI;
else if (nerdeyiz=SOZCUKDISI)
{
nerdeyiz=SOZCUKICI;
++sozsay;
}
}
printf("harf=%d sozcuk=%d satir=%d\n", karsay, sozsay, satsay);
}
gibi absürd ve sürrealist bir ucubelik yaratmak yerine C'nin
bahşettiği göz kamaştırıcı kolaylıkları sonuna kadar değerlendirerek
satsay=sozsay=karsay=0;
yazmış olduğumuza dikkat ediniz.
Falan filan...
Question: You say that the stairs went down to the basement?
Answer: Yes.
Question: And these stairs, did they go up also?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış - ordan da ben aldım)
Kelime sayma programı üzerinde oynayarak, örneğin, klavyeden
girdiğimiz text'teki her kelimeyi ayrı bir satıra yazan bir program
üretebilirsiniz. Ya da "Niye bu aptal ve acaip can
sıkıcı saçmalıklarla uğraşıyorum, text processing'den bana ne?" de
diyebilirsiniz. Yani herşey sizin hayalgücünüze kalmış.
diyerek "benimcanimarrayim" adli 30 adet integer elemanlı bir array
hazırlamış oluyoruz. Array nedir bilmeyenler için: Bir değişken
düşünün ki, içinde n tane birbirinden bağımsız oda olsun ve her bir
oda daha önceden tanımlanmış type'taki bir değeri içinde tutabilsin.
Bu odaların isimleri değil numaraları olsun ve tuttukları değerler,
içinde bulundukları array'in ismi kullanılarak ulaşılsın. (örneğin:
"benimcanimarrayim[10]" = "benimcanimarrayim"'in 11'inci elemanı).
Array'lerin, birden fazla değeri aynı isim altında tutma yeteneği
birçok durumda F15'teki paraşütlerden daha fazla hayat
kurtarıcı oluyor. Fakat unutmayın ki array, kendi ismiyle (eleman
numarası vermeden) atama, karşılaştırma gibi pozisyonlarda
kullanılamaz çünkü kendine ait belli bir değeri yoktur, sadece
numaralı odaları vardır.
Question: Have you lived in this town all of your life?
Answer: not yet.
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış - şimdi bunun array'le
alakası ne?)
Text işleme takıntımıza dönersek görürüz ki, array kullanarak her
rakamın kaç defa kullanıldığını takip etmek artık çok kolay. Bunların
yanında girdideki boşluk ve
#define ENDOFINPUT '!'
/* Rakamlari, aralari ve diger zimbirtilari sayar */
main()
{
int c, i, arasay, digersay;
int rakamsay[10];
arasay=digersay=0;
for (i=0; i<=9; ++i)
rakamsay[i]=0; /* Array'in butun elemanlarini sifirliyoruz */
while ((c=getchar())!=ENDOFINPUT)
if (c>='0' && c<='9')
++rakamsay[c-'0'];
else if (c==' ' || c=='\n' || c=='\t')
++arasay;
else
++digersay;
printf("rakamlar =");
for (i=0; i<=9; ++i)
printf(" %d", rakamsay[i]);
printf("\n");
printf("aralar = %d\ndiger zimbirtilar = %d\n", arasay, digersay);
}
İşte böyle... Programın başında "rakamsay" değişkenini 10
elemanlı bir array olarak yarattık. Pascal'dan farklı olarak C'de,
eleman numaraları (index'leri) 0'dan başlar. Yani: rakamsay[0],
rakamsay[1], ..., rakamsay[9]. ¦şte yukardaki iki "for" döngüsünde de
index'lerin bu özelliğine dikkat ettik. ("i", 0'dan başlayıp 9'da
bitiyor.)
if (c>='0' && c<='9')
satırı ise "c"'nin bir rakam olup olmadığını sorguluyor. ASCII
karakter setinde rakamların sıralanışı akla mantığa uygun olarak
küçükten büyüğe doğru '0', '1', '2', ..., '9' diye gittiği için
"(c>='0' && c<='9')" önermesiyle "c"'nin rakam olup olmadığını kısa
yoldan öğrenmiş oluyoruz. Yani önermenin doğrulanması için "c"'nin
ASCII değerinin hem '0'ınkinden büyük, hem de (AND) '9'unkinden küçük
olması gerekiyor. Yine ASCII'nin bu özelliğini kullanarak
"++rakamsay[c-'0']" diyerek, "rakamsay"'ın doğru rakama tekabül eden
index'ini kısa yoldan artırmayı becerdik. "c-'0'" işlemi ile bir
int'ten bir char çıkarıyor gibi görünsek de aslında char'lar int'lerin
bir alt kümesi ve işlemin sonucu 0 ile 9 arasında bir integer.
Açıkçası:
'0'ın integer değeri 48. Biz de klavyeden '6' girmiş olalım.
"Getchar" fonksiyonu "c" değişkenine '6'nın integer değeri olan 54'ü
atadı. "c-'0'" işleminin sonucu böylece (54-48=)6 değerinde bir
integer oldu. Bu dahiyane yöntemle abuk subuk bir ASCII integer'ına
tekabül eden bir char'ı, 0 ile 9 arasında adam gibi bir integer'a
çevirerek "rakamsay"'ın index'i olarak kullanabildik. Helal olsun
bize...
Question: Was that the same nose you broke as a child?
---
Question: Was it you or your brother that was killed in the war?
---
Question: The youngest son, the 20 year old, how old is he?
---
Question: Were you alone or by yourself?
---
Question: How long have you been a French Canadian?
---
Question: Do you have any children or anything of that kind?
---
Question: Were you present in the court this morning when you were
sworn in?
---
Question: So you were gone until you returned?
---
- Avukatların tanık kürsüsündeki vatandaşlara soruları
(ABD resmi mahkeme kayıtlarından alınmış - gerizekalı Amerikalılar)
"if-else" dallanması bu programda hem işimizi kolaylaştırıyor, hem
de açık-seçikliği sağlıyor. Çok yakında "switch" komutuyla
dallanmayı daha da basitleştirebileceğiz.
---> PROJE 1:
Girilen text'teki her kelimenin uzunluğunu sütun grafiği şeklinde
listeleyen bir program yazınız.
Çüşşş.
Bakın çok ciddi söylüyorum, zor bir şey değil. Her kelimenin
uzunluğunu ayrı bir array elemanında saklayacaksınız, sonunda da her
elemanı inceleyip tuttuğu sayı kadar ekrana '*' (örneğin)
yazacaksınız. Yani sütunlar soldan sağa uzanacak.
Hadi bakalım buraya kadar kolaydı, bundan sonra da yukarıdan
aşağı uzanan bir sütun grafiği yapmayı deneyin bakalım.
¦mkansız değil, vaktiniz kalırsa uğraşın bence. Nasılolsa
yapacak daha iyi bi işiniz yok değil mi ama?
Hmm çok faydalı bişiymiş galiba örnek verebilir müsünüz?
Aklımız estikçe kullandığımız "printf", "getchar", ve "putchar"
hepsi birer fonksiyondur ve bunların kaynak kodları "stdio.h" içinde
belirtilmiştir. Eğer "stdio.h"'i include etmeseydik bu
komutları kullanamazdık. Şimdi kendi fonksiyonumuzu yazmanın
zamanı geldi.
Ee ne yazacaz peki bu yaz sıcaanda aklına bişey gelöör mu?
Bütün dünyada ve hatta galaksimizin alpha, beta, gamma ve delta
quadrantlarının mesken köşelerinde programcılar, hangi dili
kullanırlarsa kullansınlar, mutlaka bir "power" fonksiyonu yazarak
büyülü fonksiyon dünyasına adımlarını atarlar. Yani demek istediğim,
şu anda biz power fonksiyonu yazarken aynı anda dünya üzerindeki
yüzlerce programcı adayı daha heyecanla ilk power
fonksiyonlarını kodluyorlardır herhalde. Hatta, yıllanmış
programcıların, işte canı sıkıldıklarında sırf vakit geçsin diye
bilgisayar başına oturup 5-6 dilde power fonksiyonu çeşitlemeleri
yazdıkları da söylentiler arasındadır ona göre. Neyse canım, bu power
fonksiyonu dedikleri üs almaya yarar, o kadar basit. Her kitapta
"power" diyor, ben ne yapayım?
Yazdık diyelim bu fonksiyon nası çalışıcak?
Main programdan "power(a, b)" diye bir komut verdiğimiz zaman
fonksiyonun geri gönderdiği değer, a sayısının b'inci kuvveti olacak.
Yani,
printf("%d", power(2, 5));
dediğimiz zaman ekrana "32" yazılacak. Ne yazık ki power
fonksiyonumuz şimdilik negatif tamsayıları ya da ondalık sayıları
desteklemiyor. '98 sürümünde bunlar da olacak inşallaaah tabi
tabi...
Biraz düşündükten sonra şöyle bir fonksiyon çıkarabiliriz:
/* power: as'i us'uncu kuvvetine cikarir; us>=0 */
int power(int as, int us)
{
int i, kuvvet;
kuvvet=1;
for (i=1; i<=us; ++i)
kuvvet=kuvvet*as;
return kuvvet;
}
Gördüğünüz gibi power, main() programın küçük bir kopyası gibi...
Fonksiyonun ilk satırı parametrelerin type'larını ve isimlerini
tanımlıyor; ayrıca fonksiyonun return ettiği (main programa geri
gönderdiği) sonucun type'ını da burdan öğrenebiliyoruz (int). Bir
fonksiyonun kullandığı parametre değişkenleri ve fonksiyonun vücudunda
tanımlanan diğer değişkenler tamamen o fonksiyona yereldir ve
programımızın diğer fonksiyonları tarafından "görülemezler." örneğin:
#include
int power(int, int);
/* power fonksiyonu test ediyoz */
main()
{
int i;
for (i=0; i<10; ++i)
printf("%d %d %d\n", i, power(3, i), power(-2, i));
return 0;
}
/* power: as'i us'uncu kuvvetine cikarir; us>=0 */
int power(int as, int us)
{
int i, kuvvet;
kuvvet=1;
for (i=1; i<=us; ++i)
kuvvet=kuvvet*as;
return kuvvet;
}
Böyle bir programda main fonksiyonundaki "i" değişkeniyle power
fonksiyonundaki "i" değişkeninin birbiriyle hiçbir ilişkisi yoktur.
(main'in de [hafiften özelleşmiş de olsa] aslında bir fonksiyon
olduğunu hatırlatalım.)
Question: Now, Mrs. Johnson, how was your first marriage terminated?
Answer: By death.
Question: And by whose death was it terminated?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış)
Power fonksiyonunun return ettiği değer bir int olduğundan,
printf("%d %d %d\n", i, power(3, i), power(-2, i));
satırında main program, "power(3, i)"'yi aynen "3" ya da "i" gibi
bir integer olarak görür. Yani "%d" parametresini çelişkiye yol
açmadan kullanabiliriz. Ama tabi ki her fonksiyon integer bir değer
return etmek zorunda değildir. (Bu cümle de amma ¦ngilizce oldu hay
Allah kim bööle olsun isterdi..?) :^D
C'de "return" komutundan sonra her türlü işlem gelebilir. ("return
3+i" gibi) Aynı zamanda fonksiyondan çıkılır ve kontrol, çağıran
fonksiyona geri gönderilir. Yani fonksiyonu bitiren "}" işaretine
daha ulaşmadan fonksiyon terkedilebilir. Buna rağmen hiçbir fonksiyon
bir değer return etmek zorunda değildir. örneğin, main()
fonksiyonunun sonunda "return" kullanmamıza gerek yoktu; ama ne olur
ne olmaz; programdan bittikten sonra Workbench'imize herşeyin yolunda
gittiği sinyalini verebilmek için "return 0" satırını kelam ettik.
0'dan başka bir değer, çalıştırılan programda bir abukluk meydana
geldiğini ve bu abukluk yüzden programdan çıkıldığını bildirir.
Question: Do you know how far pregnant you are now?
Answer: I'll be three months on Nov. 8.
Question: Apparently, then the date of conception was August 8?
Answer: Yes.
Question: What were you doing at that time?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış - ne ya bu takılmış plak
gibi?)
main()'den hemen önce gelen
int power(int, int);
satırı ile power fonksiyonunu erkenden tanımlamış olduk. Power'ın
iki integer argüman beklediğini ve integer bir değer return ettiğini
önceden derleyiciye bildirerek, olası minik hatalar yüzünden ilerde
saatlerce kafa patlatma olasılığını bertaraf ettik hiaayt!
Tamam işte bu kadar. Fonksiyon yazarak C bloklarını kutucuklara
koymak ve "getchar" gibi kendi fonksiyonlarımızı yaratmak oldukça
basit. Geçen sayıda yaptığımız fahrenheit-santigrad çevirim
programını hatırlıyor musunuz? (zaten hatırlamazsanız bi kafa...)
Alıştırma yapmış olmak için, o programı yeniden yazıp çevirim işlemini
ayrı bir fonksiyona yaptırabilirsiniz. (amele fonksiyonlar işte...)
Question: Do you recall approximately what time that you examined the
body of Mr. Edington at the Rose Chapel?
Answer: It was in the evening. The autopsy started about 8:30 p.m.
Question: And Mr. Edington was dead at the time, is that correct?
- Avukatın tanık kürsüsündeki vatandaşa sorusu
(ABD resmi mahkeme kayıtlarından alınmış)
Pe. Se.: C'deki fonsiyonlar, başka bir fonksiyondan parametre olarak
aldıkları değerleri değiştiremezler. Yani kendi içlerinde o
parametreler üzerinde istedikleri oynamaları yapabilirler; fakat
çağıran fonksiyona kontrolü geri gönderdiklerinde, parametreler
çağırım öncesindeki değerlerini alır. Bu güzelliği kullanarak power
fonksiyonunu, "i" değişkenini kesinlikle kullanmadan kodlayabiliriz.
Buyrun bakalım kah kah vatandaşa iş çıktı biz de helvasını yiyelim...
Geçmiş ola. Bir dolu dolu Si yazısı daha burda bitiyo. Fonksiyonlu
programlarınız kolay gelsin. Bi dahaki sayıda Bill Gates'in neden ama
neden otistik olduğunu detaylarıyla inceleyeceğiz. Aman ha
kaçırmayın... Hadi kalın sağlıcakla... :D
demircca@boun.edu.tr