İçeriğe geç →

C Programlama Dersi – 4

Selamlar,

Bugünkü konularım:

  • Veri tipi dönüşümü (type casting),
  • Operatörler,
  • Koşul ifadeleri (ifelseswitch)

Veri Tipi Dönüşümü

C dilinde veri tipi dönüşümleri otomatik oalrak ya da programcının isteğine göre gerçekleşebiliyor.

#include <stdlib.h>
#include <stdio.h>
int main(void)  
{
    double doubleSayi1 = 3, doubleSayi2 = 6, doubleSayi3;
    int intSayi;
    char karakter;
    
    intSayi = doubleSayi1 / doubleSayi2;
    karakter = intSayi + 41;
    
    printf("doubleSayi1: %lf doubleSayi2: %lfn", doubleSayi1, doubleSayi2);
    printf("doubleSayi1 / doubleSayi2: %dn", intSayi);
    printf("Karakter: %c", karakter);
}

Çıktı:

doubleSayi1: 3.000000 doubleSayi2: 6.000000
doubleSayi1 / doubleSayi2: 0
Karakter: )

8. satırda double veri tipindeki değişkenleri tanımlarken tam sayı değerler atamıştım ama otomatik olarak double veri tipine çevrilip yazıldılar.

9. satırda 3.0’ı 6.0’a bölüp sonucu int veri tipindeki intSayi değişkenine atıyorum. Sonuç=0.5 olmasına rağmen ekrana 0 yazdırıldı. Elde ettiğimiz sonuç int veri tipine dönüştüğü için ondalık kısımlar saklanmıyor.

10. satırda ise intSayi(0)+41 değeri char veri tipindeki karakter değişkenine atılıyor. Ekrana ise “)” yazdırıldığını görüyorum. Çünkü ASCII tablosundaki 41. karakter “)”.

Veri tiplerini istediğimiz veri tipine dönüştürmek istersek yapmamız gereken çok basit: (yeni veri tipi) değişken

#include <stdlib.h>
#include <stdio.h>
int main(void)  
{
    int sayi1 = 3, sayi2 = 6, sayi3;
    float sonuc = sayi1 / sayi2;
    char karakter = ')';
    
    printf("tip dönüşümü öncesin sayi1 / sayi2: %fn", sonuc);
    printf("karakter: %cn", karakter);
    
    sonuc = (float)sayi1 / (float)sayi2;
    sayi3 = (int)karakter;
    
    printf("tip dönüşümü sonrasın sayi1 / sayi2: %fn", sonuc);
    printf("karakter: %d", sayi3);
    
}

Çıktı:

tip dönüşümü öncesi
 sayi1 / sayi2: 0.000000
karakter: )
tip dönüşümü sonrası
 sayi1 / sayi2: 0.500000
karakter: 41

int veri tipini int veri tipindeki bir değişkenle işleme sokunca sonuç yine int veri tipinde dönüyor. Eğer int veri tipindeki bir değişkeni float veri tipindeki bir değişkenle işleme sokarsam sonuç ne olur?

Bu durumda veri tipi dönüşümü daha yüksek değerdeki veri tipine göre otomatik olarak dönüştürülür. Yani 3 / 6.0 işleminin sonucu 0.5  olacaktır. Eğer sonucu daha düşük değerdeki veri tipinde yazdırırsak, veri kaybı yaşayabiliriz.

printf("float veri tipi: %fn", 3 / 6.0); //0.500000 
printf("int veri tipi: %dn", 3 / 6.0); //0

(DÜŞÜK) char <-> int <-> long <-> float <-> double (YÜKSEK) *

Operatörler

C içinde birçok operatörümüz var. Arttırma, azaltma, atama, karşılaştırma, mantıksal operatörler.

Arttırma – Azaltma – Atama

