C การวนซ้ำ



while

#include <stdio.h>

int main(void)
{
    int count = 3;
    while (count > 0) {
        printf("นับถอยหลัง: %d\n", count);
        count--;
    }
    printf("จบ\n");
    return 0;
}

เช็คเงื่อนไข ก่อน วนแต่ละรอบ — ถ้าเงื่อนไขเท็จตั้งแต่แรก body ไม่ทำงานเลย

do-while

int n = 0;
int ok;
do {
    printf("ใส่เลข 1-10: ");
    ok = scanf("%d", &n);
    if (ok != 1) {                 // ผู้ใช้พิมพ์ไม่ใช่ตัวเลข
        int c;
        while ((c = getchar()) != '\n' && c != EOF) { }   // ทิ้ง token ที่ผิด
        n = 0;                     // ให้เงื่อนไขวนต่อ
        if (c == EOF) break;       // stdin ปิดแล้ว — เลิกวน
    }
} while (ok != 1 || n < 1 || n > 10);

เช็คเงื่อนไข หลัง วน — body ทำงานอย่างน้อย 1 รอบเสมอ เหมาะกับการรับ input จนกว่าจะถูกต้อง

scanf("%d") ที่ไม่เช็คค่าคืน: ถ้าผู้ใช้พิมพ์ตัวอักษร scanf จะคืน 0 ทิ้ง token ผิดไว้ใน buffer แล้ววนอ่านตัวเดิมซ้ำไม่จบ — ต้องเช็คค่าคืนและล้าง buffer

for

for (int i = 0; i < 5; i++) {
    printf("%d ", i);          // 0 1 2 3 4
}
printf("\n");

3 ส่วน: เริ่มต้น; เงื่อนไขวนต่อ; ทำท้ายรอบ

  • ประกาศตัวนับใน for ได้ตั้งแต่ C99 (for (int i = ...)) — i มีชีวิตแค่ในลูป ใช้เสมอ
  • ถ้าต้องคอมไพล์ด้วย -std=c89 ต้องประกาศ i ก่อนลูป
for (int i = 10; i > 0; i -= 2) {
    printf("%d ", i);          // 10 8 6 4 2
}

วนอาร์เรย์ — ระวังขอบเขต

int nums[5] = {10, 20, 30, 40, 50};

for (int i = 0; i < 5; i++) {          // i จาก 0 ถึง 4
    printf("%d\n", nums[i]);
}

อาร์เรย์ 5 ตัว มี index 0 ถึง 4 — เงื่อนไขต้องเป็น i < 5 ไม่ใช่ i <= 5

for (int i = 0; i <= 5; i++) {         // ❌ i = 5 เข้าถึง nums[5] ซึ่งไม่มี!
    printf("%d\n", nums[i]);           // undefined behavior — อ่านนอกขอบเขต
}

C ไม่ตรวจขอบเขตอาร์เรย์nums[5], nums[100], nums[-1] คอมไพล์ผ่านและรัน อาจได้ค่าขยะ อาจ crash อาจเขียนทับตัวแปรอื่น นี่คือ off-by-one error ที่พบบ่อยที่สุดใน C

หลีกเลี่ยงการ hardcode ขนาด:

size_t len = sizeof(nums) / sizeof(nums[0]);   // = 5
for (size_t i = 0; i < len; i++) {
    printf("%d\n", nums[i]);
}

(เทคนิคนี้ใช้ได้เฉพาะกับอาร์เรย์จริง ไม่ใช่ pointer — บทที่ 9, 12)

break และ continue

for (int i = 1; i <= 10; i++) {
    if (i % 2 != 0) {
        continue;              // ข้ามเลขคี่ ไปรอบถัดไป
    }
    if (i > 6) {
        break;                 // เจอเกิน 6 ออกจากลูปเลย
    }
    printf("%d ", i);          // 2 4 6
}
printf("\n");
  • break — ออกจากลูปชั้นในสุดทันที
  • continue — ข้ามส่วนที่เหลือของ body ไปเช็คเงื่อนไขรอบถัดไป (ใน for ยังทำส่วน i++ ก่อน)

C ไม่มี break 2; แบบ PHP — ออกจากลูปซ้อนใช้ flag หรือ goto:

int found = 0;
for (int i = 0; i < rows && !found; i++) {
    for (int j = 0; j < cols; j++) {
        if (grid[i][j] == target) {
            found = 1;
            break;
        }
    }
}

หรือ goto (กรณีนี้ยอมรับได้):

for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        if (grid[i][j] == target) goto done;
    }
}
done:
printf("เสร็จ\n");

infinite loop

while (1) {
    // ... ต้องมี break ข้างในไม่งั้นไม่จบ
    if (should_stop()) break;
}

for (;;) {                    // เขียนแบบนี้ก็ได้ ("forever")
    // ...
}

ลูปซ้อน — ตารางสูตรคูณ

for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= 5; j++) {
        printf("%3d", i * j);
    }
    printf("\n");
}
  1  2  3  4  5
  2  4  6  8 10
  3  6  9 12 15
  4  8 12 16 20
  5 10 15 20 25

เรื่องที่คนมักพลาด

  • i <= n แทน i < n เมื่อวนอาร์เรย์ขนาด n — เข้าถึง index ที่ไม่มี (off-by-one, UB)
  • ลืมเพิ่มตัวนับwhile (i < 10) { printf(...); } ไม่มี i++ = infinite loop
  • for (unsigned i = n; i >= 0; i--)unsigned ไม่มีทาง < 0 วนไม่จบ ใช้ int หรือวนขึ้น
  • แก้ตัวนับข้างใน body ของ for จนงงว่าวนกี่รอบ — ถ้าต้องทำ ใช้ while ให้ชัด
  • ; หลัง for(...) โดยไม่ตั้งใจfor (i=0; i<5; i++); body ว่าง โค้ดข้างล่างทำครั้งเดียว -Wall เตือน (-Wempty-body)
  • ประกาศ int i ใน for แล้วใช้ i หลังลูป — scope จบที่ลูป (C99+) ต้องประกาศก่อนลูปถ้าต้องใช้ต่อ
  • do { scanf("%d", &n); } while (...) ไม่เช็คค่าคืน — ผู้ใช้พิมพ์ตัวอักษร → scanf คืน 0 ไม่กิน token ผิด → วนอ่านตัวเดิมไม่จบ เช็คค่าคืน + ล้าง buffer + จับ EOF

สรุป

  • while เช็คก่อน, do-while เช็คหลัง (ทำอย่างน้อย 1 รอบ), for เมื่อมีตัวนับ
  • ประกาศตัวนับใน for (for (int i = 0; ...)) — C99 ขึ้นไป
  • วนอาร์เรย์ขนาด n: i < n (ไม่ใช่ <=) — C ไม่ตรวจขอบเขตให้
  • break ออกลูปชั้นในสุด, continue ข้ามไปรอบถัดไป; ลูปซ้อนใช้ flag หรือ goto
  • เลี่ยง unsigned เป็นตัวนับที่ต้องเทียบ >= 0

บทถัดไปว่าด้วยฟังก์ชัน — prototype, pass by value, recursion และเหตุผลที่ต้องประกาศก่อนใช้