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 expressioncase 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 เมื่อเทียบ ค่าเดียวกับหลายค่าคงที่ (เมนู, สถานะ, รหัส) — อ่านง่ายกว่า ifelse ยาว ๆ และคอมไพเลอร์ *อาจ* ปรับเป็น 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 elseelse จับ 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