- Kontrol Akışı
Şimdiye kadar yazdığımız programların hepsinde verilen komutlar yazılan sırada, hiçbir koşul kontrolü olmadan çalışmaktaydı. Fakat programlama dillerindeki genel yapı böyle değildir. Programlama dillerinin esas gücü verilen parametrelere bağlı olarak farklı işler yapabilmesidir. Yüksek seviyeli programlama dillerinde bu, kontrol akışını sağlayan komutlar tarafından sağlanır. Temel olarak 2 türlü kontrol akışı vardır:
Şarta bağlı dallanma Döngü
Şarta bağlı dallanma bir ifadenin sonucuna göre istenilen kodun çalıştırılıp çalıştırılmayacağına karar verilmesidir. Döngü ise belli bir şart sağlanana kadar aynı kod parçasının çalıştırılmasına denir.
3.1 TRUE ve FALSE
C’de kontrol akışında, TRUE ve FALSE değerleri en çok karşılaşacağımız değerlerdir. Bir şart ifadesinin sonucu TRUE veya FALSE olur. C’de 0’dan farklı bütün sayılar TRUE’yu ifade ederken, 0 sayısı FALSE değerini ifade eder. Bu özellik birçok yerde hızlı ve pratik kod yazmayı sağlar.
3.2 Conditional Branching - Şarta Bağlı Dallanma
Şartlara bağlı dallanma bir programlama dilinin en basit kontrol mekanizmasıdır. Programın karar vermesini ve verilen karara göre işlem yapıp yapmamasını veya farklı işlemler yapmasını sağlar. Kontrol edilen ifade, programın her çalıştırılmasında farklı değerler alabiliyorsa bu programcıya farklı veriye göre dinamik program yazma şansı verir. C’de şarta bağlı dallanma için if ve else keywordleri kullanılır.
if’in kullanımı oldukça kolaydır. if ile verilen ifade doğru (true) ise peşinden gelen satırlar, true değil yani false ise else kolundakiler çalıştırılır veya if bloğu terkedilir. if’in temel yazım şekli:
if (ifade)
kod1
kod2;
Kontrol edilecek ifade her zaman parantez içinde verilmelidir. Aşağıdaki örnekte verilen ifade doğru ise kod1, değil ise kod2 çalıştırılır. Dikkat edilmesi gereken şey ifade doğru olsa da olmasa da kod3’ün çalıştırılacağıdır. else keyword’ünü de kullanan bir örnek verirsek;
if (ifade)
kod1
else
kod2;
kod3;
Pascal’da olduğu gibi if satırında then kelimesinin yeralmamasına dikkat edilmelidir. if’in en çok kullanıldığı yer verinin doğruluğunun onaylanma aşamasıdır. Örneğin: Verilen bir sayının karekökünü alan bir program yazacağımızda, kullanıcının girdiği sayının negatif olup olmadığını kontrol etmemiz gerekir;
#include
#include
void main(void)
{
double num;
printf(“Pozitifi bir tamsayi giriniz:\n”);
scanf(“%lf”, &num);
if (num<0)
printf(“Giris hatasi: Girilen sayi negatif!”);
else
printf(“Girilen sayinin karekoku: %d”,sqrt(num));
}
Burada dikkat edilmesi gereken şey else kısmıdır. else satırını yazmamış olsaydık, program sayının negatif olduğu mesajını vermesine rağmen, sayının karekökünü almaya çalışacak ve sonuçta bir hata verecekti.
if ve else satırlarının printf’lerden ayrı olması sadece okuma kolaylığı içindir. Bu satırlar aynı hizada ve birarada da yazılabilirler:
#include
#include
void main(void)
{
double num;
printf(“Pozitifi bir tamsayi giriniz:\n”);
scanf(“%lf”, &num);
if (num<0) printf(“Giris hatasi: Girilen sayi negatif!”);
else printf(“Girilen sayinin karekoku: %d”,sqrt(num));
}
Bu kod da doğru olmasına rağmen anlaşılabilirlik açısından birincisine göre zayıftır.
3.1.1 Karşılaştırma Operatörleri
Genelde if ile birlikte verilen ifade, iki değerin karşılaştırmasıdır. Bu ifadede kullanılabilecek 6 ayrı karşılaştırma operatörü vardır:
< Küçüktür
> Büyüktür
<= Küçük eşit
=> Büyük eşit
== Eşit
!= Eşit değil
Bu operatörler arasında en çok dikkat edilmesi gereken ‘eşit’ operatörüdür. Eşit operatörü Pascal’daki gibi tek = ile değil iki tane = işareti ile yazılır. Bu operatörün kullanımında karşılaşılan en büyük hata tek = işareti kullanılmasıdır. Bunun sonucu if ile verilen ifadenin her zaman doğru kabul edilmesidir. Örnek üzerinde açıklayacak olursak;
if (j == 5)
kod1;
kod parçacığı j’nin 5’e eşit olup olmadığını kontrol eder ve eşit ise kod1’i çalıştırır. Bunun yerine;
if (j = 5)
kod1;
yazacak olursak hiçbir hata ile karşılaşmadan programımız çalışır. Ama burada işler biraz farklı yürür. (j = 5) ifadesi bir kontrol ifadesi değil bir atama ifadesidir. Ve bu atama da sonuç olarak 5 değerini döndürür. Bu değer de her zaman TRUE yani doğru olarak kabul edileceğinden kod1 her zaman çalışır.
Az önce söylendiği gibi 0 hariç bütün sayılar TRUE’ya denk düştüğünden kontrol ifadesi olarak bir tamsayı da kullanılabilir. Örneğin;
int i;
……
if (i) kod1;
else kod2;
kod parçacığı da kesinlikle doğrudur. Burada i 0 dan farklı ise kod1, 0 ise kod2 çalıştırılacaktır. Örnek olarak bir karakterin alfabetik bir karakter olup olmadığını söyleyen bir program yazmak istenirse;
#include
#include /* isalpha() için */
void main(void)
{
char ch;
printf(“Bir karakter giriniz:\n”);
scanf(“%c”, ch);
if (isalpha(ch))
printf(“%c alfabetik bir karakterdir!\n”, ch);
else
printf(“%c alfabetik bir karakter değildir!\n”, ch);
}
Bu programdaki
if (isalpha(ch))
ile
if (isalpha(ch) != 0)
ifadesi aynı işi yapmaktadır. C’de bu şekilde fonksiyonun dönüş değerini kontrol ifadesi olarak kullanım genel bir kullanım şeklidir. Tamsayı döndüren fonksiyonlar genelde, hata oluşması durumunda 0, aksi takdirde farklı bir değer döndürürler.
3.1.2 Bileşik ifadeler
if ile kontrol sonrasında her iki durumda da birer satırlık kod parçacıkları çalışacak diye bir kural yoktur. Birden fazla ifade de kullanılabilir ve bu kullanılan ifade bütününe bileşik ifade (compound statement) adı verilir. Bu kod bloğu { } işaretleri ile sınırlanır. Örneğin;
if (num < 0)
printf(“Giris hatasi: Girilen sayi negatif!”);
else
{
printf(“%f in karesi: %f\n”, num, num*num);
printf(“%f in kupu: %f\n”, num, num*num*num);
}
Bileşik ifadeler yani { } kullanılırken dikkat edilmesi gereken şeyler her açılan { için mutlaka kapanan bir } olması ve bunun yanında bu } işaretinin doğru yerde olması gerekmektedir. Bu işaretlerin kullanılmaması de sorun yaratır. Bir önceki örnekte bu işaretleri kullanmadığımızı varsayalım;
if (num < 0)
printf(“Giris hatasi: Girilen sayi negatif!”);
else
printf(“%f in karesi: %f\n”, num, num*num);
printf(“%f in kupu: %f\n”, num, num*num*num);
Şu durumda programın yapacağı sayı negatifse hata vermek ve sayının küpünü yazmak, sayı poizitifse sayının karesini ve küpünü yazdırmak olur.
3.1.3 İçiçe if ifadeleri
Birbirinin içinde if ifadeleri kullanmak da mümkündür. İçiçe if’lerin kullanım sebebi, programcının birden fazla duruma hakim olabilmesini sağlamaktır. İlk kontrolden sonra ikinci bir kontrol yapmak, hatta ikinciden sonra üçüncü bir kontrol yapmak gerekebilir. Verilen 3 sayıdan en küçüğünü bulan bir ifade yazmak istersek;
if (a
Görüldüğü gibi içerde olan if, bir dıştaki if’in hem TRUE hem de FALSE kolunda olabilir.
3.2 switch komutu
Yazılan programda, bir ifade çok fazla değer döndürebiliyorsa if-else dallanmaları fazla karmaşık bir hal alabilir. Bu durumda switch devreye girer. switch kullanılarak, bir ifadenin alabileceği değerlere göre çalışacak kod parçaları kolaylıkla gösterilebilir:
switch (ifade)
{
case sabit_değer1: kod1;
case sabit_değer2: kod2;
……
default: kodd;
}
switch ile verilen ifade char, short, int veya long olmalıdır - float veya double olamaz. Aynı şekilde case ile verilen her deger de sabit olmalı, yani herhangi bir değişken vs. içermemelidir. switch’in çalışması ise şöyledir:
switch ile verilen ifade değerlendirilir ve { içine girdikten sonra ifade sonucu çıkan değere uygun olan kod parçacıkları çalıştırılır. Burada dikkat edilmesi gereken bir nokta şudur: ifade’nin döndürdüğü değere uyan birden fazla case varsa, program switch bloğunun sonuna kadar bunların hepsini çalıştırır. switch’ten çıkabilmek için break, goto veya return komutlarından biri kullanılmalıdır. Bunlardan en çok kullanılanı break’tir. break switch bloğundan çıkarak, kodun kontrolünü switch bloğundan hemen sonraki kod parçacığına geçirir. default kısmının görevi ise, o kısma kadar ifadenin değerine denk düşen bir case ifadesi ile karşılaşılmadığı takdirde çalıştırılacak kod parçacığını belirtmesidir.
print_error adında hata kodunu yazan örnek bir fonksiyon yazarsak;
#include
#define ERR_INPUT_VAL 1
#define ERR_OPERAND 2
#define ERR_OPERATOR 3
#define ERR_TYPE 4
void print_error(error_code)
int error_code;
{
switch (error_code)
{
case ERR_INPUT_VAL:
printf(“Hata: Gecersiz giris degeri.\n”);
break;
case ERR_OPERAND:
printf(“Hata: Gecersiz operand.\n”);
break;
case ERR_OPERATOR:
printf(“Hata: Gecersiz islem.\n”);
break;
case ERR_TYPE:
printf(“Hata: Uyumsuz veri tipi.\n”);
break;
default: printf(“Hata: Bilinmeyen hata.\n”);
break;
}
}
Burada her değer için ayrı bir işlem gerçekleştirilmekte. Birden fazla değer için aynı işlemi gerçekleştirmek istediğimizde, switch’in çıkış komutlarından biri olan break, return veya goto’yu görene kadar ilerleme özelliğinden faydalanırız. Örneğin;
char arg;
switch (arg)
{
case ‘.’:
case ’,’:
case ‘:’:
case ’;’:
case ‘!’: return 1;
default: return 0;
}
Burada fonksiyon ., ,, :, ; ve ! karakterleri için 1 döndürürken, diğer bütün karakterler için 0 döndürür. switch’in pratik kullanımına bir örnek verecek olursak: iki operand ve işlem tipini alan bir fonksiyon düşünün:
double evaluate(op1, operator, op2)
double op1, op2;
char operator;
{
switch (operator)
{
case ‘+’: return op1 + op2;
case ‘-’: return op1 - op2;
case ‘*’: return op1 * op2;
case ‘/’: return op1 / op2;
default: ‘Yanlis operator’;
exit(1);
}
}
3.3 Döngüler
Döngüler (diğer adı ile iterasyonlar), belirli bir koşul sağlanana kadar, aynı işlem kümesini peşpeşe gerçekleştirmeyi sağlayan komutlardır. C’de 3 adet döngü komutu vardır:
while
do ... while
for
3.3.1 while döngüsü
while’ın yazım ve çalışma tarzı aşağıdaki gibidir:
while (ifade)
kod1;
Compiler while ile karşılaştığında önce ifadeyi, yani döngünün çalışması için TRUE olması gereken ifadeyi işler. Bu ifade TRUE döndürüyorsa kod1 çalıştırılır. Daha sonra kontrol tekrar while satırına döner ve ifade tekrar kontrol edilir. Bu ifadenin sonucu FALSE olana kadar bu adımlar tekrar tekrar gerçekleşir. İfadenin FALSE döndürdüğü noktada, kontrol kod1’den hemen sonraki satıra geçer. Burada kullanılan kod1 parçacğı genelde bileşik bir ifadedir.
Çok basit bir while örneği yazmak istenirse;
while (x < y)
x++;
Bileşik bir ifade kullanan bir while döngüsü örneği verilirse;
int ch, num_of_spaces = 0;
printf(“Bir cumle giriniz:\n”);
ch = getchar();
while (ch != ‘\n’)
{
if (ch == ‘ ‘) then num_of_spaces++;
ch = getchar();
}
printf(“Bosluk karakterlerinin sayisi: %d”, num_of_spaces);
Burada dikkat edilmesi gereken birkaç nokta vardır:
scanf kullanmayıp yerine getch kullanma sebebimiz, scanf’in aldığı değerin tipini belirtmesi, getch’nin ise girilen her değeri char olarak kabul etmesidir. while döngüsüne girmeden getch() işlemini yapmamız gerektiğine dikkat edilmelidir. Bunun sebebi, while’a ilk geldiğimizde ifade’nin kontrolü için ch’ye değer atanması gerekliliğidir. while işlemi \n, yani Enter veya yeniSatır anlamına gelen bir karakter girilene kadar devam eder.
3.3.2 do ... while döngüsü
while’ın karakteristik özelliklerinden biri, kontrolün döngüden öcne yapılmasıdır. Bunun anlamı şudur: İfade FALSE değer döndürürse, döngü içindeki kod parçası hiç çalıştırılmayacaktır. Fakat bazı durumlarda, kod parçasının en az bir kez çalışması gerekebilir. Bu durumda do ... while döngüsünü kullanabiliriz. Yazımı;
do
kod1;
while (ifade);
Bir önceki programı do ... while ile yazarsak;
int ch, num_of_spaces = 0;
printf(“Bir cumel giriniz:\n”);
do
{
ch = getchar();
if (ch == ‘ ‘) num_of_spaces++;
} while (ch != ‘\n’);
printf(“Bosluk karakterlerinin sayisi: %d”, num_of_spaces);
3.3.3 for döngüsü
for çok kullanılan bir döngü yapısı için kullanılır. for’a girmeden önce bir veya daha fazla değişkene ilk değer atanması ve döngü içinde de bazı değişkenlerin değerlerinin değiştirilmesi gerekir. Yeni değer alan bu fonksiyonlar, döngüden çıkış koşullarından kullanılır. for yazım şekli;
for (ifade11, ifade12, ...; ifade 21, ifade 22, ...; ifade31, ifade32, ...)
kod1;
Burada üç grup ifadenin farklı anlamları vardır:
ifade1 grubu: Döngüye girmeden önce istenilen değişkenlere iilk değer atandığı ifade kısmıdır. Bir kez çalışır ifade2 grubu: Burası döngünün koşul kısmıdır. Buradaki ifade TRUE olduğu sürece kod1 kod parçası çalışır. Her döngüde çalışır. ifade3 grubu: kod1 kod parçası çalıştıktan sonra ifade3 kısmındaki değişim gerçekleşir ve program kontrol için tekrar ifade2’ye döner. Her döngüde çalışır.
for döngüsünün mantığını anlamanın en iyi yolu while ile karşılaştırmaktır:
for (ifade1; ifade2; ifade3)
kod1;
parçasını while ile yazarsak:
ifade1;
while (ifade2)
{
kod1;
ifade3;
}
Diğer döngü komutları ile karşılaştırdığınızda for en çok kullanılanıdır. Kullanımını biraz daha kolay anlaşılır hale getirebilmek için for döngüsü için en çok kullanılan örneği yazarsak;
long int factorial(val)
int val;
{
int j, fact = 1;
for (j=2; j <= val; j++)
fact = fact*j;
return fact;
}
Bu fonksiyon anlaşılacağı gibi, verilen bir sayının faktöriyelini alır. Aşağıdaki fonksiyon, kullanıcının girdiği basamakları, tamsayıya çevirir:
int make_int()
{
int num=0, digit;
digit = getchar();
for (; isdigit(digit); digit=getchar())
{
num *= 10;
num += (digit - ‘0’);
}
}
digit - ‘0’ ifadesi, karakter olarak girilen dijiti, sayı değerine çevirir. Bu ifade genel kodlama tiplerinde geçerlidir. Örneğin 0-45 ev 5-53 ise
53 - 48 = 5
değerini verir.
Son örnekte de görülebileceği gibi, for döngüsünde verilen üç ifadeden istenileni boş bırakma olanağımız vardır. Fakat ifade boş bırakılsa bile ;’ler mutlaka konulmalıdır. Genelde bu kullanım tarzı ifade1 ve ifade3’te kullanılır. ifade2 ise büyük ölçüde boş bırakılmaz çünkü döngüden çıkmak için gereken koulun ifadesidir. Her üç ifadenin de boş bırakıldığı yerler, yazılan kodun sonsuz döngüde yani sürekli çalışma isteminin olduğu yerlerdir.
for (;;;)
Bu ifade ilk değer atama, koşul ve işlem ifadelerini içermez ve sonsuz döngü anlamındadır.
for’teki ifadeler boş geçilebileceği gibi, döngü içinde çalışması gereken satırlar da boş geçilebilir. Bu işlem, döngü içinde yapılacak işlerin, for’teki ifadelerde belirtilebildiği yerlerde olur. Örneğin;
for (c=getchar(); isspace(c); c=getchar()) ;
kod parçacığı, kullanıcı boşluktan başka bir karakter girene kadar kullanıcıdan karakter girişi alır. Burada döngü içinde kod yazmaya gerek yoktur. Bu yüzden for’un bittiğini göstermek için ;’ü kullanmak yeterlidir. Genel kullanım alışkanlığı olarak bu ; bir sonraki satıra yazılarak for’un yaptığı işlemni anlaşılması kolaylaştırılır.
3.4 İçiçe döngüler
Karşılaştırma ifadelerini içiçe kullanabileceğimiz gibi döngüleri de birbirlerinin içinde kullanabiliriz. Döngüler tasarlanırken dikkat edilecek şey, dıştaki döngünün ilerleyebilmesi için içteki döngünün bitmesi gerekmektedir. Örneğin;
int j, k;
printf(“ 1 2 3 4 5 6 7 8 9 10\n”);
printf(“--------------------------------------------\n”);
for (j=1; j<=10; j++)
{
printf(“%5d|”, j);
for (k=1;k<=10;k++)
printf(“%5%d”, j*k);
printf(“\n”);
}
Bu kod, 1’den 10’a kadar sayıların çarpım tablosunu verir. Programın yaptığı şey: Önce her j değeri için j değerini ekrana yazar. Ekrana yazacağı her sayı için 5 karakterlik yer ayırır. (%5d) Eğer sayı 5 basamaktan küçükse, boşluk yazar. j’nin değerini ekrana yazdırdıktan sonra, her j değeri için, j’yi k ile, yani 1’den 10’a kadar değerlerle çarpar ve ekrana yazdırır. Yeni satıra geçer.
3.5 break ve continue ifadeleri
break ifadesi, switch ile beraber kullanıldığında kontrolün kalınan noktadan ilerleyip, diğer kontrolleri yapmasını engelliyordu. Başka bir açıdan baktığımızda ise, break switch’ten çıkıp kontrolü bir sonraki ifadeye aktarmanın illegal bir yolu olarak görünüyor. break diğer döngü ifadeleriyle kullanıldığında da aynı işlevi görür. 50 karakterlik veya bir satırlık bir giriş beklendiğini varsayarsak;
for (cnt=0; cnt<50; cnt++)
{
c=getchar();
if (c==’\n’)
break;
else
…
}
Burada alınan yeni karakter yeni satır karakteri (\n) ise, kontrol for döngüsünden çıkıp bir sonraki ifadeye geçer. break ifadesinin program içinde çok kullanımı kodu anlamayı zorlaştırabilir. Ayrıca break, kontrolü program içinde tamamen farklı bir yere verdiğinden kullanımında dikkatli olunmalıdır.
continue ifadesi, gerkenden önce döngünün başına dönmemizi sağlar. Bu ifade daha çok es geçilecek durumlarla karşılaşıldığında kullanılır. Örneğin;
while ((digit=getchar()) != ‘\n’)
{
if (isdigit(digit) == 0)
continue;
num = num*10;
num = num + (digit - ‘0’);
}
Yukarıdaki kod parçasında kullanıcı sayısal biri karakter girmediğinde (isdigit ile kontrol edilir), bu değer dikkate alınmaz ve döngünün başına dönülür. Kullanıcı yeni satır karakteri girene kadar döngü devam eder.
3.6 Aritmetik operatörler
C’de operatörler size hesap kabiliyeti veren işlemlerdir. C’deki operatörlerin çeşitliliği C’yi diğer dillerden ayıran özelliklerden biridir.
3.6.1 - operatörü
Beraber kullanıldığı tamsayı veya ondalık sayının negatif değerini döndürür. - operatörü, çıkarma operatörü ile karıştırılmamalıdır.
j = 3 - -x;
ifadesi aslında
j = 3 - (-x);
anlamınadır.
3.6.2 Aritmetik işlemler
+, -, *, / işlemleri bilinen işlemlerdir. Farklı tiplerde verilerin bu operatörleri kullanarak yaptıkları işlemlerde, veri tipleri arasında yapılan işlemlerde kullanılan kurallar geçerlidir.
C’de kullanılan ekstra bir aritmetik operatör % - mod alma operatörüdür. % operatörü ik operand alır.
num % baz;
Bunlardan num modunu almak istediğimiz sayıyı, baz ise mod aldığımız tabanı gösterir. Örneğin;
9 % 5 = 4
3.6.3 Aritmetik atama operatörleri
Atama operatörleri şimdiye kadar yazdığımız örnek kodların hepsinde yer alıyordu:
= Atama op. a=b
+= Ekleme op. a+=b
-= Çıkarma op. a-=b
*= Çarpma op. a*=b
/= Bölme op. a/=b
%= Mod alma op. a%=b
Burada kullanım açısında atama op. olan =’in farklı bir kullanımı vardır. Atama operatörü ile birden fazla değişkene aynı anda değer atamak mümkündür:
a = b = c = d = 1;
satırı a, b, c, d değişkenlerine aynı anda 1 değerini atar. Aslında burada da işlem sağdan sola doğru işler. Yani önce d ye 1 atanır, sonra c ye d nin değeri atanır, vs. Bu yüzden farklı veri tiplerine sahip değişkenlere atma yaparken işlem sırası gözönüne alınmalıdır:
int j;
float f;
j = f = 3.5;
satırları önce f’ye 3.5 sonra da j’ye 3 değerini atar. Fakat;
int j;
float f;
f = j = 3.5;
satırları önce j’ye 3, sonra da bu değeri kullanarak f’ye 3.0 atar.
3.6.4 Arttırma ve azaltma operatörleri
Önceki kod parçalarında da kullandığımız ++ ve - operatörleri değişkenin değerini 1 artırıp, 1 azaltmak için kullanılır. Bu operatörlerin iki kullanımı vardır. Birincisi değişkenden sonra (postfix) kullanmak, diğeri de değişkenden önce (prefix) kullanmak.
a++ veya ++a
Bu kullanım şekilleri yalnız kullanıldığında aynı işi görmelerine rağmen, bir ifade içinde kullanıldıklarında farklı davranırlar. Eğer postfix ifade başkla bir ifadenin içinde kullanılırsa yapılan şey, ifadede değişkenin mevcut değerini kullanmak daha sonra değişkenin değerini artırmaktır. Eğer aynı ifadede prefix kullanılsaydı, önce değişkenin içeriği artırılır, daha sonra ifadede yeni değer kullanılırdı. Aynı şey - operatörü için de geçerlidir.
ResetAt.Com |