Operatör Açıklama Örnek Anlamı
= atama x = 7; x = 7;
+= ekleyerek atama x += 3 x = x + 3
-= eksilterek atama x -= 5 x = x - 5
*= çarparak atama x *= 4 x = x * 4
/= bölerek atama x /= 2 x = x / 2
%= bölüp, kalanını atama x %= 9 x = x % 9
++ bir arttırma x++ veya ++x x = x + 1
-- bir azaltma x-- veya --x x = x - 1

Operatörlerimiz , açıklamaları ve örnek kullanımları tablodaki gibi. Daha iyi anlamak için biraz kod yazmak gerek.

int x = 7;
    printf("başlangıçtaki x: %dn", x);
    
    x += 3; // x = x + 3;
    printf("x += 3; için %dn", x);
    
    x = x - 3; // x -= 3;
    printf("x = x-3; için %dn", x);
    
    x -= 3; // x = x - 3;
    printf("x -= 3; için %dn", x);
    
    x *= 3; // x = x * 3;
    printf("x *= 3; için %dn", x);
    
    x++; // x = x + 1;
    printf("x ++; için %dn", x);
    
    x--; // x = x - 1;
    printf("x --; için %dn", x);

Çıktı:

başlangıçtaki x: 7
x += 3; için 10
x = x-3; için 7
x -= 3; için 4
x *= 3; için 12
x ++; için 13
x --; için 12

Arttırma ve azaltma operatörlerinde bir noktaya dikkat çekmek istiyorum.

x++ ve ++x ya da x-- ve --x, x değerini 1 arttırıp ya da 1 azaltır. ++ ve -- operatörlerinin değişkenden önce ya da sonra yazılması işlemin de önce ya da sonra yapılması gerektiğini sağlar. Şöyle ki:

int x = 7, y=1;
    
    printf("x değeri: %dn", x);
    puts("-------------");
    printf("x++ sırasında: %dn", x++);
    printf("x++ sonrası: %dn", x);
    
    printf("++x sırasında: %dn", ++x);
    printf("++x sonrasında: %dn", x);
    
    puts("-------------");
    
    printf("y değeri: %dn", y);
    puts("-------------");
    printf("y-- sırasında: %dn", y--);
    printf("y-- sonrası: %dn", y);
    
    printf("--y sırasında: %dn", --y);
    printf("--y sonrasında: %dn", y);

Çıktı:

x değeri: 7
-------------
x++ sırasında: 7
x++ sonrası: 8
++x sırasında: 9
++x sonrasında: 9
-------------
y değeri: 1
-------------
y-- sırasında: 1
y-- sonrası: 0
--y sırasında: -1
--y sonrasında: -1

Yukarıda yazarında demek istediği gibi x++ önce işlemi yapar sonra x’in değerini 1 arttırır. ++x ise önce x’in değerini 1 arttırır sonra işlemi yapar.

self five!

Karşılaştırma – Mantıksal

Adlarından anlaşıldğı gibi değerleri karşılaştırmak ya da mantıksal işlemlere sokmak için kullandığımız operatörlerdir bunlar.

Operatör Açıklama Örnek Anlamı
> büyüktür x > y x, y den büyük mü?
< küçüktür x < y x, y den küçük mü?
== eşittir x == y x, y ye eşit mi?
>= büyük-eşittir x >= y x, y den büyük yada eşit mi?
<= küçük-eşittir x <= y x, y den küçük yada eşit mi?
!= eşit değil x != y x, y den farklı mı?
&& mantıksal VE x>2 && x<y x, 2 den büyük VE y den küçük mü?
|| mantıksal VEYA x>2 || x<y x, 2 den büyük VEYA y den küçük mü?
! mantıksal DEGIL !(x>2) x, 2 den büyük değilse

Mantıksal Karşılaştırmalar

p q p&&q p||q !p
0 0 0 0 1
0 1 0 1 1
1 0 0 1 0
1 1 1 1 0

Tamam, operatörleri anladım ama x, y’den büyükse büyük, küçükse küçük. Ne yapalım büyükse ya da küçükse?

