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ığı.
- haftada döngüleri ve if’leri BR komutlarıyla elle kurdun. C’de aynı yapılar
if,while,forgibi 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 topla | for (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 oku | while (x != 0) { ...; scanf("%d", &x); } |
| En az bir kez sor | do { ... } while (cevap != 0); |
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
if (x = 5) x’i 5 ile karşılaştırır | Atar; her zaman doğru |
| else girintiye göre bağlanır | En yakın if’e bağlanır |
| switch eşleşen case’i çalıştırıp çıkar | break yoksa alttakilere düşer |
| do-while koşul yanlışsa hiç çalışmaz | En az bir kez çalışır |
for (...); sonrası satır döngüde | Döngü boş, satır bir kez çalışır |
| continue döngüyü bitirir | Sadece bu turu atlar |
| break tüm iç içe döngülerden çıkar | Sadece en içtekinden |
case x > 5: geçerli | Case sabit olmalı |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
What is printed?
int x = 0;
if (x = 5) printf("yes %d\n", x);
else printf("no %d\n", x);Cevabı göster
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.
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
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.
What is printed?
int i = 1, s = 0;
while (i <= 10) {
s += i;
i += 3;
}
printf("%d %d\n", s, i);Cevabı göster
i = 1, 4, 7, 10 değerlerinde gövde çalışır: s = 1 + 4 + 7 + 10 = 22. Sonra i = 13, koşul yanlış.
What is printed?
int i;
for (i = 2; i < 15; i += 4)
printf("%d ", i);Cevabı göster
i = 2, 6, 10, 14 için koşul doğru. i = 18 olunca biter.
What is printed?
int n = 10;
do {
printf("%d ", n);
n++;
} while (n < 5);Cevabı göster
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.
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
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.
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
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.
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
İç 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.
What is printed?
int i;
for (i = 0; i < 5; i++);
printf("%d\n", i);Cevabı göster
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.
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
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.
What is printed?
int n = 4096, s = 0;
while (n > 0) {
s += n % 10;
n /= 10;
}
printf("%d\n", s);Cevabı göster
Basamak toplamı: n % 10 son basamağı alır, n / 10 onu atar. 6 + 9 + 0 + 4 = 19.
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
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).