SEN2201 · 11. hafta · Programming in C

C kontrol yapıları: if, switch, while, for, do-while

Karar ve döngü yapıları, break/continue, switch'te düşme (fall-through), sık tuzaklar (if içinde =, sarkan else, for sonrası ;) ve bunların LC-3 dallanmalarına karşılığı.

control structureconditionalif-elsedangling elseswitchcasefall-throughbreakcontinuewhile loopfor loopdo-while looploop countersentinel
  1. haftada döngüleri ve if’leri BR komutlarıyla elle kurdun. C’de aynı yapılar if, while, for gibi anahtar kelimelerle yazılır; derleyici onları senin yazdığın BR kalıplarına çevirir. Bu sayfa her yapının kuralını ve sınavda en çok tuzak kurulan noktaları anlatır. Soru tipi yine “What is printed?”.

if ve if-else

int score = 72;
if (score >= 85) printf("AA\n");
else if (score >= 70) printf("BB\n");
else if (score >= 50) printf("CC\n");
else printf("FF\n");
BB

Koşullar yukarıdan aşağı test edilir, ilk doğru olanın bloğu çalışır, gerisi atlanır.

TUZAK · = ile ==. if (x = 5) x’e 5 atar ve ifadenin değeri 5’tir, her zaman doğru. Derleyici çoğu zaman sadece uyarı verir. Sınavda bu kodu gördüğünde çıktıyı atamaya göre yaz.

Sarkan else (dangling else)

if (a == 0)
    if (b == 0) printf("A\n");
else printf("B\n");

Girinti, else’in dıştaki if’e ait olduğunu düşündürüyor. Kural öyle değil: else, en yakın eşlenmemiş if’e bağlanır. Yani içteki if (b == 0)’ın else’idir. a = 1 iken hiçbir şey basılmaz. Ne demek istediğini netleştirmek için her zaman { } kullan.

switch

switch (ifade) {
    case SABIT1: ... break;
    case SABIT2: ... break;
    default: ...
}

İfadenin değeri hangi case’le eşleşirse oraya atlanır ve break’e kadar (ya da switch bitene kadar) devam edilir. break yoksa alttaki case’lerin kodu da çalışır: fall-through.

int d = 1;
switch (d) {
    case 0: printf("zero ");
    case 1: printf("one ");
    case 2: printf("two ");
}
one two

Case etiketleri sabit olmalıdır (int ya da char). case x > 5: yazamazsın.

while

int n = 3;
while (n > 0) {
    printf("%d ", n);
    n--;
}
printf("go\n");
3 2 1 go

Koşul önce test edilir. Başta yanlışsa gövde hiç çalışmaz.

LC-3 karşılığı (n R0’da):

LOOP    ADD  R0, R0, #0    ; CC'yi n'e göre ayarla
        BRnz DONE          ; n <= 0 ise çık
        ...                ; gövde
        ADD  R0, R0, #-1   ; n--
        BRnzp LOOP
DONE    ...

for

for (başlangıç; koşul; güncelleme) gövde;

Şununla aynıdır:

başlangıç;
while (koşul) { gövde; güncelleme; }
int i;
for (i = 1; i <= 4; i++)
    printf("%d ", i * i);
1 4 9 16

İzleme tablosu

Döngü sorularını tabloyla çöz:

for (i = 10; i > 0; i /= 2) c++;

i     i > 0?   c
10    evet     1
5     evet     2
2     evet     3
1     evet     4
0     hayır    (biter)

TUZAK · for’dan sonra noktalı virgül. for (i = 0; i < 5; i++); boş gövdeli bir döngüdür. Alttaki satır, girintisi ne olursa olsun, döngü bittikten sonra bir kez çalışır ve i = 5 görür.

do-while

do {
    gövde;
} while (koşul);

Gövde önce çalışır, koşul sonra test edilir. Bu yüzden en az bir kez çalışır. Menü döngüleri (“kullanıcı 0 girene kadar sor”) için idealdir. Sondaki ; zorunludur.

break ve continue

  • break: en içteki döngüden ya da switch’ten hemen çık.
  • continue: bu turun geri kalanını atla, bir sonraki tura geç. for’da güncelleme kısmı (i++) yine çalışır.
for (i = 0; i < 10; i++) {
    if (i % 3 == 0) continue;   /* 0, 3, 6 atlanır */
    if (i > 7) break;           /* 8'de çık */
    printf("%d ", i);
}
1 2 4 5 7

İç içe döngüler

Dış döngünün her turunda iç döngü baştan sona çalışır.