Ee yani, boşu boşuna değerleri karşılaştırma ya da mantıksal işlemlere sokmayız. Belirli koşulların sağlanıp sağlanmadığını kontrol etmemiz gerektiğinde bunları uygulayacağız. Bunun için de koşul ifadelerini öğrenmen gerek.

Ama önce operatörlerin işlem önceliğine bakalım.

operetörlerin önceliği

Şu örnek durumu gayet güzel açıklıyor:

int i=5, j=10, k=20;
    i > 5 * k % 3 && !( k- 2 != 18)
        i > 5 * k % 3 && !( 18 != 18)
            i > 5 * k % 3 && !0
                i > 5 * k % 3 && 1
                    i > 100 % 3 && 1
                        i > 1 && 1
                            1 && 1
                                1

 

Koşul İfadeleri

Program akışında bazı kararlar verip, ona göre koşulları düzenlemek gerekebilir. Mesela, kullanıcıdan 1-10 arasında bir sayı girmesini istedik. Kullanıcı değeri girdikten sonra program kaldığı yerden devam edecek ama önce bir bak bakalım, gerçekten 1-10 arası bir sayı girmiş mi? Koşulumuzu kontrol ettikten sonra kararımızı verip programın akışına devam edebiliriz.

İşte bunun için kullandığımız bazı koşul ifadeleri var:

  • if ve else. Genelde bunlar if-else ikilisi olarak bilinir. Ama ikisini birlikte kullanmak zorunda değiliz.
  • ?” ve “:“. Bunlarda if-else ikilisinin şekil hali.
  • switch

if-else

