C เงื่อนไข if, else, switch
if / else if / else
#include <stdio.h>
int main(void)
{
int score = 72;
if (score >= 80) {
printf("เกรด A\n");
} else if (score >= 70) {
printf("เกรด B\n");
} else if (score >= 60) {
printf("เกรด C\n");
} else {
printf("ตก\n");
}
return 0;
}เงื่อนไขเป็นค่า scalar — 0 เท็จ, ที่เหลือจริง
if/while/for รับนิพจน์ scalar ได้ทุกชนิด — จำนวนเต็ม, ทศนิยม, และ pointer (NULL = เท็จ) ไม่จำกัดแค่ int
int n = 5;
if (n) printf("n ไม่ใช่ 0\n"); // พิมพ์
char *p = NULL;
if (p) printf("p ไม่ใช่ NULL\n"); // ไม่พิมพ์ (NULL = 0)
if (!p) printf("p เป็น NULL\n"); // พิมพ์
int result = some_function();
if (result != 0) { /* จัดการ error */ } // ชัดกว่า if (result)ปีกกา — ใส่เสมอ
C ยอมให้ละปีกกาเมื่อ body มีบรรทัดเดียว แต่ อย่าทำ — เป็นบ่อเกิดบั๊กชื่อดัง:
// อันตราย
if (x > 0)
printf("บวก\n");
total += x; // บรรทัดนี้ทำงาน "เสมอ" ไม่ได้อยู่ใน if!
// goto fail ของ Apple (2014) — ช่องโหว่ SSL จากการละปีกกา
if (err != 0)
goto fail;
goto fail; // บรรทัดนี้ทำงานเสมอ ข้ามการตรวจสอบที่เหลือ
// ปลอดภัย: ใส่ปีกกาเสมอ
if (x > 0) {
printf("บวก\n");
}dangling else
else จับคู่กับ if ที่ใกล้ที่สุดที่ยังไม่มี else:
if (a)
if (b)
printf("a และ b\n");
else
printf("นี่จับคู่กับ if (b) ไม่ใช่ if (a)!\n");ใส่ปีกกาให้ชัด:
if (a) {
if (b) {
printf("a และ b\n");
}
} else {
printf("ไม่ a\n");
}ternary
int age = 20;
const char *type = (age >= 18) ? "ผู้ใหญ่" : "เยาวชน";
printf("%s\n", type);
int max = (a > b) ? a : b;
printf("ค่ามากกว่า: %d\n", max);เหมาะกับการเลือกค่าสั้น ๆ — อย่าซ้อนหลายชั้น อ่านยาก
switch
#include <stdio.h>
int main(void)
{
int day = 3;
switch (day) {
case 1:
case 2:
case 3:
case 4:
case 5:
printf("วันทำงาน\n");
break;
case 6:
case 7:
printf("วันหยุด\n");
break;
default:
printf("ไม่รู้จักวันนี้\n");
break;
}
return 0;
}กฎของ switch
- ค่าที่เทียบต้องเป็นชนิด จำนวนเต็ม (int, char, enum,
longฯลฯ) — ไม่ใช่ float, ไม่ใช่สตริง caseต้องเป็น integer constant expression —case 3:,case 1+2:,case SIZE:(#define),case RED:(enum) ใช้ได้ แต่case x:(ตัวแปร) ใช้ไม่ได้- ปกติต้อง
breakทุก case — ไม่งั้น “ไหลต่อ” (fallthrough) ไป case ถัดไป ข้อยกเว้น:caseว่างที่รวมหลายค่าไว้ด้วยกัน, fallthrough ที่ตั้งใจ (ดูหัวข้อถัดไป), และ case ที่จบด้วยreturn/goto(ไม่ต้องbreak) defaultจับกรณีที่เหลือ (วางตรงไหนก็ได้ นิยมวางท้าย)
fallthrough — บางทีตั้งใจ
switch (grade) {
case 'A':
case 'B':
printf("ผ่าน\n");
// จงใจไม่ break — ไหลต่อ
__attribute__((fallthrough)); // บอกคอมไพเลอร์ว่าตั้งใจ (GCC/Clang)
case 'C':
printf("ได้เครดิต\n");
break;
default:
printf("ไม่ผ่าน\n");
}ส่วนใหญ่ fallthrough คือลืม break — เปิด -Wimplicit-fallthrough ให้เตือน
ประกาศตัวแปรใน case
switch (x) {
case 1: {
int temp = compute(); // ต้องครอบด้วย { } ไม่งั้น error
printf("%d\n", temp);
break;
}
case 2:
printf("สอง\n");
break;
}ตัวแปรที่ประกาศใน case โดยไม่มีปีกกา จะ “มองเห็น” ใน case อื่นด้วยแต่ยังไม่ถูก init — สร้าง scope ด้วย { }
switch เทียบ if–else
ใช้ switch เมื่อเทียบ ค่าเดียวกับหลายค่าคงที่ (เมนู, สถานะ, รหัส) — อ่านง่ายกว่า if–else ยาว ๆ และคอมไพเลอร์ *อาจ* ปรับเป็น jump table ให้ (ขึ้นกับว่า case กระจายตัวยังไง — ไม่ได้การันตีว่าเร็วกว่าเสมอ) ใช้ if เมื่อเงื่อนไขเป็นช่วงหรือซับซ้อน
เรื่องที่คนมักพลาด
- ลืม
breakในswitch— ไหลต่อไป case ถัดไป เปิด-Wimplicit-fallthrough - ละปีกกา —
if (x) a(); b();ทำให้b()ทำงานเสมอ ใส่{ }ทุกครั้ง if (x = 5)แทนif (x == 5)— กำหนดค่าแล้วจริงเสมอ-Wallเตือน; บางคนเขียนif (5 == x)กันพลาดswitchกับ float หรือสตริง — ไม่ได้ ต้องเป็นจำนวนเต็ม- ประกาศตัวแปรใน
caseไม่มีปีกกา — compile error หรือ scope กว้างกว่าที่คิด caseที่ค่าไม่คงที่ —case n:(ตัวแปร) ไม่ได้ ต้องเป็น integer constant expression (#define/enum/ นิพจน์ค่าคงที่)- dangling else —
elseจับifที่ใกล้สุด ใส่ปีกกาให้ชัด
สรุป
if / else if / else— เงื่อนไขเป็น scalar (จำนวน/pointer), 0 = เท็จ; ใส่ปีกกาเสมอ แม้ body บรรทัดเดียว- ternary
cond ? a : bสำหรับเลือกค่าสั้น ๆ switch (int-expr)—caseเป็น integer constant expression,break/returnทุก case (เว้นแต่ตั้งใจ fallthrough),defaultจับที่เหลือ- ประกาศตัวแปรใน
caseต้องครอบ{ } - เปิด
-Wimplicit-fallthroughและ-Wall
บทถัดไปว่าด้วยการวนซ้ำ — for, while, do-while และเรื่อง off-by-one