int i, j;
for (i = 1; i <= 3; i++) {
    for (j = 0; j < i; j++)
        printf("*");
    printf("\n");
}
*
**
***

Toplam iç tur sayısı: 1 + 2 + 3 = 6. “Kaç kez çalışır?” sorularında her dış tur için iç döngünün kaç kez döndüğünü yazıp topla.

Çözümlü iz: Collatz dizisi

n çiftse yarıla, tekse 3n + 1 yap; 1’e ulaşana kadar devam et.

int n = 6, steps = 0;
while (n != 1) {
    if (n % 2 == 0) n = n / 2;
    else n = 3 * n + 1;
    printf("%d ", n);
    steps++;
}
printf("\n%d\n", steps);
3 10 5 16 8 4 2 1
8

İz tablosu kurarken her satırda üç şeyi yaz: koşulun sonucu, hangi dal çalıştı, değişkenlerin yeni değeri.

n    n != 1?   dal        yeni n   steps
6    evet      çift       3        1
3    evet      tek        10       2
10   evet      çift       5        3
5    evet      tek        16       4
16   evet      çift       8        5
8    evet      çift       4        6
4    evet      çift       2        7
2    evet      çift       1        8
1    hayır     (biter)

for döngüsünün LC-3 karşılığı

int s = 0, i;
for (i = 1; i <= 4; i++) s += i;     /* s = 10 */

Derleyici (s R0’da, i R1’de) kabaca şunu üretir:

        AND  R0, R0, #0     ; s = 0
        AND  R1, R1, #0
        ADD  R1, R1, #1     ; i = 1          (başlangıç)
LOOP    ADD  R2, R1, #-5    ; i - 5
        BRzp DONE           ; i >= 5 ise çık  (koşulun tersi)
        ADD  R0, R0, R1     ; s += i          (gövde)
        ADD  R1, R1, #1     ; i++             (güncelleme)
        BRnzp LOOP
DONE    HALT

Simülatörde sonuç R0 = 10, R1 = 5. i <= 4 testi, “i - 5 negatif mi?” sorusuna dönüşür: LC-3’te karşılaştırma her zaman çıkarma + condition code demektir. Koşul doğruysa gövdeye düşülür; yanlışsa (z veya p) döngünün dışına atlanır.

Hazır döngü kalıpları

İşKalıp
1’den n’e toplafor (i = 1; i <= n; i++) s += i;
Basamak toplamıwhile (n > 0) { s += n % 10; n /= 10; }
Basamak sayısıwhile (n > 0) { c++; n /= 10; }
Sentinel ile okuwhile (x != 0) { ...; scanf("%d", &x); }
En az bir kez sordo { ... } while (cevap != 0);

Sık yapılan hatalar

Yanlış düşünceDoğrusu
if (x = 5) x’i 5 ile karşılaştırırAtar; her zaman doğru
else girintiye göre bağlanırEn yakın if’e bağlanır
switch eşleşen case’i çalıştırıp çıkarbreak yoksa alttakilere düşer
do-while koşul yanlışsa hiç çalışmazEn az bir kez çalışır
for (...); sonrası satır döngüdeDöngü boş, satır bir kez çalışır
continue döngüyü bitirirSadece bu turu atlar
break tüm iç içe döngülerden çıkarSadece en içtekinden
case x > 5: geçerliCase sabit olmalı

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

What is printed?

int x = 0;
if (x = 5) printf("yes %d\n", x);
else printf("no %d\n", x);
Cevabı göster
yes 5

x = 5 karşılaştırma değil, atamadır. x'e 5 yazılır ve ifadenin değeri 5 olur; 5 sıfır olmadığı için doğru. Karşılaştırma için == gerekir.

Soru 2zor

What is printed?

int a = 1, b = 0;
if (a == 0)
    if (b == 0) printf("A\n");
else printf("B\n");
printf("end\n");
Cevabı göster
end

else, girintiye değil en yakın eşlenmemiş if'e bağlanır (dangling else): if (b == 0)'ın else'idir. Dıştaki if (a == 0) yanlış olduğu için içteki if-else hiç çalışmaz. Sadece end basılır.

Soru 3orta

What is printed?

int i = 1, s = 0;
while (i <= 10) {
    s += i;
    i += 3;
}
printf("%d %d\n", s, i);
Cevabı göster
22 13

i = 1, 4, 7, 10 değerlerinde gövde çalışır: s = 1 + 4 + 7 + 10 = 22. Sonra i = 13, koşul yanlış.

Soru 4kolay

What is printed?

int i;
for (i = 2; i < 15; i += 4)
    printf("%d ", i);