if(//koşul ifadesi burada)
{
   //koşul doğruysa bu alandaki kodlar çalışacak
}
else
{
   //if içindeki koşul doğru değilse bu alandaki kodlar çalışacak
}

Basit bir if-else bu şekilde çalışıyor. Şimdi basit bir örnek:

#include <stdlib.h>
#include <stdio.h>
int main(void)  
{
    int sayi;
    
    printf("1-10 arasında bir sayı girin: ");
    scanf("%d", &sayi);

    if(sayi >= 1 && sayi <= 10)
    {
        printf("Girdiğiniz sayı: %d", sayi);
    }
    else
    {
        printf("Sayı 1-10 aralığında değil.");
    }
}

9. satırda girilen sayının 1-10 arasında olup olmadığını kontrol ettim. && kullanmanın sebebi ise, girilen sayının hem 1’den büyük olmasını hem de 10’dan küçük olmasını kontrol etmek.

8 değerini girdiğimizde (sayi>=1) yani 8>=1 ifadesi doğru demektir. Ayrıca (sayi<=10) için de ifade doğru demektir. Çünkü 8, 10’dan küçük. Sonuç olarak doğru && doğru = doğru demektir. Bu arada bilgisayar için doğru demek 1 demektir. Yanlış ise 0.

Sonuç olarak 9. satırdaki if ifadesi doğru olduğu için if bloğu içindeki kodlar çalışacaktır.

Şimdik if ifadesindeki koşulu şu şekilde değiştiriyorum:

if(sayi >= 1 || sayi <= 10)

Bu durumda hangi sayıyı girersek girelim sonuç doğru çıkacaktır. 15 girdiğimizi düşünelim. (15>=1) ifadesi doğrudur. (15<=10) ifadesi yanlış. Sonuç olarak (doğru) || (yanlış) = (doğru) yapacağından yine if içerisindeki kodlar çalışacaktır.

Koşul ifadesini kontrol ettikten sonra yapacağımız işlem tek satırlık ise { } arasına almamıza gerek yok. Çünkü if koşulu kontrol eder ve doğruysa hemen altındaki kod satırını çalıştırır. Aynısı else içinde geçerli. Yani şöyle:

int sayi;
    
    printf("Bir sayı girin: ");
    scanf("%d", &sayi);
    
    if(sayi < 0)
        printf("Girilen sayı negatif.");
    
    printf("nBen hep buradayım...");

0 ve 0’dan büyük değerler için if ifadesi yanlış çıkacaktır. Yani if‘in hemen altındaki 11. satırdaki kodlar çalışmayacaktır. 13. satırdan devam edecektir.

evet

if-else ilişkisinde önemli bir detay var. “her else en yakın eşleşmemiş if ile eşleştirilir.*

int a = -5, b = 10;
    
    if(a>0)
        if(b>0)
            printf("+");
        else
            printf("#");
    printf("Son");

Yukarıdaki kodu inceleyelim.

3. satırdaki if için (-5>0)  ifadesi yanlıştır. Dolayısıyla if‘in hemen altındaki 4. satırdaki kodlar çalışmayacaktır.

6.satırdaki else ise kendisine en yakın olan if‘e bağlı olduğu için, yani 4. satırdaki if, program direkt 8. satıra geçecektir. Sonuç olarak ekrana sadece “Son” yazılacaktır.

else if

Eğer çok sayıda koşul kontrol edilecekse else if ile kontrol edebiliriz. Koşullardan birisi doğru ise diğer koşulları kontrol etmemize gerek kalmaz. Bu da şöyle çalışmakta:

int sayi = 35;
    
    if(sayi > 50)
    {
        printf("50 den büyük");
    }
    else if (sayi > 40)
    {
        printf("50 den küçük 40 dan büyük");
    }
    else if (sayi > 30)
    {
        printf("40 dan küçük 30 dan büyük");
    }
    else if (sayi > 20)
    {
        printf("30 dan küçük 20 den büyük");
    }
    else
    {
        printf("20 ya da daha küçük");
    }

Önce 3. satırdaki koşul kontrol ediliyor. İfade yanlış olduğu için 7. satırdaki else if koşulu kontrol ediyor.

Koşul yine yanlış olduğu için 11. satırdaki else if koşulu kontrol ediyor. İfade doğru olduğu için 15. ve 19. satırlardaki koşulların kontrol edilmesine gerek kalmıyor.

İşte böyle…

Dilersen { } arasına istediğin kadar kod yazabilirsin. İç içe istediğin kadar if, else, else if oluşturabilirsin. Ama şunu da unutmamak lazım ki programın ne kadar çok koşul kontrol etmesi gerekiyorsa o kadar çok durup düşünmesi gerekecek demektir. O yüzden koşulları kontrol ettirirken en basit ve efektif şekilde tasarlamak gerek.

 ? ve : (Conditional Operator)

? ve : operatörleri if ve else ile aynı mantıkta çalışıyor. Ne farkı var diye araştırdım. Kendileri ternary operation ailesine mensunpmuş. Yani 3 farklı argüman alıyor. Geriye dönen sonuç ve argümanlar farklı tiplerde olabiliyormuş.

Basit bir örnekle açıklamak daha iyi olur*:

#include <stdlib.h>
#include <stdio.h>
int main(void)  
{
    int sayi = 4, sonuc;
    
    sonuc = (sayi%2 == 0) ? 1 : 0;
    if(sonuc == 1)
    {
        puts("Sayı çift");
    }
    else
    {
        puts("Sayı tek");
    }
    
    return 0;
}

7. satırda ? ve : kullanımını görüyoruz.

Öncelikle bir koşulumuz var: (sayi %2 == 0). Yani, sayi değişkeninin 2 ile bölümünden kalan 0’a eşittir.

Koşuldan sonra ? geliyor.  Yani, “koşul doğru mu?” diye soruyor. Eğer doğruysa sonuc değişkenine 1 değerini atıyor.

: ise eğer koşul yanlışsa sonuç değişkenine 0 değerini atıyor.

Yukarıdaki kodu daha pratik hale getirebiliriz. Şöyle ki:

(sayi%2==0) ? printf("Çift sayı") : printf("Tek sayı");

 switch

if‘ler else if‘ler ile hemen hemen aynı sayılır. Ama aralarında bazı farklar var. En önemli fark, if - else, if ile koşulları kontrol ederken doğru koşula ulaşınca diğer koşulları atlayarak yola devam ediyorduk. switch ile istersek doğru koşul sağlansa bile diğer koşulları atlamayıp çalıştırabiliriz. Bunun için “break” anahtar kelimesini kullanmak gerek.

if- else‘de hiçbir koşul doğru değilse else alanındaki kodlar çalışıyordu. switch'de bu görevi “default” üstleniyor.

Çok sayıda iç içe  if-else, else if‘lerin kullanıldığı durumlarda bazen kodları okuması ve yorumlaması zor ve kafa karıştırıcı olabilir. Bu tip durumlarda switch kullanmak hem daha performanslı hem de daha okunaklı kodlar elde etmeye yarıyor.

Önce hafif iç içe geçmiş bir if örneği sunmak istiyorum:

    if(koşul1)
        kodlar
    else   
    {
        kodlar
        if(koşul2)
        {
            if(koşul3)
                kodlar
            else
                if(koşul 4)
                {
                    kodlar
                }         
        }
        else
        {
            kodlar
        }
    }

Yukarıdaki örnekte çok sayıda kod olduğunu düşünürsen, hata yapma olasılığının farkına varabilirsin. Şimdi switch‘in genel kullanım yapısına bir göz atalım*:

switch(değişken)
{
case değer1 : 
    kodlar
    break;

case değer2: 
    kodlar
    break;

case value3 : 
    body3
    break;

default :
    kodlar
    break;  
}

Görüldüğü üzere switch çok sayıda içi içe if’e göre daha okunaklı.  Ayrıca kaynağını hatırlamadığım bir yazıda, iyi geliştiricilerin if yerine switch tercih ettiği yazıyordu.

Basit bir örnek ile bugünkü dersimi de bitireyim.

yes, no, maybe

 

int sayi;
    
    printf("lütfen bir sayı girin: ");
    scanf("%d", &sayi);
    
    switch(sayi)
    {
        case 1:
            puts("Sayı 1");
            break;
        case 2:
            puts("Sayı 2");
        case 3:
            puts("Sayı 3");
            break;
        case 4:
        case 5:
        case 6:
        case 7:
            puts("4, 5, 6, 7 olabilir.");
            break;
        default:
            puts("1-7 arasında değil.");
            break;
    }

İlk olarak 1 değerini girdiğimi düşünelim. Bu durumda sayi değişkeni 1 değerini tutuyor. Yani switch 1 olan durumu aramaya başlıyor. case 1‘e geldiğinde koşul sağlanıyor ve puts("Sayı 1") fonksiyonunu çalışıyor. Daha sonra break komutu  için switch içinden çıkıyoruz.

Şimdi 2 değerini girdiğimi düşünelim. case 1 koşulu sağlamıyor. case 2 koşulu sağladığı için puts() çalışıyor. Henüz break komutu olmadığından case 3‘e geçiliyor ve buradaki komutlarda çalıştırılıyor. Önce puts() çalışıyor. Ardından break komutu geldiği için switch sonlanıyor.

5 değeri için, case 1, case 2, case 3,  case 4 çalışmıyor. case 5 ise koşulu sağlıyor ama herhangi bir komut olmadığı için case 6 ile devam ediyoruz. case 6'da çalışıyor ve case 7‘ye geliniyor. Önce puts() çalışıyor ardından break ile switch sonlanıyor.

case haricinindeki bir değer girersek, 10 gibi, hiçbir case koşulu sağlamadığı için default alanındaki kodlar çalıştırılıyor. default için break komutu kullanmak opsiyonel.

Özet

  • veri tipi dönüşümü otomatik ya da elle olsun, büyük değerden küçük değere dönüşüm yapılırken veri kaybı yaşanabilir. dikkat etmek lazım.
  • x++, önce işlemleri yap sonra x’in değerini arttır, ++x önce x’i arttır sonra işlemleri yap.
  • x *= 3 ile x = x* 3 aynı şey.
  • her else kendine en yakın eşleşmemiş if ile eşleşir.
  • koşulları karşılaştırırken switch kullanmaya özen göster. daha hızlı ve okunaklı.

Çalışma Soruları

//Güncellenecek.

Senaryo1: Girilen yılın, artık yıl olup olmadığı bulan bir program yazalım. Artık yıl, 4’ün katı olmalıdır. Eğer yıl 100’ün katıysa 400’e de kalansız olarak bölünmelidir. *

Senaryo2: Bir boyacı dükkanımız var. 3 farklı tipte (A, B, C) boyamız var ve bu tiplere göre boyanın fiyatı değişiyor. A boyası için, metre karesi 100.00 TL, B boyası için 125.00 TL, C boyası için 135.45 TL. Kullanıcı önce boya tipini daha sonra kaç metre kare boya yaptıracağını seçecek. Bunun sonucunda kullanıcıya fiyat bilgisi verilecek.

Senaryo3: x<1 –> f(x) = x^2 + 3x;  x >= 1 –> 3x + 4 fonksiyonunu hesaplayan program.

Eğitimim için kullandığım örnekleri bu adresteki github reposunda saklıyorum. Ders başlığıyla eşit olan klasörde kaynak kodları bulabilirsiniz.

Senaryo1

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {

    int year;
    
    puts("Enter a year, please:");
    scanf("%d", &year);
    

    if(year < 0) {
        puts("You have to enter positive integer.");
    }
    else {
        if( (year % 4) == 0 ) {
            if( (year % 100) == 0 ) {
                if( (year % 400) == 0 )
                    puts("Leap year!");
                else
                    puts("Not leap year!");
            } 
            else
                puts("Leap year!");
        }
        else
            puts("Not leap year!");
    }

    /**
     * Alternative solution 
     * (http://stackoverflow.com/questions/3220163/how-to-find-leap-year-programatically-in-c)
     * 
    if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) {
        puts("Leap year!");
    }
    else {
        puts("Not leap year!");
    }
     * 
     */
    return (EXIT_SUCCESS);
}

 Senaryo2

#include <stdio.h>
#include <stdlib.h>


int main(int argc, char** argv) {

    char paintType;
    float sm, cost;
    float A = 100.00, B = 125.00, C = 135.45;
    
    puts("Enter paint type (A, B, C), please:");
    scanf("%c", &paintType);
    
    switch(paintType) {
        case 'a':
        case 'A':
            puts("How many square meters will paint?");
            scanf("%f", &sm);
            cost = A * sm;
            printf("Total cost: %.2f", cost);
            break;
        case 'b':
        case 'B':
            puts("How many square meters will paint?");
            scanf("%f", &sm);
            cost = B * sm;
            printf("Total cost: %.2f", cost);
            break;
        case 'c':
        case 'C':
            puts("How many square meters will paint?");
            scanf("%f", &sm);
            cost = C * sm;
            printf("Total cost: %.2f", cost);
            break;
        default:
            printf("Sorry, we don't have %c paint type", paintType);
            break;
    }
    return (EXIT_SUCCESS);
}

 Senaryo3

#include <stdio.h>
#include <stdlib.h>


int main(int argc, char** argv) {
    
    float x, fx;
    
    puts("Enter a number please:");
    scanf("%f", &x);
    
    if(x >= 1) {
        fx = (3*x) + 4;
    }
    else {
        fx = (x * x) + (x*3);
    }
    printf("f(x) = %.3f", fx);
    
    return (EXIT_SUCCESS);
}

Kategori: C

Yorumlar

Sizde düşüncelerinizi paylaşın

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back To Top