11. Bölüm
Cenker Özkurt
Geçen ay yapısal programlar için gerekli olan akış-kontrol elemanlarının, karar ifadelerini kontrol eden (if), çıkış testi başlangıçta olan (while, for), çıkış testi sonda olan (do), çeşitli olasılıklardan bir tanesini seçmemizi yarayan (switch) ve bu komutların akısını etkileyen (break, continue) komutlarını öğrenmiştik. Bu komutlar C dilinin en önemli komut setini oluşturmaktaydı. Bu nedenle bu komutları ve kullanımlarını çok iyi irdelemelisiniz. Çünkü bu ay C'de ayrı bir önemi olan fonksiyonları ve C'nin program yapısını öğreneceğiz. Bunları öğrendikten sonra da bu komutları bol bol kullanacağız. Şimdi C' nin program yapısını ve fonksiyonların C'de kullanımını öğrenmeye haşlayalım.
C'nin program yapısı fonksiyonları çağırarak oluşturulur. Fonksiyonları çağırmadan önce, varsa fonksiyonun istediği parametreler hazırlanır. C'de her işlem fonksiyonlara bölünerek programlanmaktadır. Bildiğiniz gibi, bütün C programlarımızda eksik etmediğimiz main() fonksiyonu da, bu fonksiyonların en başını oluşturmaktadır. Programımız çalışır çalışmaz, main() fonksiyonuna atlar ve diğer alt fonksiyonlara sıçrar. Böylece fonksiyonlardan oluşmuş, yani, yapısal bir program çalışmaya başlar. Önce main(), sonra temel fonksiyonlar, daha sonra fonksiyonların alt fonksiyonları ve diğerleri; işte C'nin genel program yapısı. Gördünüz mü? Ne kadar kolay!..
C'de herhangi bir fonksiyon kendi kendini çağırabilir (recursion). Aşağıdaki örneğin kullanımına dikkat edin.
printd(n)
int n;
{
int i;
if(n<0) {
putchar('-');
n=-n;
}
if((i=n/10)!=0)
printd(i);
putchar(n%10+'0');
}
Fonksiyonlar çağırıldıktan sonra ilk olarak, varsa, kendi parametrelerini alırlar ve içlerinde kullanacakları değişkenleri tanımlarlar.
main()
{
int i;
float f;
char c;
i=55;
f=52.34;
c='a';
printi(i);
printfl(f);
printc(c);
}
printi(a)
int a;
{
printf("%d\n",a);
}
printfl(a)
float a;
{
printf("%f\n",a);
}
printc(a)
char a;
{
printf("%c\n",a);
}
Yukarıdaki örnekten de anlaşılaşacağı gibi her farklı tipteki değişkenler için ayrı bir print altprogramı yazılmıştır. Burada dikkatinizi çektiyse, bu altprogramların parametreleri farklı tiplerdedir. Fonksiyonlarda parametreleri tanımlarken verilen parametrenin aynı tipini tanımlamak gerekir. Aksi taktirde programınız hatalı çalışacaktır.
Fonksiyonun içinde tanımlanan değişkenler otomatiktir, yani fonksiyon işlemini bitirdikten sonra otomatikman bellekten silinir. Burada değişkenler bir fonksiyona aittir. Fakat değişkenler, bütün fonksiyonların kullanabileceği şekilde (global) tanımlanabilir. Global değişkenleri kullanırken dikkatli olmak gerekir. Genel olarak global değişkenler bir döngünün kontrol değişkeni olarak kullanılmaz. Çünkü bu döngünün içinde kullanılan bir fonksiyon varsa ve bu fonksiyon da aynı değişkeni kullanıyorsa; bu, programın çalışmasını kontrolden çıkarabilir. Bunun dışında global değişkenler parametre sayısını azaltır. Bu, bazen oldukça yararlı olmaktadır.
Şimdi her bir fonksiyonun kullanım formuna bir göz atalım.
isim(değişken listesi)
değişken tanımlamaları
{
tanımlamalar ve cümleler,
}
Fikir vermek amacıyla, fonksiyonu minimal fonksiyon olarak kullanabiliriz. Yani burada sahte bir fonksiyon mevcuttur.
dummy() {}
Fonksiyonun işlevi bittikten sonra, fonksiyonda olup bitenleri bildirmek amacıyla bir dönüş kodu yollanabilir. Bunu yapmamızı sağlayan komut;
return(sayı)komutudur. Return komutu fonksiyonlarda sık sık kullanılır. Ayrıca return komutunun yalnızca fonksiyonun sonunda olmasına gerek yoktur. Fonksiyonun herhangi bir bölümünden bir dönüş koduyla ya da kodsuz çıkılabilir.
main()
{
int i;
for(i=0;i<=10;i++)
printf("2^%d=% d\n",i,power(2,i));
}
power(j,k)
intj,k;
{
int r,m;
r=1;
for(m=1;m<=k;m++)
r*=j;
return(r);
}
Yukarıdaki örnekte 2 sayısının 0-10 arası üst kuvvetini alan bir fonksiyonun kullanımını görüyorsunuz. 2 sayısının istenilen değerdeki üst kuvveti alındıktan sonra, return komutuyla fonksiyondan bir kodla dönülür. Burada dikkat edilmesi gereken, fonksiyonun istediği parametreleri doğru vermektir. Aksi takdirde istenmeyen bir kodla karşılaşabiliriz.
Örnekte de olduğu gibi fonksiyon içerisinde parametreler bulunabilir. Fonksiyonu çağırırken kullandığımız değişkenlerle, fonksiyon içerisindeki değişkenler arasında adres olarak bir bağ yoktur. Yani bu parametrelerin yalnızca değeri alınarak işleme sokulur, adresler eşitlenmez.
Şimdi de fonksiyonların birbirleri ile iletişiminde kullanılan extern sıfatını öğrenelim.
Extern değişkenler, global değişkenlerdir. Herhangi bir değişkenin başına konularak, o değişkenin global olduğu belirtilir.
int p;
main()
{
int k,l;
...
}
func()
{
extern int p;
...
}
Yukarıdaki örnekten de anlaşılaşacağı gibi, p global bir değişken olarak tanımlanmıştır. Fakat burada farklı bir kullanım mevcuttur. func() fonksiyonunun içerisinde p değişkeni extern olarak belirtilmiştir. Yani burada func() fonksiyonu aynı source listesi içinde olmasa bile, p değişkenini global olarak diğer source programına bağlayacaktır. Aslında buradaki kullanım modüler bir çalışmada daha çok kullanılır. Modüler programlarda önce ana program compile edildikten sonra obje haline yani linke hazır hale getirilir. Daha sonra diğer program parçasıyla link edilirken, birinci program modülünün global değişkenlerini kullanmak isteyen ikinci program modülü, kullanacağı bu global değişkenleri extern olarak tanımlar. Yani artık ikinci program modülü de birinci program modülünün global değişkenlerini kullanabilecektir.
C dilinde Fonksiyonlar da external olarak tanımlanmaktadır. Bu nedenle C dili, bir fonsiyon içerisinde yeni bir fonksiyonun tanımlanmasına izin vermez.
C dilinde kullanılan diğer bir değişken tipi de, register (yazmaç) değişkenlerdir. Bu değişkenler, herhangi bir bellek kullanmaz. Yani direkt olarak makinenizin mikroişlemcisinin registerlerini kullanır. Bu da programınızın kod kısmının kısalmasını ve hızlanmasını sağlar. Register değişkenler, global olarak kullanılmazlar. Bir fonksiyon içerisinde birkaç tane değişken register olarak yakalanabilir. Aslında bu mikroişlemcinin kullandığı register sayısına bağlıdır. Register değişkenler int, char ya da pointer olarak tanımlanırlar.
f(c,n)
register char c;
{
register int i;
...
}
C dilinde global diziler tanımlanırken, başlangıçta herhangi bir eleman atılabilir (initializing).
int nwhite=0;
int nother=0;
int ndigit[10]={0,0,0,0,0,0,0,0,0,0};
main()
{
...
}
Buradaki örnekte ndigit[] dizisi tanımlanırken bütün elemanları otomatik olarak sıfırlanmaktadır. Dikkatinizi çektiyse bunu yaparken süslü parantezler kullandık. Çünkü burada bir sıfırlama fonksiyonu mevcut.
Şimdi son olarak C dilindeki önişlem komutlarını görelim.
Bunların başlıcaları #include ve #define komutlarıdır.Bu komutlar aslında programımızı daha çok compiler'a hazırlar.
#include "isim"Bu komut "isim" adındaki C listesine (source) geçmemizi gösterir. Compiler programı ilk olarak bu source listesini compile etmeye başlar. Bu kısımda daha çok tanımlamalar ve fonksiyonlar mevcuttur.
#define YES 1#define komutu, C dilinde sabit değişkenleri tanımlamada kullanılır. Buradaki YES sabit değişkeni, program içerisinde herhangi bir fonksiyonda kullanılabilir. Fakat burada değişkenin sabit olduğunu unutmamak gerekir.
#define komutu ile sabit değişkenlere farklı tanımlamalar da yapabiliriz;
#define then
#define begin {
#define end };
if(i>0) then
begin
a=1;
b=2;
end
ya da,
#define square(x) x*xşeklinde kullanılabilir. Burada square(5) yazdığımızda, 5 sayısı işleme sokulur. Yani x*x, 5*5 gibi değerlendirilir. #define komutu ile buna benzer birçok fonsiyon da tanımlanabilir.
Daha önce yaptığımız print alt programını şimdi #define komutundan yararlanarak yapalım.
#define printi(x) printf("%c\n",x);
#define printfl(x) printf("%f\n",x);
#define printc(x) printf("%c\n",x);
main()
{
int i;
float f;
char c;
printi(i);
printfl(f);
printc(c);
}
Örnekten de anlaşılacağı gibi, #define ile tüm print fonksiyonlarını belirtebiliriz.
Bu ayki konularımız biraz önemli. Çünkü fonksiyonlar, C dilinin temelini oluşturmaktadır. Fakat bu konuları da atlattıktan sonra, artık siz de daha rahat C diline hakim olabilirsiniz.
Şimdilik bu kadar. Gelecek ay Dizi ve İşaret (array, pointer) değişkenlerini öğreneceğiz. Hoşçakalın.