Cevabı göster
2 6 10 14

i = 2, 6, 10, 14 için koşul doğru. i = 18 olunca biter.

Soru 5kolay

What is printed?

int n = 10;
do {
    printf("%d ", n);
    n++;
} while (n < 5);
Cevabı göster
10

do-while koşulu gövdeden SONRA test eder, bu yüzden gövde en az bir kez çalışır. 10 basılır, n = 11, 11 < 5 yanlış, döngü biter.

Soru 6orta

What is printed?

int k = 2;
switch (k) {
    case 1: printf("one ");
    case 2: printf("two ");
    case 3: printf("three ");
            break;
    default: printf("other ");
}
Cevabı göster
two three

case 2'ye atlanır. break olmadığı için bir sonraki case'e düşer (fall-through) ve three de basılır. break'te switch biter, default çalışmaz.

Soru 7orta

What is printed?

int i;
for (i = 0; i < 10; i++) {
    if (i % 3 == 0) continue;
    if (i > 7) break;
    printf("%d ", i);
}
Cevabı göster
1 2 4 5 7

continue o turun geri kalanını atlar (i = 0, 3, 6). break döngüyü bitirir: i = 8'de 8 % 3 ≠ 0, 8 > 7 → break. Basılanlar 1 2 4 5 7.

Soru 8orta

What is printed?

int i, j, c = 0;
for (i = 0; i < 4; i++)
    for (j = i; j < 4; j++)
        c++;
printf("%d\n", c);
Cevabı göster
10

İç döngü i = 0'da 4, i = 1'de 3, i = 2'de 2, i = 3'te 1 kez döner: 4 + 3 + 2 + 1 = 10.

Soru 9zor

What is printed?

int i;
for (i = 0; i < 5; i++);
    printf("%d\n", i);
Cevabı göster
5

for'un sonundaki ; boş bir gövdedir. Döngü hiçbir şey yapmadan 5 kez döner, i = 5 olur. Girintiye rağmen printf döngünün dışındadır, bir kez çalışır.

Soru 10orta

How many times does the loop body execute, and what is printed?

int i, c = 0;
for (i = 10; i > 0; i /= 2)
    c++;
printf("%d\n", c);
Cevabı göster
4

i = 10, 5, 2, 1 için gövde çalışır (tam sayı bölmesi: 5 / 2 = 2, 1 / 2 = 0). i = 0 olunca biter.

Soru 11kolay

What is printed?

int n = 4096, s = 0;
while (n > 0) {
    s += n % 10;
    n /= 10;
}
printf("%d\n", s);
Cevabı göster
19

Basamak toplamı: n % 10 son basamağı alır, n / 10 onu atar. 6 + 9 + 0 + 4 = 19.

Soru 12zor

A C compiler translates if (x < 0) x = -x; for the LC-3, with x already loaded into R0. Which branch instruction skips the assignment?

Cevabı göster
BRzp

Kalıp: koşulun tersiyle atla. x < 0 değilse (yani x ≥ 0, z veya p) atama atlanmalı: BRzp SKIP. Ardından NOT R0, R0 / ADD R0, R0, #1 / STR ile x güncellenir. 8. haftadaki mutlak değer programının aynısı.

Doğrulama: Her kod parçası C99 olarak Zig cc (clang) ile derlenip çalıştırıldı, çıktılar birebir kopyalandı (verify/SEN2201/c/w11b.c ve w11c.c, node verify/SEN2201/c/run.mjs w11); for döngüsünün LC-3 karşılığı kendi simülatörümüzde çalıştırıldı (verify/SEN2201/08.mjs sonu).

Deftere not kartı

if (x = 5) atama! değeri 5 -> doğru. Karşılaştırma == else en yakın eşlenmemiş if'e bağlanır (girinti önemsiz) -> { } kullan while: önce test | do-while: önce gövde (en az 1 kez) | for(init; test; update) for (...); -> boş gövde, alttaki satır döngüden SONRA bir kez switch: eşleşen case'e atla, break yoksa ALTTAKİLERE DÜŞ | default: hiçbiri tutmazsa case etiketi sabit olmalı (int/char), aralık yazılamaz break: en içteki döngü/switch'ten çık | continue: bu turu atla (for'da update çalışır) İç içe: j = i..n-1 -> n + (n-1) + ... + 1 i /= 2 döngüsü: 10,5,2,1 -> 4 tur Basamak: n%10 al, n/10 at LC-3: if -> koşulun TERSİYLE atla (x<0 için BRzp) | while -> başta test + sonda BRnzp geri