Genel Programlama I
Genel Programlama I
Genel Programlama I
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
<strong>Genel</strong> <strong>Programlama</strong> I<br />
04.11.2010<br />
Ders 6<br />
1
Fonksiyonlar<br />
C'de alt programlara fonksiyon denir. Fonksiyon sözcüğü burada matematiksel<br />
anlamıyla değil diğer programlama dillerinde kullanılan, "alt program", "prosedür",<br />
"subroutine" sözcüklerinin karşılığı olarak kullanılmaktadır.<br />
Fonksiyonlar C dilinin temel yapı taşlarıdır. Çalıştırılabilen bir C programı en az bir C<br />
fonksiyonundan oluşur. Bir C programının oluşturulmasında fonksiyon sayısında bir<br />
kısıtlama yoktur.<br />
Fonksiyonların onları çağıran fonksiyonlardan aldıkları girdileri ve yine onları çağıran<br />
fonksiyonlara gönderdikleri çıktıları vardır. Fonksiyonların girdilerine aktüel<br />
parametreler (actual parameters) ya da argumanlar (arguments) diyoruz.<br />
Fonksiyonların çıktılarına geri dönüş değeri (return value) diyoruz.<br />
2
Bir fonksiyon iki farklı amaçla kullanılabilir :<br />
•Fonksiyon, icrası süresince belli amaçları gerçekleştirir. (Belli işlemleri yapar)<br />
•Fonksiyon icrası sonunda üreteceği bir değeri kendisini çağıran fonksiyona<br />
gönderebilir.<br />
Fonksiyonların Tanımlanması ve Çağırılması<br />
Bir fonksiyonun ne iş yapacağının ve bu işi nasıl yapacağının C dilinin sentaks<br />
kurallarına uygun olarak anlatılmasına o fonksiyonun tanımlanması (definition)<br />
denir. Fonksiyon tanımlamaları aşağıda inceleneceği gibi birtakım kurallara tabidir.<br />
Bir fonksiyonun çağırılması ise o fonksiyonun yapacağı işi icraya davet edilmesi<br />
anlamına gelir. Fonksiyon çağırma ifadesi karşılığında derleyici, programın akışını<br />
ilgili fonksiyonun kodunun bulunduğu bölgeye aktaracak şekilde bir kod üretir.<br />
Programın akışı fonksiyonun kodu içinde akıp bu kodu bitirdiğinde, yani fonksiyon<br />
icra edildiğinde, programın akışı yine fonksiyonun çağırıldığı noktaya geri<br />
dönecektir.<br />
3
Fonksiyonların Geri Dönüş Değerleri (return values)<br />
Bir fonksiyonun yürütülmesi sonunda onu çağıran fonksiyona dönüşünde<br />
gönderdiği değere, fonksiyonun geri dönüş değeri (return value) denmektedir.<br />
Her fonksiyon bir geri dönüş değeri üretmek zorunda değildir. Fonksiyonların geri<br />
dönüş değerleri farklı amaçlar için kullanılabilir;<br />
Bazı fonksiyonlar tek bir değer elde etmek amacıyla tasarlanmışlardır. Elde<br />
ettikleri değeri de kendilerini çağıran fonksiyonlara geri dönüş değeri olarak<br />
iletirler. Örneğin:<br />
y = pow(2, 3);<br />
pow fonksiyonu standart bir C fonksiyonudur. Birinci parametresiyle belirtilen<br />
sayının ikinci parametresiyle belirtilen kuvvetini hesaplayarak, hesapladığı sayıyı<br />
geri dönüş değeri olarak kendisini çağıran fonksiyona iletir. Yukarıdaki örnekte 2<br />
sayısının 3. kuvveti bu fonksiyon yardımıyla hesaplanarak bulunan değer y<br />
değişkenine atanmıştır.<br />
4
Bazı fonksiyonların geri dönüş değerleri fonksiyonun yürütülmesi sırasında yapılan<br />
işlemlerin başarısı hakkında bilgi verir. Yani bu tür fonksiyonların geri dönüş değerleri<br />
test amacıyla kullanılmaktadır. Geri dönüş değerleri yapılması istenen işlemin başarılı<br />
olup olmaması durumunu açıklar. Örneğin :<br />
p = malloc(200);<br />
ifadesiyle bellekte 200 byte uzunluğunda bir blok tahsis etmek isteyen programcı bu<br />
işlemin başarılı bir biçimde yerine getirilip getirilmediğini de test etmek zorundadır.<br />
Hemen arkasından p değişkeninin aldığı değeri kontrol edecek ve işlemin başarısı<br />
hakkında bir karara varacaktır. Dolayısıyla malloc fonksiyonunun geri dönüş değeri,<br />
fonksiyonun yapması gereken işin başarılı bir şekilde sonuçlanıp sonuçlanmadığını<br />
göstermektedir.<br />
5
Bazı fonksiyonlar kendilerine gönderilen argumanları belirli bir kritere göre test<br />
ederler. Ürettikleri geri dönüş değerleri ise test sonucunu belirtir. Örneğin:<br />
if (isalpha(ch)) {<br />
...<br />
}<br />
Burada isalpha fonksiyonu arguman olarak gönderilen karakterin bir harf<br />
karakteri olup olmadığını test eder. Eğer harf karakteriyse, isalpha fonksiyonu 0<br />
dışı bir değere geri dönecek, eğer harf karakteri değilse 0 değerine geri<br />
dönecektir. Çağıran fonksiyonda da geri dönüş değerine göre farklı işlemler<br />
yapılabilecektir.<br />
6
Bazı fonksiyonlar hem belli bir amacı gerçekleştirirler hem de buna ek olarak<br />
amaçlarını tamamlayan bir geri dönüş değeri üretirler. Örneğin :<br />
x = printf("Merhaba Dünya\n");<br />
Burada printf fonksiyonu ekrana Merhaba Dünya yazısını yazmak için kullanılmıştır.<br />
Ancak ekrana yazdığı karakter sayısını da geri dönüş değeri olarak vermektedir.<br />
Bir yazı içersinde bulunan belirli bir karakteri silecek bir fonksiyon tasarladığımızı<br />
düşünelim. Fonksiyon işini bitirdikten sonra yazıdan kaç karakter silmiş olduğunu geri<br />
dönüş değeri ile çağırıldığı yere bildirilebilir.<br />
7
Bazen geri dönüş değerlerine ihtiyaç duyulmaz. Örneğin yalnızca ekranı silme<br />
amacıyla tasarlanmış olan bir fonksiyonun geri dönüş değerine sahip olması<br />
gereksizdir.<br />
clrscr();<br />
clrscr fonksiyonu yalnızca ekranı siler, böyle bir fonksiyonun geri dönüş<br />
değerine ihtiyacı yoktur.<br />
Fonksiyonların geri dönüş değerlerinin de türleri söz konusudur.<br />
Fonksiyonların geri dönüş değerleri herhangi bir türden olabilir. Geri dönüş<br />
değerlerinin türleri fonksiyonların tanımlanması sırasında belirtilir.<br />
8
Fonksiyonların Tanımlanması<br />
Kendi yazdığımız fonksiyonlar için tanımlama (definition) terimini kullanıyoruz. C'de<br />
fonksiyon tanımlama işleminin genel biçimi şöyledir:<br />
[Geri dönüş değerinin türü] ([parametreler])<br />
{<br />
...<br />
...<br />
}<br />
Yukarıdaki gösterimde açısal parantez içinde belirtilen ifadeler zorunlu olarak<br />
bulunması gerekenleri köşeli parantez içinde belirtilen ifadeler ise bulunması zorunlu<br />
olmayan, isteğe bağlı (optional) ifadeleri göstermektedir. Tanımlanan fonksiyonlar en<br />
az bir blok içerirler. Bu bloğa fonksiyonun ana bloğu denir. Ana blok içinde istenildiği<br />
kadar içiçe blok yaratılabilir. Aşağıdaki fonksiyon tanımlamasından fonk1<br />
fonksiyonunun parametre almadığını ve geri dönüş değerinin de double türden<br />
olduğunu anlıyoruz.<br />
double fonk1()<br />
{<br />
...<br />
... Fonksiyonun ana bloğu<br />
...<br />
}<br />
9
void Anahtar Sözcüğü<br />
Bir fonksiyonun parametre değişkeni ya da geri dönüş değeri olmak zorunda<br />
değildir. Bir fonksiyonun parametre değişkeni olmadığı iki şekilde belirtilebilir:<br />
•Fonksiyon parametre parantezinin içi boş bırakılır, yani buraya hiçbirşey yazılmaz.<br />
•Fonksiyon parametre parantezinin içine void anahtar sözcüğü yazılır.<br />
fonk() fonk(void)<br />
{ {<br />
... ...<br />
} }<br />
Yukarıdaki tanımlamalar C'de aynı anlama gelmiyor. Fonksiyon prototipleri konusunu<br />
öğrenirken bu iki tanımlama arasındaki farkı da öğrenmiş olacağız. Şimdilik bu iki<br />
tanımlamanın aynı anlama geldiğini ve fonksiyonun parametre almadığını<br />
belirttiklerini varsayacağız.<br />
10
Geri dönüş değerine ihtiyaç duyulmadığı durumlarda da geri dönüş<br />
değerinin türü yerine void anahtar sözcüğü yerleştirilir. Örneğin:<br />
void sample(void)<br />
{<br />
...<br />
}<br />
Yukarıda tanımlanan sample fonksiyonu parametre almamakta ve bir<br />
geri dönüş değeri de üretmemektedir.<br />
Fonksiyon tanımlarken geri dönüş değeri yazılmayabilir. Bu durum geri<br />
dönüş türünün olmadığı anlamına gelmez. Eğer geri dönüş değeri<br />
yazılmazsa, C derleyicileri tanımlanan fonksiyonun int türden bir geri<br />
dönüş değerine sahip olduğunu varsayarlar. Örneğin :<br />
sample2()<br />
{<br />
...<br />
}<br />
Tanımlanan sample2 fonksiyonunun parametresi yoktur ama int türden<br />
bir geri dönüş değeri vardır.<br />
11
C dilinde fonksiyon içinde fonksiyon tanımlanamaz!<br />
Örneğin aşağıdaki durum error oluşturur, çünkü sample2 fonksiyonu<br />
sample1 fonksiyonunun içinde tanımlanmıştır:<br />
double sample1()<br />
{<br />
...<br />
int sample2() /* error */<br />
{<br />
...<br />
}<br />
...<br />
}<br />
tanımlamanın aşağıdaki şekilde yapılması gerekirdi :<br />
double sample1()<br />
{<br />
...<br />
}<br />
int sample2()<br />
{<br />
...<br />
}<br />
12
Fonksiyonların Çağırılması (function calls)<br />
C dilinde fonksiyon çağırma operatörü olarak () kullanılmaktadır. Bir fonksiyon<br />
çağırıldığı zaman programın akışı fonksiyonu icra etmek üzere bellekte fonksiyonun<br />
kodunun bulunduğu bölgeye atlar, fonksiyonun icra edilme işlemi bittikten sonra da<br />
akış tekrar çağıran fonksiyonun kalınan yerinden devam eder.<br />
Bir fonksiyonun geri dönüş değeri varsa, fonksiyon çağırma ifadesi geri dönüş değerini<br />
üretir.<br />
Geri dönüş değeri bir değişkene atanabileceği gibi doğrudan aritmetik işlemlerde de<br />
kullanılabilir. Örneğin:<br />
sonuc = hesapla();<br />
Burada hesapla fonksiyonunun çağırılma ifadesiyle üretilen geri dönüş değeri sonuc<br />
değişkenine atanmaktadır. Bir başka deyişle bir fonksiyon çağırma ifadesinin ürettiği<br />
değer, ilgili fonksiyonun ürettiği (eğer üretiyorsa) geri dönüş değeridir. Yukarıdaki<br />
örnekte önce hesapla() fonksiyonu çağırılacak daha sonra fonksiyonun icra<br />
edilmesiyle oluşan geri dönüş değeri sonuc değişkenine atanacaktır.<br />
13
Fonksiyonlar ancak tanımlanmış fonksiyonların içerisinden çağırılabilirler. Blokların<br />
dışından fonksiyon çağırılamaz. Çağıran fonksiyon ile çağırılan fonksiyonun her ikisi de<br />
aynı amaç kod içerisinde bulunmak zorunda değildir. Çağıran fonksiyon ile çağırılan<br />
fonksiyon farklı amaç kodlar içerisinde de bulunabilir. Çünkü derleme işlemi sırasında<br />
bir fonksiyonun çağırıldığını gören derleyici, amaç kod içerisine (yani .obj içine)<br />
çağırılan fonksiyonun adını ve çağırılış biçimini yazmaktadır. Çağıran fonksiyon ile<br />
çağırılan fonksiyon arasında bağlantı kurma işlemi, bağlama aşamasında, bağlayıcı<br />
program (linker) tarafından yapılır.<br />
Bu nedenle tanımlanan bir fonksiyon içerisinde, var olmayan bir fonksiyon çağırılsa bile<br />
derleme aşamasında bir hata oluşmaz. Hata bağlama aşamasında oluşur. Çünkü<br />
bağlayıcı çağırılan fonksiyonu bulamayacaktır.<br />
Bütün C programları çalışmaya main fonksiyonundan başlar. Programın başladığı<br />
nokta olma dışında main fonksiyonunun diğer fonksiyonlardan başka hiçbir farkı<br />
yoktur. main fonksiyonun icrası bitince program da sonlanır. Bir C programının<br />
çalışabilmesi için mutlaka bir main fonksiyonuna sahip olması gerekir.<br />
14
Değer döndürmeyen bir fonksiyonun tanımlanması ve çağırılması:<br />
15
Değer döndüren bir fonksiyona örnek:<br />
16
eturn İfadesi<br />
Bir fonksiyonun geriye değer döndürüp döndürmemesi, o fonksiyonu genel yapı<br />
içersinde nasıl kullanacağınıza bağlıdır. Eğer hazırlayacağınız fonksiyonun, çalışıp,<br />
üreteceği sonuçları başka yerlerde kullanmayacaksanız, fonksiyondan geriye değer<br />
dönmesi gerekmez. Ancak fonksiyonun ürettiği sonuçları, bir değişkene atayıp<br />
kullanacaksanız, o zaman fonksiyonun geriye değer döndürmesi gerekir. Bunun için<br />
'return' ifadesini kullanırız.<br />
Geriye değer döndürmeyen fonksiyonları tanımlarken, başına void koyuyorduk. Geriye<br />
değer döndüren fonksiyonlar içinse, hangi tipte değer dönecekse, onu fonksiyon<br />
adının başına koyuyoruz. Diyelim ki fonksiyonumuz bir tamsayı döndürecekse, int; bir<br />
karakter döndürecekse char diye belirtiyoruz. Fonksiyon içersinden neyin döneceğine<br />
gelince, burada da return ifadesi devreye giriyor.<br />
Fonksiyonun neresinde olduğu farketmez, return sonuç döndürmek üzere kullanılır.<br />
Döndüreceği sonuç, elle girilmiş veya değişkene ait bir değer olabilir. Önemli olan<br />
döndürülecek değişken tipiyle, döndürülmesi vaad edilen değişken tipinin<br />
birbirinden farklı olmamasıdır. Yani int kup_hesapla( ) şeklinde bir tanımlama<br />
yaptıysanız, double tipinde bir sonucu döndüremezsiniz. Daha doğrusu<br />
döndürebilirsiniz ama program yanlış çalışır. Tip uyuşmazlığı genel hatalardan biri<br />
olduğu için, titiz davranmanız gerekir.<br />
17
Dikkat edilmesi gereken bir diğer konu; return koyduğunuz yerde, fonksiyonun<br />
derhâl sonlanmasıdır. Fonksiyonun kalan kısmı çalışmaz.<br />
Geriye değer döndürmeyen fonksiyonlar için de aynı durum geçerlidir, onlarda<br />
da return ifadesini kullanabilirsiniz. Değer döndürsün, döndürmesin yazdığınız<br />
fonksiyonda herhangi bir yere 'return;' yazın. Fonksiyonun bu noktadan<br />
itibaren çalışmayı kestiğini fark edeceksiniz.<br />
Bu fonksiyonu çalıştırmanın uygun olmadığı şartlarda, kullanabileceğiniz bir<br />
yöntemdir. Bir kontrol ekranında, kullanıcı adı ve/veya şifresini yanlış<br />
girildiğinde, programın çalışmasını anında kesmek isteyebilirsiniz. Böyle bir<br />
durumda 'return;' kullanılabilir.<br />
18
Fonksiyon Prototipleri<br />
Bildiğiniz gibi fonksiyonlarımızı, main( ) üzerine yazıyoruz. Tek kısa bir<br />
fonksiyon için bu durum rahatsız etmez; ama uzun uzun 20 adet fonksiyon<br />
olduğunu düşünün. main( ) fonksiyonu sayfalar dolusu kodun altında kalacak<br />
ve okunması güçleşecektir. Fonksiyon prototipleri burada devreye girer.<br />
Daha önce yazdığımız programı tekrar yazalım. Ama bu sefer, fonksiyon<br />
prototipi yapısına uygun olarak bunu yapalım.
Yukarıdaki programda<br />
alan( ) fonksiyonunu, main( ) fonksiyonundan önce yazmadık. Sadece<br />
böyle bir fonksiyon olduğunu ve alacağı parametre tiplerini bildirdik.<br />
( İsteseydik parametre adlarını da yazabilirdik ama buna gerek yok. )<br />
Daha sonra main( ) fonksiyonu altına inip, fonksiyonu yazdık.<br />
20
Elektrik Alan hesaplayan fonksiyon<br />
21
Bu fonksiyonun prototip ile yazılması<br />
22
Rekürsif Fonksiyonlar<br />
Bir fonksiyon içersinden, bir diğerini çağırabiliriz. Rekürsif fonksiyonlar,<br />
fonksiyon içersinden fonksiyon çağırmanın özel bir hâlidir. Rekürsif fonksiyon<br />
bir başka fonksiyon yerine kendisini çağırır ve şartlar uygun olduğu sürece bu<br />
tekrarlanır. Rekürsif, Recursive kelimesinden geliyor ve tekrarlamalı,<br />
yinelemeli anlamını taşıyor. Kelimenin anlamıyla, yaptığı iş örtüşmekte.<br />
Rekürsif fonksiyonları bir kenara bırakığ, bildiğimiz yöntemle 1, 5, 9, 13<br />
serisini oluşturan bir fonksiyon yazalım:<br />
23
Bu fonksiyonu yazmak oldukça basitti. Şimdi aynı işi yapan rekürsif bir<br />
fonksiyon yazalım:<br />
Son yazdığımız programla, bir önce yazdığımız program aynı çıktıları üretir.<br />
Ama birbirlerinden farklı çalışırlar.<br />
24
İkinci programın farkını akış diyagramına bakarak sizler de görebilirsiniz.<br />
Rekürsif kullanım, fonksiyonun tekrar tekrar çağrılmasını sağlamıştır.<br />
25
Faktöriyel hesaplayan fonksiyonu, rekürsif olarak yazalım:<br />
26
faktöriyel hesaplaması yapılırken, adımları görmenizi istiyorum. Adım olarak<br />
geçen her kutu, fonksiyonun bir kez çağrılmasını temsil ediyor. Başlangıç<br />
kısmını geçerseniz fonksiyon toplamda 5 kere çağrılıyor.<br />
27
Rekürsif yapılar, oldukça karmaşık olabilir. Fakat kullanışlı oldukları kesin.<br />
Örneğin silme komutları rekürsif yapılardan yararlanır. Bir klasörü altında<br />
bulunan her şeyle birlikte silmeniz gerekiyorsa, rekürsif fonksiyon<br />
kaçınılmazdır. Ya da bazı matematiksel işlemlerde veya arama ( search )<br />
yöntemlerinde yine rekürsif fonksiyonlara başvururuz. Bunların dışında<br />
rekürsif fonksiyonlar, normal fonksiyonlara göre daha az kod kullanılarak<br />
yazılır. Bunlar rekürsif fonksiyonların olumlu yönleri... Ancak hiçbir şey<br />
mükemmel değildir.<br />
Rekürsif fonksiyon kullanmanın bilgisayarınıza bindereceği yük daha fazladır.<br />
Faktoriyel örneğine bakın; tam 5 kez aynı fonksiyonu çağırıyoruz ve bu sırada<br />
bütün değerler bellekte tutuluyor. Eğer çok sayıda iterasyondan söz ediyorsak,<br />
belleğiniz hızla tükenecektir. Rekürsif yapılar, bellekte ekstra yer kapladığı gibi,<br />
normal fonksiyonlara göre daha yavaştır. Üstelik kısa kod yazımına karşın,<br />
rekürsif fonksiyonların daha karmaşık olduklarını söyleyebiliriz. Anlamak zaman<br />
zaman sorun olabiliyor. Kısacası bir programda gerçekten rekürsif yapıya<br />
ihtiyacınız olmadığı sürece, ondan kaçınmanız daha iyi!<br />
28
Matematiksel İşlemler<br />
Matemetik kütüphanesi math.h kullanılarak kütüphane içerisindeki hazır<br />
matematiksel işlemler yapılabilir.<br />
Sıkça kullanılan matematiksel fonksiyonlar.<br />
• double ceil( double n ) : Virgüllü n sayısını, kendisinden büyük olan ilk tam<br />
sayıya tamamlar. Örneğin ceil(51.4) işlemi, 52 sonucunu verir.<br />
• double floor( double n ) : Virgüllü n sayısının, virgülden sonrasını atarak, bir<br />
tam sayıya çevirir. floor(51.4) işlemi, 51 sayısını döndürür.<br />
• double fabs( double n ) : Verilen n sayısının mutlak değerini döndürür. fabs(-<br />
23.5), 23.5 değerini verir.<br />
• double fmod( double a, double b ) : a sayısının b sayısına bölümünden kalanı<br />
verir. (Daha önce gördüğümüz modül (%) operatörü, sadece tam sayılarda<br />
kullanılırken, fmod fonksiyonu virgüllü sayılarda da çalışır.)<br />
29
• double pow( double a, double b ) : Üstel değer hesaplamak için kullanılır;<br />
a b değerini verir.<br />
• double sqrt( double a ) : a'nın karekökünü hesaplar.<br />
Bu fonksiyonlardan herhangi birini kullacağınız zaman, program kodununun<br />
başına #include yazmalısınız.<br />
Ayrıca derleyici olarak gcc'yle çalışıyorsanız, derlemek için -lm parametresini<br />
eklemeniz gerekir. (Örneğin: "gcc –o –lm test test.c " gibi...)<br />
30
Üslü sayılarla işlem yapmak<br />
Programlarınızda kullandığınız değişkenler veya sabitler her zaman tam sayı<br />
veya ondalık şeklinde yazabileceğiniz sayılar olmayabilir. Bazen pozitif veya<br />
negatif üslü sayılarla işlem yapmanız gerekebilir.<br />
Örneğin elektrik alan hesabında kullanmanız gereken k=8,99x 10 9 sabitini<br />
programınızda nasıl yazarsınız bunu göreceğiz.<br />
k=8,99e+9<br />
Program içerisinde üslü sayı yazarken üslü kısım yerine “e” yanına da<br />
üssün kaç olduğu yazılır.<br />
Örneğin elektronun yükünü yazmak için e=1,6x 10 -19<br />
e=1,6e-19 yazarsınız<br />
31
Elektrik alan hesaplayan programı inceleyelim<br />
32
Programın sonucunu dosyaya yazdırma.<br />
Programda yaptığımız işlemlerin sonucunu çok büyük olduğunda veya sonuçları<br />
saklamak istediğimizde bunları bir dosyaya yazdırabiliriz.<br />
Bunun için program içerisinde sonuçların kaydedileceği bir dosya tanımlamamız<br />
gerekir.<br />
FILE *output;<br />
Bu şekilde dosyayı tanımladıktan sonra program içerisinde programın<br />
sonuçları kaydedeceği yere bir isim verip bunu açmasını programa iletmemiz<br />
gerekiyor.<br />
Dosyayı açtıktan sonra yapacağımız işlem sonuçları bu dosyaya yazdırmak<br />
olacaktır.<br />
Son olarak ta açtığımız bu dosyayı kapatmamız gerekiyor. Bunun içinde<br />
kullanacağımız komut.<br />
33
Bu yazdıklarımızı bir örnekle açıklayalım.<br />
Program bize sonucun yazdırıldığı alan.txt adında bir dosya verecektir.<br />
34
Potansiyel enerji hesaplayan ve sonucu dosyaya yazdıran bir program yazalım<br />
35
Potansiyel enerji hesaplayan ve sonucu dosyaya yazdıran bir program yazalım<br />
36
Fahrenheit ı Celcius a çeviren bir program yazın.<br />
37