Sign in

Part of 10306103 หลักการเขียนโปรแกรมเบื้องต้น (Fundamentals of Programming)

2026-10-22 · updated 2026-10-30 · 21 min read

บทที่ 5 โครงสร้างควบคุมแบบวนซ้ำ

ShareFacebookXLinkedInLINE

แผนบริหารการสอนประจำบทที่ 5

บทนี้ว่าด้วยโครงสร้างควบคุมแบบวนซ้ำของภาษา C ต่อยอดจากโครงสร้างควบคุมแบบทางเลือก (if/else) และตัวดำเนินการเปรียบเทียบ/ตรรกะในบทที่ 4 โดยอธิบายลูปสามชนิดคือ for, while และ do-while การเลือกชนิดลูปให้เหมาะกับโจทย์ การใช้ตัวแปรนับและตัวแปรสะสม และการควบคุมการวนซ้ำด้วย break และ continue เพื่อให้เขียนโปรแกรมที่ทำงานซ้ำตามเงื่อนไขได้อย่างถูกต้องและอ่านง่าย

ผลลัพธ์ข้อความหลักฐานCLO
LLO5.1อธิบายและใช้ลูป for, while และ do-while ในการทำงานซ้ำงานข้อ 1 (ปฏิบัติการที่ 5 Lab A)CLO1
LLO5.2เลือกชนิดลูปให้เหมาะกับโจทย์ (จำนวนรอบที่รู้ล่วงหน้า เทียบกับเงื่อนไข) และใช้ตัวแปรนับ/สะสมงานข้อ 2 (ปฏิบัติการที่ 5 Lab A)CLO2
LLO5.3ใช้ break และ continue ควบคุมการวนซ้ำได้ถูกต้องงานข้อ 3 (ปฏิบัติการที่ 5 Lab B)CLO2
LLO5.4ปฏิบัติตามมาตรฐานการเขียนโค้ด (การจัดย่อหน้า การตั้งชื่อสื่อความหมาย การใส่คอมเมนต์) ในงานโค้ดของบทนี้โค้ดทุกข้อที่ส่งในบทนี้CLO5

CLO ที่เกี่ยวข้อง (ข้อความจากหลักสูตร):

  • CLO1 อธิบายและใช้ตัวแปร ชนิดข้อมูล การรับ-แสดงผล และโครงสร้างควบคุม (if/else, switch, loop) เพื่อแก้ปัญหาและได้ผลลัพธ์ที่ถูกต้อง
  • CLO2 อธิบายขั้นตอนการทำงาน (Logic) ของภาษาโปรแกรมและเลือกใช้โครงสร้างควบคุม (Control Structure) ให้สอดคล้องกับโจทย์ที่กำหนดได้

บทที่ 5 เป็นบทที่ทำให้โปรแกรมทำงานซ้ำได้โดยไม่ต้องเขียนคำสั่งเดิมหลายครั้ง จึงเป็นสะพานเชื่อมความรู้เรื่องการเลือกทำงานในบทที่ 4 ไปสู่การออกแบบฟังก์ชันในบทที่ 6 และการประมวลผลอาร์เรย์ในบทที่ 7 งานทั้งสามข้อในบทนี้สะสมหลักฐานของ CLO1 และ CLO2 ต่อเนื่องจากบทก่อน งานของบทนี้อยู่ในหมวดงานท้ายบทตามแผนคะแนนที่ผู้สอนยืนยันไว้ ดูรายละเอียดในบันทึกสำหรับผู้สอนท้ายบท

วิธีสอน

  1. ทบทวนการเลือกทำงานด้วย if และตัวดำเนินการเปรียบเทียบจากบทที่ 4 แล้วตั้งโจทย์ที่ต้องทำงานซ้ำหลายรอบเพื่อชี้ให้เห็นความจำเป็นของลูป
  2. แนะนำลูป for ด้วยตัวแปรนับ โดยสาธิตส่วนประกอบสามส่วน (ค่าเริ่ม เงื่อนไข ปรับค่า) บนเครื่องจริง
  3. เปรียบเทียบ while ที่ตรวจเงื่อนไขก่อนทำ กับ do-while ที่ทำก่อนอย่างน้อยหนึ่งรอบ พร้อมชี้กับดักลูปไม่รู้จบ
  4. สาธิตรูปแบบตัวแปรสะสมและตัวแปรนับ แล้วให้ลงมือปฏิบัติตามใบงาน โดยกำหนดจุดตรวจระหว่างทางเพื่อให้ข้อมูลป้อนกลับทันที
  5. สรุปการใช้ break และ continue ตามเจตนา และย้ำข้อผิดพลาดที่พบบ่อย เช่น การตั้งค่าเริ่มต้นตัวสะสมผิดและการลืมปรับตัวนับ

สื่อการเรียนการสอน

  • เครื่องคอมพิวเตอร์ที่เข้าถึงแพลตฟอร์มการเรียนของรายวิชาสำหรับเขียน คอมไพล์ และรันโปรแกรมภาษา C (มาตรฐาน C11) บนเว็บ
  • เอกสารประกอบบทที่ 5 และสไลด์สรุปลูปสามชนิดและการใช้ break/continue
  • ไฟล์โปรแกรมตัวอย่าง ch5_sum.c และ ch5_loops.c

การวัดผลและการประเมินผล

กำหนดส่ง: งานข้อ 1–3 ภายใน 23:59 น. ของวันก่อนวันปฏิบัติการในสัปดาห์ที่ 6 ส่วนชุดฝึกท้ายบทและแบบฝึกหัดวัดความเข้าใจของบทนี้ ภายใน 23:59 น. ของวันก่อนวันบรรยายในสัปดาห์ที่ 6 ดูวันที่จริงของตอนเรียนได้ในห้องเรียนออนไลน์ (มติผู้สอน 28 ต.ค. 2569)

ข้องานหลักฐานผลลัพธ์CLO
1เขียนลูปคำนวณผลรวม 1..n ด้วย for และแฟกทอเรียลด้วย whileไฟล์ซอร์สโค้ด + ผลการรันLLO5.1CLO1
2เลือกชนิดลูปให้เหมาะกับโจทย์ที่กำหนด พร้อมใช้ตัวแปรสะสม/ตัวนับไฟล์ซอร์สโค้ด + ผลการรันLLO5.2CLO2
3ใช้ continue ข้ามกรณี และ break หยุดลูปตามเงื่อนไขไฟล์ซอร์สโค้ด + ผลการรันLLO5.3CLO2

เงื่อนไขผ่าน: งานข้อ 2 ประเมินจากไฟล์เดียวกับงานข้อ 1 ที่ต่อยอดการเลือกชนิดลูปและออกแบบตัวแปรสะสม ดังนั้นงานข้อ 1 ต้องให้ผลรวมและแฟกทอเรียลถูกต้องและคอมไพล์ผ่านก่อนจึงจะประเมินข้อ 2 ได้

คะแนนใบงาน (10 คะแนน)

ผลลัพธ์สิ่งที่ตรวจคะแนน
LLO5.1งานข้อที่ระบุผลลัพธ์นี้3
LLO5.2งานข้อที่ระบุผลลัพธ์นี้3
LLO5.3งานข้อที่ระบุผลลัพธ์นี้3
LLO5.4มาตรฐานโค้ด ตรวจจากโค้ดทุกข้อที่ส่ง1

ใบงานบทที่ 1–13 รวมกันคิดเป็น 15% ของคะแนนรายวิชา ชิ้นละเท่ากัน ส่งช้าหัก 10% ของคะแนนงานต่อสัปดาห์ที่ช้า สูงสุด 50% และรับงานถึงสิ้นสัปดาห์ที่ 15 งานที่ผู้สอนส่งกลับให้แก้นับเวลาจากการส่งครั้งแรก ดูคะแนนของตนเองได้ที่หน้า “คะแนนของฉัน” ในห้องเรียนออนไลน์ของรายวิชา

เกณฑ์ให้คะแนน

งานข้อ 1 และงานข้อ 3 ใช้เกณฑ์ผ่าน/ไม่ผ่าน:

เกณฑ์ผ่านยังไม่ผ่าน
งานข้อ 1ผลรวมและแฟกทอเรียลถูกต้องทุกค่าทดสอบ และไม่มีลูปไม่รู้จบผลไม่ตรงบางค่า หรือเกิดลูปไม่รู้จบ
งานข้อ 3continue/break ทำงานตามเจตนา ผลลัพธ์ตรง และคอมไพล์ผ่านไม่มีการแจ้งเตือนผลไม่ตรง หรือใช้ break/continue ผิดเจตนา

งานข้อ 2 ใช้รายการตรวจ (checklist): เลือกชนิดลูปเหมาะกับลักษณะโจทย์, ตั้งค่าเริ่มต้นตัวสะสมถูก (0 สำหรับบวก, 1 สำหรับคูณ), ปรับตัวนับครบทุกรอบ, จัดย่อหน้า 4 ช่องว่างและมีคอมเมนต์ส่วนหัว

เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ

บทนำ

โปรแกรมจำนวนมากต้องทำงานเดิมซ้ำหลายครั้ง เช่น การรวมตัวเลขหลายจำนวนหรือการตรวจเงื่อนไขซ้ำจนกว่าจะพบคำตอบ การเขียนคำสั่งเดิมซ้ำ ๆ ด้วยมือย่อมยืดยาวและเกิดข้อผิดพลาดง่าย โครงสร้างควบคุมแบบวนซ้ำ (loop) จึงเข้ามาแก้ปัญหานี้ บทที่ 4 ได้แนะนำการเลือกทำงานด้วย if และตัวดำเนินการเปรียบเทียบไปแล้ว บทนี้นำเงื่อนไขแบบเดียวกันมาใช้ควบคุมการทำงานซ้ำด้วยลูป for, while และ do-while พร้อมการใช้ตัวแปรนับและตัวแปรสะสม และการควบคุมการวนซ้ำด้วย break และ continue

ทักษะการวนซ้ำในบทนี้เป็นเครื่องมือพื้นฐานของทุกบทที่ตามมา การวนซ้ำจะถูกใช้ร่วมกับฟังก์ชันในบทที่ 6 และใช้เป็นหัวใจของการประมวลผลอาร์เรย์ในบทที่ 7 ส่วนการออกแบบตัวแปรสะสมและการเลือกชนิดลูปช่วยให้แก้โจทย์ที่ซับซ้อนขึ้นได้อย่างเป็นระบบตลอดรายวิชา

สิ่งที่ได้จากบทนี้ใช้ต่อในบท
ลูป for, while และ do-whileบท 6 (ฟังก์ชัน), บท 7 (อาร์เรย์) เป็นต้นไป
การเลือกชนิดลูปและตัวแปรนับ/สะสมทุกบทที่มีการประมวลผลซ้ำ
การควบคุมการวนซ้ำด้วย break และ continueบท 7 (วนอาร์เรย์) และบทที่ต้องหยุด/ข้ามรอบ

ความรู้พื้นฐาน เครื่องมือ และรุ่นซอฟต์แวร์

บทนี้ต้องใช้คอมไพเลอร์ภาษา C และโปรแกรมแก้ไขข้อความหรือ IDE และต้องมีความรู้เรื่องการเลือกทำงานด้วย if กับตัวดำเนินการเปรียบเทียบจากบทที่ 4 การรับข้อมูลด้วย scanf จากบทที่ 3 และตัวแปรกับตัวดำเนินการจากบทที่ 2 ตารางด้านล่างเป็นรุ่นเครื่องมือที่ใช้ทดสอบโค้ดตัวอย่างในบทนี้จริง

เครื่องมือรุ่นที่ทดสอบหน้าที่
คอมไพเลอร์ gcc13.3.0 (Ubuntu)แปลซอร์สโค้ด .c เป็นไฟล์ปฏิบัติการ
มาตรฐานภาษาC11 (-std=c11)กำหนดไวยากรณ์ภาษา C ที่ใช้อ้างอิง
⚠️Warning

โค้ดตัวอย่างในบทนี้ทดสอบด้วย gcc 13.3.0 บนระบบปฏิบัติการลินุกซ์ โดยการรับข้อมูลทดสอบด้วยการป้อนค่าผ่านทางเข้ามาตรฐาน (standard input) หากใช้คอมไพเลอร์หรือระบบปฏิบัติการอื่น ผลการแสดงข้อความภาษาไทยอาจต่างกันเล็กน้อย แต่ซอร์สโค้ดภาษา C มาตรฐานใช้ได้เหมือนกัน

วิธีใช้บทเรียนและใบงานในหน้านี้

ทบทวน บทที่ 4 โครงสร้างควบคุมแบบทางเลือก โดยอธิบายให้ได้ว่าเงื่อนไขเป็นศูนย์หรือไม่เป็นศูนย์ส่งผลอย่างไร จากนั้นเปลี่ยนคำถามจาก “เลือกทำอะไร” เป็น “ทำอีกครั้งหรือหยุดแล้ว”

ใบงานในหน้านี้เป็นตารางสำหรับคัดลงสมุดหรือเอกสารของตนเอง ไม่ใช่ช่องส่งคำตอบบนเว็บไซต์ ทำตามลำดับ ทำนายผล → ไล่สถานะ → คอมไพล์ → รัน → อธิบายความต่าง โดยเก็บผลที่ทำนายไว้แม้ทายผิด

คะแนนและเกณฑ์ผ่าน 50 ในกรณีศึกษาเป็นข้อมูลสมมติสำหรับฝึกโปรแกรม ไม่ใช่คะแนนนักศึกษาหรือเกณฑ์ประเมินรายวิชา ชุดสอนยังใช้ LLO5.1–5.3 และงานส่งเดิม ส่วนเวลาและแนวคำตอบอยู่ในส่วนผู้สอน

สำหรับการรับจำนวนเต็ม ใช้ค่าที่แทนด้วย int ได้และตรวจจำนวนข้อมูลที่ scanf อ่านสำเร็จ การตรวจผลอ่านไม่ใช่ตัวแปลงเลขทุกขนาด และ 12x อาจอ่านสำเร็จเป็น 12 โดยเหลือ x ไว้ จึงยังไม่ใช่การตรวจทั้งบรรทัด

5.1 แนวคิดการวนซ้ำและลูป for

ระดับที่ 1: การวนซ้ำ (loop) คือการสั่งให้โปรแกรมทำกลุ่มคำสั่งเดิมซ้ำหลายรอบโดยอัตโนมัติ แทนการเขียนคำสั่งเดิมซ้ำด้วยมือ ลูปจะทำงานต่อไปตราบที่เงื่อนไขยังเป็นจริง และหยุดเมื่อเงื่อนไขเป็นเท็จ

ระดับที่ 2: ลูป for เหมาะเมื่อรู้จำนวนรอบล่วงหน้า โครงสร้างประกอบด้วยสามส่วนในวงเล็บ คั่นด้วยเครื่องหมายอัฒภาค ; คือ ค่าเริ่มต้น เงื่อนไขทำซ้ำ และการปรับค่าต่อรอบ ดังรูปแบบ for (ค่าเริ่ม; เงื่อนไข; ปรับค่า) ตัวอย่างการนับจาก 1 ถึง 5

C
for (int i = 1; i <= 5; i++) {
    printf("%d ", i);
}

ตัวแปร i เรียกว่าตัวแปรนับ (counter) เริ่มที่ 1 ตรวจเงื่อนไข i <= 5 ก่อนทำงานแต่ละรอบ และเพิ่มค่าทีละหนึ่งด้วย i++ จากบทที่ 2 จนเงื่อนไขเป็นเท็จจึงออกจากลูป

ระดับที่ 3: กับดักที่พบบ่อยของลูป for คือความคลาดเคลื่อนที่ขอบเขต (off-by-one) เช่น การใช้ i < 5 แทน i <= 5 ทำให้วนน้อยไปหนึ่งรอบ หรือเริ่มตัวนับผิดค่า การตรวจค่าตัวนับในรอบแรกและรอบสุดท้ายทุกครั้งช่วยลดข้อผิดพลาดนี้ได้ นอกจากนี้ตัวแปรนับที่ประกาศภายใน for จะใช้ได้เฉพาะภายในลูปเท่านั้น

คำถามตรวจความเข้าใจ 5.1: ลูป for มีส่วนประกอบสามส่วนอะไรบ้าง และส่วนใดทำหน้าที่หยุดการวนซ้ำ

ใบงาน 1: นับรอบทำงานให้ต่างจากรอบตรวจเงื่อนไข

จากตัวอย่าง for ที่แสดง 1–5 ให้คัดตารางนี้แล้วกรอกก่อนรัน บรรทัดสุดท้ายเป็นการตรวจเงื่อนไขที่ไม่เข้าสู่ตัวลูป จึงไม่ควรนับเป็นรอบที่พิมพ์ข้อมูล

ครั้งที่ตรวจi ก่อนตรวจi <= 5 จริงหรือเท็จพิมพ์อะไรi หลังปรับค่า
11
22
33
44
55
66

ทำอีกครั้งโดยเปลี่ยนเป็น i < 5 และเปลี่ยนจุดเริ่มเป็น 0 ทีละอย่าง บันทึกค่าที่พิมพ์ตัวแรก ตัวสุดท้าย และจำนวนครั้ง แยกคำอธิบายว่าเปลี่ยน “ชุดค่าที่ผ่านเงื่อนไข” หรือเปลี่ยน “จุดเริ่ม”

สำหรับผลรวม 1..n ให้ทำตารางเพิ่มคอลัมน์ sum ก่อนบวกและหลังบวก เมื่อ n เป็น 1, 3 และ 5 อย่าวาง sum = 0 ภายในลูป เพราะต้องเก็บผลจากรอบก่อนหน้าไว้

5.2 ลูป while และ do-while

ระดับที่ 1: ลูป while และ do-while ใช้วนซ้ำตามเงื่อนไขเช่นเดียวกับ for แต่เหมาะกับกรณีที่ยังไม่รู้จำนวนรอบแน่นอน และขึ้นกับเงื่อนไขที่ตรวจระหว่างทาง

ระดับที่ 2: ลูป while (เงื่อนไข) ตรวจเงื่อนไขก่อนทำงานทุกรอบ หากเงื่อนไขเป็นเท็จตั้งแต่แรกจะไม่ทำงานเลยแม้แต่รอบเดียว ส่วนลูป do { } while (เงื่อนไข); ทำงานก่อนอย่างน้อยหนึ่งรอบแล้วจึงตรวจเงื่อนไขท้ายรอบ ความต่างสรุปได้ดังตาราง

ชนิดลูปตรวจเงื่อนไขจำนวนรอบอย่างน้อยเหมาะกับ
whileก่อนทำงานแต่ละรอบ0 รอบงานที่อาจไม่ต้องทำเลยถ้าเงื่อนไขเป็นเท็จแต่แรก
do-whileหลังทำงานแต่ละรอบ1 รอบงานที่ต้องทำอย่างน้อยหนึ่งครั้งก่อนตรวจ เช่น เมนู

ตัวอย่างการนับถอยด้วย while

C
int k = 3;
while (k > 0) {
    printf("%d ", k);
    k--;
}

ระดับที่ 3: ความเสี่ยงสำคัญของ while และ do-while คือลูปไม่รู้จบ (infinite loop) ซึ่งเกิดเมื่อเงื่อนไขไม่มีวันเป็นเท็จ หรือลืมปรับค่าตัวนับภายในลูป เช่น ลืมคำสั่ง k-- ในตัวอย่างข้างต้น ทุกลูปที่อาศัยตัวนับจึงต้องมีคำสั่งปรับค่าที่ทำให้เงื่อนไขเข้าใกล้ความเป็นเท็จในที่สุด

คำถามตรวจความเข้าใจ 5.2: ลูป while กับ do-while ต่างกันอย่างไรเมื่อเงื่อนไขเป็นเท็จตั้งแต่แรก

ใบงาน 2: เงื่อนไขเริ่มต้นเท็จและสิ่งที่ยังต้องทำหนึ่งครั้ง

สร้างไฟล์ loop_counts.c เพื่อเปรียบเทียบจำนวนครั้งที่เข้าตัวลูปด้วยค่าขีดจำกัดเดียวกัน ช่วง 0–3 ใช้เพื่อให้ trace ได้ครบ ไม่ใช่ข้อจำกัดของชนิดลูป

C
/* loop_counts.c : compare pre-test and post-test loops */
#include <stdio.h>
 
int main(void)
{
    int limit;
    if (scanf("%d", &limit) != 1) {
        puts("Input error");
        return 1;
    }
    if (limit < 0 || limit > 3) {
        puts("Range error");
        return 1;
    }
 
    int i = 0;
    int for_count = 0;
    for (i = 0; i < limit; ++i) {
        ++for_count;
    }
 
    i = 0;
    int while_count = 0;
    while (i < limit) {
        ++while_count;
        ++i;
    }
 
    i = 0;
    int do_count = 0;
    do {
        ++do_count;
        ++i;
    } while (i < limit);
 
    printf("for=%d while=%d do=%d\n",
           for_count, while_count, do_count);
    return 0;
}
limitคาดว่า for ทำกี่ครั้งคาดว่า while ทำกี่ครั้งคาดว่า do-while ทำกี่ครั้งผลจริงตรงหรือไม่
0
1
3

รันทีละค่าและชี้ตำแหน่งตรวจเงื่อนไขบนซอร์สโค้ด หากนับเฉพาะจำนวนบรรทัดที่แสดงผลจะตอบไม่ได้ เพราะทั้งสามลูปแสดงผลสรุปร่วมกันเพียงบรรทัดเดียว

กรณีศึกษา: เมนูที่ต้องแสดงก่อนเลือกออก

สร้างไฟล์ repeat_menu.c เมนูมีเพียง 1 เพื่อแสดงข้อความตัวอย่าง และ 0 เพื่อออก หมายเลขอื่นแสดงข้อผิดพลาดแล้วให้เลือกใหม่ ข้อมูลไม่ใช่จำนวนเต็มหรือจบข้อมูลก่อนเลือกออกให้รายงานแล้วจบ

C
/* repeat_menu.c : show a menu at least once */
#include <stdio.h>
 
int main(void)
{
    int choice;
    do {
        puts("1: help, 0: exit");
        if (scanf("%d", &choice) != 1) {
            puts("Input error");
            return 1;
        }
        switch (choice) {
            case 1:
                puts("Practice one step at a time");
                break;
            case 0:
                puts("Bye");
                break;
            default:
                puts("Invalid choice");
                break;
        }
    } while (choice != 0);
    return 0;
}

ทำนายข้อความและจำนวนครั้งที่แสดงเมนูเมื่อป้อน 0, 1 0 และ 9 1 0 แล้วลอง x สังเกตว่า break ใน switch จบการเลือกเมนู ส่วนเงื่อนไขท้าย do-while เป็นผู้ตัดสินว่าจะวนอีกครั้งหรือไม่

หากใส่ break หลัง switch แต่ยังอยู่ในตัวลูป ผลจะต่างกันอย่างไร ให้วาดลูกศรเส้นทางก่อนแก้โค้ด ความหมายของ break ขึ้นกับโครงสร้างที่ครอบอยู่ใกล้ที่สุด ไม่ใช่ชื่อเมนูที่กำลังทำ

5.3 การเลือกชนิดลูปและตัวแปรนับ/สะสม

ระดับที่ 1: การเลือกชนิดลูปพิจารณาจากลักษณะโจทย์ หากรู้จำนวนรอบล่วงหน้าให้ใช้ for หากวนตามเงื่อนไขที่ยังไม่รู้จำนวนรอบให้ใช้ while และหากต้องทำอย่างน้อยหนึ่งรอบก่อนตรวจให้ใช้ do-while

ระดับที่ 2: ภายในลูปมักใช้ตัวแปรสองแบบ คือ ตัวแปรสะสม (accumulator) ที่เก็บผลสะสมทีละรอบ และตัวแปรนับ (counter) ที่นับจำนวนครั้ง รูปแบบที่ใช้บ่อยคือการบวกสะสมด้วย sum += i; และการนับด้วย count++; ซึ่งอาศัยตัวดำเนินการจากบทที่ 2 ตัวอย่างการรวม 1 ถึง n ด้วย for

C
int sum = 0;
for (int i = 1; i <= n; i++) {
    sum += i;
}

ระดับที่ 3: การตั้งค่าเริ่มต้นของตัวแปรสะสมต้องถูกต้องตามการดำเนินการ สำหรับการบวกสะสมให้เริ่มที่ 0 เพราะ 0 เป็นค่าที่ไม่เปลี่ยนผลบวก แต่สำหรับการคูณสะสมต้องเริ่มที่ 1 เพราะการเริ่มที่ 0 จะทำให้ผลคูณเป็น 0 เสมอ การเริ่มค่าตัวสะสมผิดเป็นข้อผิดพลาดที่พบบ่อยเมื่อคำนวณแฟกทอเรียล

คำถามตรวจความเข้าใจ 5.3: โจทย์ "รวมเลข 1 ถึง n" ควรใช้ลูปชนิดใด เพราะเหตุใด และควรตั้งค่าเริ่มต้นของตัวแปรสะสมเป็นเท่าใด

5.4 break และ continue

ระดับที่ 1: คำสั่ง break และ continue ใช้เปลี่ยนการไหลของการวนซ้ำภายในลูป โดย break ใช้ออกจากลูป ส่วน continue ใช้ข้ามไปเริ่มรอบถัดไป

ระดับที่ 2: เมื่อโปรแกรมพบคำสั่ง break ในลูป จะออกจากลูปที่ครอบอยู่ใกล้ที่สุดทันทีและไปทำคำสั่งถัดจากลูปนั้น เหมาะกับการหยุดเมื่อพบคำตอบแล้ว ส่วนคำสั่ง continue จะข้ามคำสั่งที่เหลือในรอบนั้นสำหรับ for จะทำส่วนปรับค่าก่อนตรวจเงื่อนไข ส่วน while และ do-while จะไปตรวจเงื่อนไขของตน เหมาะกับการข้ามบางกรณีที่ไม่ต้องประมวลผล ทั้งสองคำสั่งมักใช้ร่วมกับ if จากบทที่ 4 ดังตัวอย่างการข้ามเลขคี่

C
for (int i = 1; i <= 10; i++) {
    if (i % 2 != 0) {
        continue;
    }
    printf("%d ", i);
}

ระดับที่ 3: break และ continue ช่วยให้โค้ดกระชับขึ้น แต่ควรใช้เท่าที่จำเป็นเพื่อคงความอ่านง่าย การใช้ continue ในลูป while ต้องระวังเป็นพิเศษ เพราะหากข้ามไปก่อนถึงคำสั่งปรับตัวนับ ตัวนับจะไม่ถูกปรับและเกิดลูปไม่รู้จบ การวางคำสั่งปรับตัวนับให้ทำงานทุกรอบจึงสำคัญเมื่อใช้ continue

คำถามตรวจความเข้าใจ 5.4: break กับ continue ต่างกันอย่างไร และเหตุใดการใช้ continue ในลูป while จึงต้องระวังเรื่องตัวนับ

ใบงาน 3: ตามเส้นทาง continue ไปถึงจุดปรับค่า

ใช้ตัวอย่างข้ามเลขคี่ในหัวข้อ 5.4 แล้วกรอกแถว i เท่ากับ 1, 2, 3 และ 4 โดยแยก “เข้าตัวลูป” ออกจาก “พิมพ์ค่า” การข้ามไม่ได้ลดจำนวนครั้งที่เข้าตัวลูปตามขอบเขตเดิม

iข้ามเพราะเป็นเลขคี่หรือไม่พิมพ์หรือไม่ทำส่วน i++ หรือไม่ค่าที่ใช้ตรวจครั้งถัดไป
1
2
3
4

ใน for เส้นทาง continue ยังไปทำส่วนปรับค่า ก่อนกลับไปตรวจเงื่อนไข ส่วน while จะกลับไปตรวจเงื่อนไขทันที และ do-while จะไปตรวจเงื่อนไขท้ายลูป ไม่มีการเพิ่มตัวนับให้อัตโนมัติในสองชนิดหลัง

วิเคราะห์ลูปนับ 1–5 ที่ใช้ while และวาง i++ ท้ายบล็อก ถ้าข้ามเลขคี่ก่อนถึง i++ จะติดอยู่ที่ค่าใด ไม่ต้องรันลูปค้างเพื่อพิสูจน์ ให้ trace สองครั้งที่มีสถานะเดิม แล้วแก้เส้นทางให้ตัวนับเปลี่ยนแน่นอน

5.5 กรณีศึกษาต่อเนื่อง: รับคะแนนหลายครั้งโดยไม่ทำข้อมูลหาย

ลูปไม่ได้มีไว้ย่อข้อความที่เขียนซ้ำเท่านั้น แต่ช่วยรักษากฎเดิมขณะจัดการข้อมูลหลายชิ้น สมมติรับคะแนนสมมติห้าค่าเพื่อหาผลรวม ค่าเฉลี่ย และจำนวนคะแนนที่ถึงเกณฑ์ฝึกหัด 50 เราต้องแยกตัวแปรตามหน้าที่ ได้แก่ตัวนับจำนวนข้อมูลที่รับแล้ว ตัวสะสมผลรวม และตัวนับข้อมูลที่ผ่านเงื่อนไข

5.5.1 ไล่สถานะก่อนอ่าน ระหว่างทำ และหลังจบรอบ

ใช้คะแนน 40, 60, 50, 80 และ 70 โดยเริ่ม sum = 0 และ passed = 0 ในแต่ละรอบอ่านค่าหนึ่ง ตรวจช่วง สะสมผลรวม แล้วเพิ่ม passed เมื่อคะแนนอย่างน้อย 50 การเริ่มตัวสะสมที่ศูนย์หมายถึงยังไม่มีข้อมูลใดถูกรวม ไม่ใช่เพราะทุกตัวแปรต้องเริ่มศูนย์เสมอ

หลังประมวลผลค่าจำนวนที่ประมวลผลsumpassed
ยังไม่มี000
401400
6021001
5031502
8042303
7053004

ตารางบอกความสัมพันธ์ว่า หลังประมวลผลไปจำนวนหนึ่ง sum ต้องเท่าผลรวมของค่าที่รับมาแล้วทั้งหมด ความสัมพันธ์ที่รักษาไว้ในแต่ละรอบนี้ช่วยตรวจโค้ดได้ เช่น ถ้านำ sum = 0; ไปไว้ภายในลูป ผลรวมเก่าจะถูกล้างทุกครั้ง และแถวสุดท้ายอาจเหลือเพียง 70

เมื่อคำนวณเฉลี่ยต้องใช้จำนวนข้อมูลที่นำมารวมจริง ถ้าข้ามคะแนนผิดช่วงด้วย continue แต่ยังหารด้วยห้าทั้งที่รับถูกเพียงสี่ค่า ผลจะไม่ตรงกับความหมายของค่าเฉลี่ย ต้องเลือกกติกาให้ชัดว่าจะปฏิเสธทั้งชุด รับใหม่จนได้ห้าค่าที่ถูก หรือเฉลี่ยเฉพาะค่าที่ผ่าน แล้วใช้ตัวนับให้ตรงกติกานั้น

5.5.2 ตัวอย่างลูปจำนวนรอบแน่นอน

C
#include <stdio.h>
 
int main(void)
{
    int sum = 0;
    int passed = 0;
 
    for (int i = 0; i < 5; ++i) {
        int score;
        if (scanf("%d", &score) != 1) {
            puts("Input error");
            return 1;
        }
        if (score < 0 || score > 100) {
            puts("Range error");
            return 1;
        }
        sum += score;
        if (score >= 50) {
            ++passed;
        }
    }
    printf("sum=%d average=%.2f passed=%d\n",
           sum, sum / 5.0, passed);
    return 0;
}

สำหรับข้อมูลในตาราง ผลคือ sum=300 average=60.00 passed=4 โปรแกรมนี้เลือกหยุดทั้งชุดเมื่อพบข้อมูลผิด ไม่ได้ข้ามแล้วทำเหมือนประมวลผลครบห้าค่า ผู้เรียนควรบอกนโยบายนี้ได้จากเส้นทาง return ไม่ใช่ดูเพียงข้อความผิดพลาด

ใบงาน 4: ข้อมูลถูกห้าค่าหรืออ่านห้าครั้ง

คัดตารางของชุด 40 60 50 80 70 แล้วปิดคอลัมน์ผลในตัวอย่างเดิม ลองคำนวณ sum และ passed ด้วยมืออีกครั้ง จากนั้นใช้ชุด 0 49 50 99 100 เพื่อดูค่าตรงเกณฑ์ ไม่ใช้เฉพาะข้อมูลที่ผ่านทั้งหมด

ชุดข้อมูลจะอ่านครบห้าค่าหรือหยุดก่อนค่าที่ทำให้หยุดผลที่ทำนายผลจริง
40 60 50 80 70
0 49 50 99 100
40 200 60 50 80 70
40 60 x
มีเพียง 40 60 แล้วจบข้อมูล

โปรแกรมเดิมหยุดทั้งชุดเมื่อพบค่าผิดช่วง จึงไม่ใช่ฉบับรับใหม่จนได้ห้าค่าถูกต้อง ในงานขยายหัวข้อ 5.7.2 ให้ใช้ตัวนับที่ยอมรับจริงควบคุมการจบ และแยกจำนวนครั้งที่อ่านเมื่ออยากรายงานการลองป้อน

ก่อนใช้ continue ให้บอกได้ว่ารอบที่มี 200 จะไม่เพิ่มตัวสะสมและตัวนับใด หากเปลี่ยนกติกาแต่ยังเพิ่มตัวนับทุกครั้ง จะจบเร็วทั้งที่มีคะแนนถูกต้องไม่ครบ

5.6 ออกแบบลูปที่ไม่รู้จำนวนข้อมูลล่วงหน้า

5.6.1 sentinel เป็นสัญญาณจบ ไม่ใช่ข้อมูล

ถ้ารับคะแนนจนพบ -1 ค่า -1 เป็นสัญญาณจบ ไม่ต้องสะสมและไม่เพิ่มจำนวนข้อมูล ต้องตรวจสัญญาณจบทันทีหลังอ่านสำเร็จ จากนั้นจึงตรวจว่าคะแนนอยู่ใน 0–100 และค่อยประมวลผล การนำ -1 มารวมก่อนแล้วหักคืนภายหลังทำให้โค้ดผูกกับรายละเอียดมากขึ้นและตรวจยาก

ลอง trace ข้อมูล 40 60 -1 จะมีข้อมูลจริงสองค่า ผลรวม 100 เฉลี่ย 50 ส่วนข้อมูล -1 ทันทีมีข้อมูลจริงศูนย์ค่า จึงต้องรายงานว่าไม่มีข้อมูล ไม่คำนวณ sum / count เพราะตัวหารเป็นศูนย์ การตรวจกรณีว่างเป็นส่วนจำเป็นของอัลกอริทึม ไม่ใช่งานเก็บรายละเอียดหลังเขียนเสร็จ

5.6.2 ทุกเส้นทางต้องทำให้เข้าใกล้จุดจบ

เมื่อใช้ while อ่านข้อมูล ต้องคิดว่าเมื่ออ่านสำเร็จ อ่านผิด และพบสัญญาณจบ เกิดอะไรต่อ หากอ่าน x ด้วย scanf("%d", ...) ไม่สำเร็จแล้ววนกลับทันทีโดยไม่จบหรือจัดการข้อมูลค้าง อักขระเดิมอาจยังอยู่และทำให้ผิดซ้ำไม่สิ้นสุด ในระดับนี้การรายงานแล้วจบเป็นแนวทางที่อธิบายได้ชัด

ระวัง continue ในลูป while ที่เพิ่มตัวนับไว้ท้ายบล็อก เพราะการข้ามอาจข้ามการเพิ่มค่าด้วย ส่วน for จะไปทำส่วนปรับค่าก่อนตรวจเงื่อนไขรอบถัดไป ให้ trace กรณีข้ามเลขคู่จาก 1–5 โดยระบุจุดที่ตัวนับเปลี่ยน ถ้าอธิบายเส้นทางไม่ได้ ยังไม่ควรแก้ด้วยการเพิ่มตัวนับหลายจุดโดยเดา

5.6.3 break จบลูปชั้นใด

ในลูปซ้อนสำหรับตรวจอุปกรณ์สามกล่อง กล่องละสี่ชิ้น break ภายในลูปชิ้นจบเพียงลูปชั้นใน ไม่ได้ออกจากลูปกล่องทั้งหมด หากโจทย์ต้องหยุดงานทั้งชุดเมื่อพบของชำรุด ต้องออกแบบสัญญาณสถานะหรือแยกงานเป็นฟังก์ชันในบทต่อไป ไม่ถือว่าการเขียน break หนึ่งครั้งหมายถึงจบโปรแกรมเสมอ

ตัวอย่างพร้อมรัน: หยุดด้วยสัญญาณและรักษาจำนวนข้อมูลจริง

สร้างไฟล์ sentinel_scores.c รับคะแนนสมมติ 0–100 จนพบ -1 โปรแกรมข้ามจำนวนเต็มผิดช่วงและรับต่อ แต่หยุดเมื่ออ่านไม่สำเร็จ จำกัดจำนวนค่าที่ยอมรับไว้ 100 ค่า จึงสะสมผลรวมไม่เกิน 10000 ซึ่งอยู่ในช่วงขั้นต่ำของ int

C
/* sentinel_scores.c : sentinel, accepted count, and bounded sum */
#include <stdio.h>
 
int main(void)
{
    int count = 0;
    int sum = 0;
 
    while (count < 100) {
        int score;
        if (scanf("%d", &score) != 1) {
            puts("Input error");
            return 1;
        }
        if (score == -1) {
            break;
        }
        if (score < 0 || score > 100) {
            puts("Skipped");
            continue;
        }
        sum += score;
        ++count;
    }
 
    if (count == 0) {
        puts("No data");
    } else {
        printf("count=%d sum=%d average=%.2f\n",
               count, sum, sum / (double)count);
    }
    if (count == 100) {
        puts("Limit reached");
    }
    return 0;
}

ในตัวอย่างนี้อ่านข้อมูลใหม่ที่ต้นทุกรอบ แม้ข้ามค่าผิดช่วง จึงไม่ติดอยู่กับค่าเดิม แต่ถ้ามีค่าผิดช่วงส่งเข้ามาต่อเนื่อง โปรแกรมยังรอค่าที่ถูกหรือสัญญาณจบได้ จำนวนค่าที่ยอมรับไม่ใช่ตัวนับจำนวนครั้งที่ลองทั้งหมด

ข้อมูลที่จะลองประเด็นที่ต้องสังเกตทำนายก่อนรันผลจริง
-1ไม่มีข้อมูลและไม่หารด้วยศูนย์
40 60 -1สัญญาณจบไม่ใช่คะแนน
40 200 60 -1ค่าผิดช่วงไม่นับรวม
0 100 -1ศูนย์เป็นข้อมูลที่ยอมรับ
x หรือจบข้อมูลทันทีอ่านไม่สำเร็จแล้วจบ

เปรียบเทียบกับโปรแกรมคะแนนห้าค่า ระบุสิ่งที่เปลี่ยนสามอย่าง ได้แก่เงื่อนไขจบ กติกาค่าผิดช่วง และตัวหารเฉลี่ย การคัดลูปมาโดยไม่เปลี่ยนตัวหารจาก 5 เป็นจำนวนที่รับจริงยังทำให้ผลผิดได้

5.7 ขอบเขตของการคำนวณซ้ำและเวิร์กช็อปขยาย

5.7.1 แฟกทอเรียลไม่ได้โตช้าเหมือนตัวนับ

ผลรวมของ 1–5 เท่ากับ 15 แต่ผลคูณของ 1–5 เท่ากับ 120 เมื่อเพิ่มจำนวนรอบ ผลคูณโตเร็วมาก จึงไม่ควรให้ตัวอย่างแฟกทอเรียลรับจำนวนใดก็ได้แล้วสะสมใน int การเกิด signed integer overflow ไม่ใช่กฎการวนกลับที่พึ่งพาได้

สำหรับแบบฝึกหัดพกพาระดับต้น สามารถกำหนดรับเพียง 0–8 และเก็บผลใน unsigned long เพราะ 8! เท่ากับ 40320 ซึ่งอยู่ในช่วงขั้นต่ำที่ชนิดนี้ต้องรองรับ ใช้ค่าตั้งต้น 1 เพื่อให้กรณี 0! ได้ 1 เมื่อไม่มีรอบคูณ การเลือก 1 แทน 0 มาจากบทบาทของเอกลักษณ์การคูณ ถ้าเริ่ม 0 ทุกผลคูณจะคงเป็นศูนย์

แหล่งอ่านขยายคือ GNU C: Loop Statements สำหรับโครงสร้างลูป และ GNU C: Integer Arithmetic สำหรับการคิดเรื่องชนิดของการคำนวณ

5.7.2 ทดลองสามงานที่ใช้ลูปต่างกัน

งานแรกคำนวณผลรวมของจำนวน 1–10 ใช้ลูปจำนวนรอบแน่นอนและ trace ทุกแถว งานที่สองรับคะแนนจนพบสัญญาณจบ ใช้ลูปตามเงื่อนไขและทดสอบกรณีไม่มีข้อมูล งานที่สามแสดงเมนูอย่างน้อยหนึ่งครั้งก่อนตัดสินใจวนต่อ ใช้โครงสร้างที่สะท้อนการทำก่อนตรวจหลัง อธิบายว่าทำไมการเลือกชนิดลูปจึงสอดคล้องกับข้อกำหนด

จากนั้นนำโปรแกรมคะแนนห้าค่ามาปรับให้รับใหม่เมื่อพบค่าที่เป็นจำนวนเต็มแต่ผิดช่วง โดยจำนวนค่าที่ถูกต้องยังต้องครบห้า แยกตัวนับ “จำนวนครั้งที่ลองป้อน” กับ “จำนวนคะแนนที่ยอมรับ” และทดลองข้อมูล 40 200 60 50 80 70 จะลองอ่านหกครั้งแต่ใช้คะแนนห้าค่า

บันทึกผลก่อนและหลังการเปลี่ยนกติกา รวมทั้ง trace รอบที่มี 200 ระบุชัดว่ารอบนั้นเปลี่ยนตัวนับใดและไม่เปลี่ยนตัวสะสมใด ผู้เรียนต้องแสดงได้ว่าการเพิ่ม continue ไม่ได้ทำให้เงื่อนไขจบคลาดเคลื่อน และกรณีอ่านไม่สำเร็จยังไม่เกิดลูปไม่สิ้นสุด

ท้ายงานให้สร้างชุดข้อมูลแยกข้อผิดพลาดสามแบบ ได้แก่เริ่มตัวสะสมผิด วนเกินหนึ่งรอบ และหารด้วยจำนวนข้อมูลที่ไม่ตรงกับค่าที่รวม สิ่งที่ส่งควรมีทั้งกรณีทดสอบและเหตุผลที่กรณีนั้นจับความผิดได้ เพื่อเชื่อมไปสู่อาร์เรย์และการทดสอบในบทถัดไป

5.8 ตัวอย่างเปรียบเทียบ: ลูปซ้อนสร้างรายงานจำนวนชุด

ต้องการแสดงตารางว่าสาย 0–5 เส้นจัดได้กี่ชุดเมื่อขนาดชุดเป็น 2 และ 3 เส้น สามารถคิดเป็นแถวของจำนวนสายและคอลัมน์ของขนาดชุด แต่ละช่องคำนวณได้จากข้อมูลแถวกับคอลัมน์ ไม่ต้องสร้างตัวแปรใหม่ให้ทุกช่อง

เริ่ม trace แบบเล็กที่จำนวนสาย 0–2 และขนาดชุด 2–3 ก่อน จะมีการคำนวณหกครั้ง คือ (0,2), (0,3), (1,2), (1,3), (2,2), (2,3) วงรอบนอกเปลี่ยนจำนวนสาย วงรอบในเดินครบขนาดชุด เมื่อจบแถวต้องขึ้นบรรทัดใหม่เพียงครั้งเดียว ตำแหน่งคำสั่งขึ้นบรรทัดจึงเปลี่ยนรูปตารางได้แม้สูตรถูก

จำนวนสายชุดละ 2ชุดละ 3
000
100
210
311
421
521

นับจำนวนการคำนวณในตารางจริงเป็นหกแถวคูณสองคอลัมน์ ได้สิบสองครั้ง หากวงรอบในเริ่มที่ขนาดชุดศูนย์ จะเกิดการหารด้วยศูนย์ในสูตรจัดชุด แสดงว่าขอบเขตลูปไม่ใช่เรื่องจำนวนรอบเท่านั้น แต่ต้องรักษาขอบเขตที่นิพจน์ด้านในยอมรับด้วย

ลองเพิ่มเงื่อนไขให้แสดงเฉพาะกรณีไม่มีของเหลือ โดยใช้ continue อย่างมีเหตุผลแล้วอธิบายว่าข้ามการแสดงช่องหรือข้ามทั้งแถว ผลรูปแบบอาจไม่เป็นตารางสี่เหลี่ยมเหมือนเดิม จึงควรเลือกแสดงเป็นรายการคู่ข้อมูลแทน ไม่ฝืนใช้รูปแบบเดิมโดยไม่คิดผล

ท้ายกิจกรรมให้ผู้เรียนทำนายจำนวนรอบก่อนรัน เปรียบเทียบกับ trace และขยายช่วงข้อมูลทีละน้อย การสังเกตจำนวนงานที่โตตามผลคูณของสองช่วงเป็นฐานสำหรับเรื่องประสิทธิภาพ ไม่ต้องใช้ข้อมูลใหญ่จนเกิด output จำนวนมากเพื่อพิสูจน์สิ่งที่นับได้จากโครงสร้าง

ใบงาน 5: break อยู่ใกล้ลูปใด

สร้างไฟล์ nested_break.c รับหมายเลขชิ้นที่จะหยุดตรวจในแต่ละกล่อง ช่วง 1–4 มีกล่องสามกล่อง โปรแกรมนี้ใช้หมายเลขเดียวกันทุกกล่องเพื่อให้เห็นผลของ break ชัดก่อนเพิ่มเงื่อนไขจริง

C
/* nested_break.c : break only the inner item loop */
#include <stdio.h>
 
int main(void)
{
    int stop_item;
    if (scanf("%d", &stop_item) != 1) {
        puts("Input error");
        return 1;
    }
    if (stop_item < 1 || stop_item > 4) {
        puts("Range error");
        return 1;
    }
 
    for (int box = 1; box <= 3; ++box) {
        printf("box=%d:", box);
        for (int item = 1; item <= 4; ++item) {
            if (item == stop_item) {
                break;
            }
            printf(" %d", item);
        }
        puts("");
    }
    return 0;
}

ทดสอบ 1, 2 และ 4 ก่อนรันให้วาดเส้นครอบลูปกล่องกับลูปชิ้น ระบุว่าพบ break แล้วไปคำสั่งใดต่อ คำสั่งพิมพ์ชิ้นอยู่หลังการตรวจ จึงไม่พิมพ์ชิ้นที่ตรงกับหมายเลขหยุด

หากต้องหยุดทุกกล่องเมื่อพบปัญหา ต้องออกแบบสถานะร่วมกับเงื่อนไขลูปนอก ไม่เพิ่ม break โดยเดาตำแหน่ง ลองเขียนขั้นตอนภาษาไทยก่อน ส่วนการแยกเป็นฟังก์ชันจะต่อยอดในบทที่ 6

5.9 อ่านโค้ดที่ผิดและทำความเข้าใจข้อความวินิจฉัย

ตัวอย่างต่อไปนี้เป็นโค้ดฝึกแยกจากงานที่ต้องส่ง แสดงข้อผิดพลาดที่สัมพันธ์กับเนื้อหาบท พร้อมข้อความวินิจฉัยจริง วิธีอ่าน และฉบับแก้ คำว่า diagnostic ใช้รวมข้อความ error, warning และ note จึงไม่ควรเรียกทุกบรรทัดว่า error หรือถือว่าการไม่มี error แปลว่าโปรแกรมถูกตามโจทย์

ที่มาของ log: ผู้เขียนทดสอบด้วย GCC 11.4.0 บน Ubuntu ผ่าน Linux environment เมื่อ 24 ตุลาคม 2569 ใช้ C11 และตัวเลือกดังนี้ ชื่อไฟล์ เลขบรรทัด คอลัมน์ ถ้อยคำ และคำเตือนที่เปิดใช้อาจต่างจากแพลตฟอร์มการเรียน นักศึกษาไม่จำเป็นต้องเปลี่ยนเครื่องมือหรือการติดตั้งตามชุดทดสอบของผู้เขียน

TEXT
-std=c11 -O0 -Wall -Wextra -Wpedantic -Wformat=2 -fdiagnostics-color=never -fmessage-length=0

ข้อความรูปแบบ bad.c:5:17 อ่านว่าไฟล์ bad.c บรรทัด 5 คอลัมน์ 17 ส่วน ^ และ ~ ช่วยชี้บริเวณที่ตรวจพบ จุดนี้อาจเป็นปลายเหตุจากบรรทัดก่อนหน้า error ทำให้การสร้างโปรแกรมล้มในกรณีนั้น warning เตือนความเสี่ยงแม้คำสั่งสร้างอาจจบสำเร็จ และ note ให้บริบท เช่น declaration เดิม ต้องอ่านคู่กัน (GCC: Diagnostics)

ตัวอย่างที่ผิด 1: ใช้ตัวนับนอกขอบเขตของ for

ประเภท: ข้อผิดพลาดขณะคอมไพล์

ตัวแปร i ที่ประกาศในส่วนต้นของ for มีขอบเขตสำหรับลูปนั้น การนำมาใช้หลังจบลูปจึงไม่พบ declaration ที่ใช้งานได้ กรณีนี้ต่างจากตัวนับมีค่าไม่ถูกต้อง เพราะ compiler ยังไม่ยอมรับการอ้างชื่อนี้

โค้ดที่ผิด — ไม่ใช้แทนฉบับแก้

C
#include <stdio.h>
int main(void)
{
    for (int i = 0; i < 3; ++i) {
        printf("item=%d\n", i);
    }
    printf("last=%d\n", i);
    return 0;
}

คำสั่งสร้างและข้อความที่ได้จริง

TEXT
gcc -std=c11 -O0 -Wall -Wextra -Wpedantic -Wformat=2 -fdiagnostics-color=never -fmessage-length=0 bad.c -o bad
TEXT
bad.c: In function 'main':
bad.c:7:25: error: 'i' undeclared (first use in this function)
    7 |     printf("last=%d\n", i);
      |                         ^
bad.c:7:25: note: each undeclared identifier is reported only once for each function it appears in

สถานะจบของคำสั่งสร้างที่ตัวเก็บผลอ่านได้: 1 คำสั่งล้มและไม่ได้รัน executable ของตัวอย่างนี้ การรันไฟล์เก่าที่อาจค้างจาก build ครั้งก่อนจะไม่ใช่หลักฐานของโค้ดปัจจุบัน

อ่านข้อความว่าแจ้งอะไรและผิดอย่างไร

undeclared ที่บรรทัด printf หลังลูปไม่ได้แปลว่าไม่เคยมีชื่อ i ในไฟล์ แต่หมายถึงไม่เห็น declaration ในขอบเขตนี้ ต้องดูวงเล็บและจุดประกาศ ไม่ย้ายทุกตัวแปรเป็น global เพื่อทำให้ใช้ได้ทุกแห่ง

โค้ดที่แก้แล้ว

C
#include <stdio.h>
int main(void)
{
    for (int i = 0; i < 3; ++i) {
        printf("item=%d\n", i);
    }
    return 0;
}

เหตุผลของการแก้และสิ่งที่ต้องตรวจซ้ำ

ถ้าโจทย์ต้องแสดงแต่รายการ ให้ลบการอ่านนอกลูปดังฉบับแก้ หากต้องใช้จำนวนที่ประมวลผลต่อจริง ให้ประกาศตัวแปร count ที่สื่อบทบาทและขอบเขตเหมาะสม ไม่อาศัยชื่อ i เป็นคำตอบทุกงาน

ผลตรวจฉบับแก้จริง: คอมไพล์ด้วยตัวเลือกเดิมและเพิ่ม -Werror เพื่อให้ชุดทดสอบผู้เขียนไม่ยอมรับ warning ไม่มีข้อความวินิจฉัยและคำสั่งสร้างจบด้วยสถานะ 0 จากนั้นรันได้ stdout ดังนี้ และ stderr ว่าง

TEXT
item=0
item=1
item=2

ตัวอย่างที่ผิด 2: ลูปรันเกินจำนวนที่ต้องการหนึ่งรอบ

ประเภท: ข้อผิดพลาดเชิงตรรกะที่ compiler อาจไม่เตือน

ต้องการทำงานสามรอบโดยเริ่มตัวนับที่ 0 แต่ <= ยอมให้ i เท่ากับ 3 ด้วย จึงทำงานที่ 0, 1, 2 และ 3 รวมสี่รอบ ตัวอย่างนี้นับรอบอย่างเดียวจึงไม่อ่านอาร์เรย์เกินขอบเขตและสามารถรันดูผลได้อย่างกำหนดแน่นอน

โค้ดที่ผิด — ไม่ใช้แทนฉบับแก้

C
#include <stdio.h>
int main(void)
{
    int rounds = 0;
    int count = 3;
    for (int i = 0; i <= count; ++i) {
        ++rounds;
    }
    printf("rounds=%d\n", rounds);
    return 0;
}

คำสั่งสร้างและข้อความที่ได้จริง

TEXT
gcc -std=c11 -O0 -Wall -Wextra -Wpedantic -Wformat=2 -fdiagnostics-color=never -fmessage-length=0 bad.c -o bad

ไม่มีข้อความวินิจฉัยจาก compiler ในการทดลองนี้ ทั้ง stdout และ stderr ว่าง การไม่มีข้อความเป็นผลที่สังเกต ไม่ใช่การรับรองความถูกต้องของโปรแกรม

สถานะจบของคำสั่งสร้างที่ตัวเก็บผลอ่านได้: 0 ขั้นสร้างสำเร็จ จากนั้นจึงตรวจพฤติกรรมขณะรันหรือใช้ชุดทดสอบตามประเภทข้างต้น

ผลจากการรันฉบับผิด

stdout:

TEXT
rounds=4

stderr ว่าง ไม่มี runtime diagnostic จากระบบในการทดลองนี้ ต้องใช้การเทียบผลกับข้อกำหนดเป็นหลักฐาน

สถานะจบที่ตัวเก็บผลอ่านได้: 0

ผลตามข้อกำหนดที่ใช้เทียบ:

TEXT
rounds=3

อ่านข้อความว่าแจ้งอะไรและผิดอย่างไร

ไม่มี compiler diagnostic ไม่ได้แปลว่าจำนวนรอบถูก ใช้ตาราง i ก่อนตรวจเงื่อนไข ผลเงื่อนไข และ rounds หลังทำงาน เพื่ออธิบายว่ารอบเกินมาจากจุดใด ไม่ตีความ exit code 0 เป็นการยืนยันข้อกำหนด

โค้ดที่แก้แล้ว

C
#include <stdio.h>
int main(void)
{
    int rounds = 0;
    int count = 3;
    for (int i = 0; i < count; ++i) {
        ++rounds;
    }
    printf("rounds=%d\n", rounds);
    return 0;
}

เหตุผลของการแก้และสิ่งที่ต้องตรวจซ้ำ

เปลี่ยนเป็น i < count ตามช่วง 0 ถึง count-1 ทดสอบ count เป็น 0, 1 และ 3 เพื่อไม่ให้การแก้ตอบได้เฉพาะกรณีเดียว และเชื่อมกับขอบเขตดัชนีที่จะเรียนในบทอาร์เรย์

ผลตรวจฉบับแก้จริง: คอมไพล์ด้วยตัวเลือกเดิมและเพิ่ม -Werror เพื่อให้ชุดทดสอบผู้เขียนไม่ยอมรับ warning ไม่มีข้อความวินิจฉัยและคำสั่งสร้างจบด้วยสถานะ 0 จากนั้นรันได้ stdout ดังนี้ และ stderr ว่าง

TEXT
rounds=3

ลำดับการอ่าน log เมื่อลองโปรแกรมของบทนี้

  1. ระบุว่าเกิดในขั้น compile, link, run หรือ test และแยกข้อความจากระบบออกจากข้อความที่โปรแกรมพิมพ์เอง
  2. อ่าน diagnostic แรกกับบริบทบรรทัดก่อนหน้า แล้วใช้ note เพื่อเทียบ declaration หรือชนิดข้อมูล อย่าแก้ทุกข้อความตามลำดับโดยไม่หาสาเหตุร่วม
  3. แก้ทีละสาเหตุ สร้างโปรแกรมใหม่ และตรวจว่ากำลังรันผล build ใหม่ การเติมวงเล็บ ปิด warning หรือลบ test อาจทำให้ log เงียบโดยยังผิดโจทย์
  4. ใช้ข้อมูลเดิมที่ทำให้ผิดทดสอบซ้ำ แล้วเพิ่มกรณีขอบเขตที่สัมพันธ์กับบท การรันจบสถานะศูนย์เพียงครั้งเดียวไม่แทนการตรวจข้อกำหนดครบถ้วน

ความหมายตามภาษาอ้างจากร่างมาตรฐาน C11 โดยข้อความ diagnostic เฉพาะถ้อยคำเป็นของเครื่องมือที่ใช้ ไม่ได้กำหนดให้ compiler ทุกตัวพิมพ์เหมือนกัน (WG14 N1570, GCC: Warning Options)

ปฏิบัติการที่ 5 โครงสร้างควบคุมแบบวนซ้ำ

ปฏิบัติการนี้แบ่งเป็นสองส่วน ส่วนแรกฝึกเขียนลูป for และ while เพื่อคำนวณผลรวมและแฟกทอเรียลพร้อมออกแบบตัวแปรสะสม ส่วนที่สองฝึกควบคุมการวนซ้ำด้วย continue และ break ทั้งสองส่วนทำแยกกันได้ แต่ควรทำตามลำดับเพื่อให้เห็นการใช้ลูปตั้งแต่พื้นฐานไปจนถึงการควบคุมการไหลของลูป

ℹ️Note

ไฟล์ซอร์สโค้ดในปฏิบัติการนี้ให้ตั้งชื่อเป็นภาษาอังกฤษแบบ snake_case และบันทึกไว้ในโฟลเดอร์ของผู้เรียนเอง คำสั่งคอมไพล์ในเอกสารนี้ทดสอบด้วย gcc 13.3.0 โดยการรับข้อมูลทดสอบด้วยการป้อนค่าผ่านทางเข้ามาตรฐาน

Lab A: ผลรวมและแฟกทอเรียล

สถานการณ์ ต้องการรับจำนวนเต็มบวก n ในช่วง 1–10 เพื่อให้ผลคูณอยู่ในช่วง long ที่ C11 รับประกัน แล้วคำนวณผลรวมของจำนวน 1 ถึง n ด้วยลูป for และคำนวณแฟกทอเรียล n! ด้วยลูป while เพื่อฝึกออกแบบตัวแปรสะสมและเลือกชนิดลูป

เป้าหมาย ฝึก LLO5.1 และ LLO5.2 คือเขียนลูป for และ while และออกแบบตัวแปรสะสม/ตัวนับให้ถูกต้อง

ขั้นปฏิบัติ

  1. สร้างไฟล์ชื่อ ch5_sum.c ด้วยโปรแกรมแก้ไขข้อความหรือ IDE
  2. พิมพ์ซอร์สโค้ดต่อไปนี้ โดยจัดย่อหน้า 4 ช่องว่างและใส่คอมเมนต์ส่วนหัว
C
/* ch5_sum.c : ผลรวมและผลคูณสะสมด้วย for และ while */
#include <stdio.h>
 
int main(void) {
    int n;
    printf("ป้อนจำนวนเต็มบวก n: ");
    if (scanf("%d", &n) != 1 || n < 1 || n > 10) {
        puts("Enter an integer from 1 to 10");
        return 1;
    }
 
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    printf("ผลรวม 1..%d = %d\n", n, sum);
 
    long factorial = 1;
    int k = 1;
    while (k <= n) {
        factorial *= k;
        k++;
    }
    printf("%d! = %ld\n", n, factorial);
    return 0;
}
  1. คอมไพล์และรันด้วยคำสั่ง แล้วทดสอบด้วยค่า n = 5 และ n = 10
BASH
gcc -std=c11 -Wall ch5_sum.c -o ch5_sum
./ch5_sum

ผลการรันจริง (ทดสอบแล้วด้วย gcc 13.3.0 ป้อนค่า 5 และ 10)

TEXT
ป้อนจำนวนเต็มบวก n: ผลรวม 1..5 = 15
5! = 120
ป้อนจำนวนเต็มบวก n: ผลรวม 1..10 = 55
10! = 3628800

สังเกตว่าตัวแปรสะสม sum เริ่มที่ 0 เพราะเป็นการบวก ส่วนตัวแปร factorial เริ่มที่ 1 เพราะเป็นการคูณ หากเริ่ม factorial ที่ 0 ผลคูณจะเป็น 0 เสมอ ลูป for ใช้คำนวณผลรวมเพราะรู้จำนวนรอบ (1 ถึง n) ส่วนลูป while ใช้คำนวณแฟกทอเรียลเพื่อฝึกการตรวจเงื่อนไขก่อนทำและการปรับตัวนับด้วย k++

ตัดสินใจเอง A.1 ต่อยอดโปรแกรมโดยเพิ่มการนับจำนวนเลขคู่ในช่วง 1 ถึง n ด้วยลูปเดียว โดยใช้ตัวแปรนับและเงื่อนไข % 2 == 0 จากบทที่ 4 แล้วแสดงจำนวนเลขคู่ที่นับได้ โดยเลือกชนิดลูปและตั้งชื่อตัวแปรให้เหมาะสม

ผลลัพธ์ที่คาดหวัง A.1 โปรแกรมคอมไพล์ผ่านไม่มีการแจ้งเตือน และแสดงจำนวนเลขคู่ในช่วง 1 ถึง n ได้ถูกต้อง เช่น เมื่อ n = 10 ควรนับได้ 5 จำนวน (ผลที่คาดว่าจะเห็นสำหรับโปรแกรมที่ผู้เรียนออกแบบเอง ยังไม่ได้ทดสอบในเอกสารนี้ เพราะขึ้นกับการออกแบบของแต่ละคน)

จุดตรวจ A ผลรวมและแฟกทอเรียลถูกต้องทุกค่าทดสอบ ตั้งค่าเริ่มต้นตัวสะสมถูก (0 สำหรับบวก, 1 สำหรับคูณ) ปรับตัวนับครบทุกรอบ ไม่มีลูปไม่รู้จบ และโค้ดมีการจัดย่อหน้าและคอมเมนต์ส่วนหัว

ปัญหาที่พบบ่อย

อาการสาเหตุวิธีแก้
แฟกทอเรียลได้ 0 เสมอตั้งค่าเริ่มต้น factorial เป็น 0เปลี่ยนค่าเริ่มต้นเป็น 1 สำหรับการคูณสะสม
ผลรวมขาดหรือเกินหนึ่งจำนวนใช้เงื่อนไขขอบเขตผิด เช่น i < n แทน i <= nตรวจค่าตัวนับในรอบแรกและรอบสุดท้ายให้ตรงช่วง
โปรแกรมค้างไม่หยุดลืมปรับตัวนับ k++ ในลูป whileเพิ่มคำสั่งปรับตัวนับให้ทำงานทุกรอบ

เปิดหลังผู้เรียนออกแบบด้วยตนเองแล้ว

ใบงานตรวจ Lab A: ขอบเขตและเอกลักษณ์ของตัวสะสม

ทดสอบ n ตั้งแต่ 1 ถึง 10 และเลือก -1, 0, 11, x กับการจบข้อมูลเป็นกรณีปฏิเสธ ตรวจแยกว่าจบด้วยข้อความผิดพลาดหรือได้ผลคำนวณ การยอมรับค่า 0 เป็นการเปลี่ยนข้อกำหนดของ Lab A ไม่ใช่การแก้ให้ตรงโจทย์เดิม

n ที่ใช้ผลรวมที่ทำนายแฟกทอเรียลที่ทำนายจำนวนรอบบวก/คูณผลจริง
1
3
5
10

หัวข้อ 5.7 เสนอแบบฝึกต่างหากที่ยอมรับ 0–8 จึงพูดถึง 0! ได้ ส่วน Lab A ยังคงรับ 1–10 และเก็บใน long อย่าเปลี่ยนช่วงข้อมูลโดยไม่บันทึกว่ากำลังทำฉบับใด

งาน A.1 ให้ทดสอบจำนวนเลขคู่เมื่อ n เป็น 1, 2, 3, 9 และ 10 เพื่อครอบคลุมทั้งปลายช่วงคี่และคู่ ส่วนงานรวมเลขคู่ท้ายบทต้องรวม “ค่าเลขคู่” ไม่ใช่นับ “จำนวนเลขคู่” ใช้ผลต่างสองอย่างนี้ตรวจว่าตัวแปรทำหน้าที่ตรงโจทย์

Lab B: break และ continue

สถานการณ์ ต้องการแสดงเลขคู่ในช่วง 1 ถึง 10 โดยใช้ continue ข้ามเลขคี่ และหาตัวหารแรกของจำนวนที่กำหนดโดยใช้ break หยุดลูปทันทีที่พบ เพื่อฝึกควบคุมการไหลของการวนซ้ำ

เป้าหมาย ฝึก LLO5.3 คือใช้ continue ข้ามกรณี และ break หยุดลูปตามเงื่อนไขได้ถูกต้อง

ขั้นปฏิบัติ

  1. สร้างไฟล์ชื่อ ch5_loops.c ด้วยโปรแกรมแก้ไขข้อความหรือ IDE
  2. พิมพ์ซอร์สโค้ดต่อไปนี้ โดยจัดย่อหน้า 4 ช่องว่างและใส่คอมเมนต์ส่วนหัว
C
/* ch5_loops.c : break และ continue */
#include <stdio.h>
 
int main(void) {
    int count = 0;
    for (int i = 1; i <= 10; i++) {
        if (i % 2 != 0) {
            continue;
        }
        count++;
        printf("%d ", i);
    }
    printf("\nมีเลขคู่ %d จำนวน\n", count);
 
    int num = 15;
    for (int d = 2; d <= num; d++) {
        if (num % d == 0) {
            printf("ตัวหารแรกของ %d คือ %d\n", num, d);
            break;
        }
    }
    return 0;
}
  1. คอมไพล์และรันด้วยคำสั่ง
BASH
gcc -std=c11 -Wall ch5_loops.c -o ch5_loops
./ch5_loops

ผลการรันจริง (ทดสอบแล้วด้วย gcc 13.3.0)

TEXT
2 4 6 8 10 
มีเลขคู่ 5 จำนวน
ตัวหารแรกของ 15 คือ 3

ลูปแรกใช้ continue ข้ามเลขคี่ จึงแสดงและนับเฉพาะเลขคู่ ส่วนลูปที่สองใช้ break หยุดทันทีที่พบตัวหารแรกของ 15 คือ 3 โดยไม่วนต่อจนจบ หากไม่ใช้ break ลูปจะวนต่อและพบตัวหารอื่นอีก การใช้ break จึงช่วยหยุดเมื่อได้คำตอบที่ต้องการแล้ว

ตัดสินใจเอง B.1 ดัดแปลงลูปที่สองให้รับจำนวนเต็มบวกด้วย scanf แล้วตรวจว่าเป็นจำนวนเฉพาะหรือไม่ โดยวนหาตัวหารตั้งแต่ 2 ถึงค่าก่อนตัวมันเอง หากพบตัวหารให้ใช้ break แล้วสรุปว่าไม่ใช่จำนวนเฉพาะ หากวนครบโดยไม่พบตัวหารให้สรุปว่าเป็นจำนวนเฉพาะ

ผลลัพธ์ที่คาดหวัง B.1 โปรแกรมคอมไพล์ผ่านไม่มีการแจ้งเตือน และสรุปได้ถูกต้องว่าจำนวนที่รับเข้าเป็นจำนวนเฉพาะหรือไม่ เช่น 15 ไม่ใช่จำนวนเฉพาะ (มีตัวหาร 3) ส่วน 7 เป็นจำนวนเฉพาะ (ผลที่คาดว่าจะเห็นสำหรับโปรแกรมที่ผู้เรียนออกแบบเอง ยังไม่ได้ทดสอบในเอกสารนี้ เพราะขึ้นกับการออกแบบของแต่ละคน)

จุดตรวจ B continue ข้ามกรณีที่กำหนดได้ถูกต้อง break หยุดลูปตามเงื่อนไขได้ถูกต้อง ผลลัพธ์ตรงโจทย์ คอมไพล์ผ่านไม่มีการแจ้งเตือน และโค้ดมีการจัดย่อหน้าและคอมเมนต์ส่วนหัว

ปัญหาที่พบบ่อย

อาการสาเหตุวิธีแก้
โปรแกรมค้างเมื่อใช้ continue ในลูป whileข้ามไปก่อนถึงคำสั่งปรับตัวนับ จึงเกิดลูปไม่รู้จบวางคำสั่งปรับตัวนับให้ทำงานทุกรอบก่อน continue
ผลลัพธ์กลับด้านระหว่างข้ามกับหยุดสับสนหน้าที่ของ break กับ continueทบทวนว่า break ออกจากลูป ส่วน continue ข้ามรอบ
หาตัวหารแล้วยังวนต่อลืมใส่ break เมื่อพบตัวหารเพิ่ม break ภายใน if ที่ตรวจพบตัวหาร

เปิดหลังผู้เรียนส่งงานแล้ว

ใบงานตรวจ Lab B: พบตัวหารกับตัดสินจำนวนเฉพาะเป็นคนละขั้น

ในโค้ดตั้งต้น num = 15 ให้บันทึกค่าของ d ที่ลองและค่าที่ทำให้จบ แล้วทดลองเปลี่ยนเป็น 2, 7 และ 9 เพื่อเข้าใจเส้นทาง หาตัวหารแรกที่รวมตัวเลขเองยังไม่ใช่การตัดสินจำนวนเฉพาะโดยตรง

จำนวนในงาน B.1คำถามก่อนเขียนเงื่อนไข
1จำนวนที่เล็กกว่า 2 จัดอย่างไร
2ถ้าไม่เข้าสู่ลูปหาตัวหาร จะสรุปจากอะไร
7ลองแล้วไม่พบตัวหาร ต้องเก็บสถานะอย่างไร
9เมื่อพบตัวหารแล้วหยุดที่ใด
15การตรวจต้องมีทั้งเลขคู่และเลขคี่หรือไม่

จำนวนเฉพาะคือจำนวนเต็มมากกว่า 1 ที่มีตัวหารบวกเพียง 1 และตัวมันเอง สำหรับการลองในห้องใช้ช่วง 1–100 และตรวจช่วงก่อนวน ค่า 1 จึงไม่ใช่จำนวนเฉพาะ แม้ลูปหาตัวหารจะไม่ได้ทำงาน

เขียนเหตุผลเลือก break พร้อมแยกผล “ไม่พบตัวหาร” ออกจาก “ไม่ได้ตรวจเพราะอยู่นอกโดเมน” สำหรับงานท้ายบทที่ข้ามพหุคูณของ 3 และหยุดหลัง 15 ให้ trace ค่า 15 กับ 16 เพื่อทดสอบขอบเขตโดยตรง

เก็บกวาด

เมื่อจบปฏิบัติการ ให้ลบไฟล์ปฏิบัติการที่สร้างจากการคอมไพล์ (เช่น ch5_sum และ ch5_loops) และเก็บเฉพาะไฟล์ซอร์สโค้ด .c ไว้ เพื่อให้โฟลเดอร์งานสะอาดและส่งไฟล์ซอร์สโค้ด .c พร้อมบันทึกผลการรันตามที่กำหนด

ตรวจความเข้าใจก่อนเริ่มบทที่ 6

ตอบโดยอ้างอิงสถานะตัวแปรหรือเส้นทางคำสั่ง ไม่ใช้เพียงคำว่า “จำรูปแบบได้”

  1. เหตุใดลูปพิมพ์ 1–5 จึงตรวจเงื่อนไขมากกว่าจำนวนรอบที่พิมพ์
  2. เมื่อตั้ง limit เป็น 0 เหตุใด do-while ยังเพิ่มตัวนับได้หนึ่งครั้ง
  3. continue ใน for ยังทำส่วนใด และต่างจาก while อย่างไร
  4. ถ้าชุดข้อมูลเริ่มที่ -1 จะหลีกเลี่ยงการหารด้วยศูนย์ตรงไหน
  5. เหตุใดเริ่มผลคูณที่ 0 จึงไม่ใช่ข้อผิดพลาดแบบเดียวกับลืมเพิ่มตัวนับ
  6. break ใน switch ที่อยู่ในเมนูแบบวนซ้ำทำให้จบโปรแกรมเสมอหรือไม่

เมื่ออธิบายได้ ให้เลือกงานผลรวมและงานตัดสินจำนวนเฉพาะมาเขียนเป็นกลุ่มขั้นตอนที่มีข้อมูลเข้าและผลลัพธ์ชัดเจน เพื่อเตรียมแยกงานเป็นฟังก์ชันใน บทที่ 6 ฟังก์ชัน

สรุป

บทนี้ต่อยอดโครงสร้างควบคุมแบบทางเลือกจากบทที่ 4 ไปสู่การทำงานซ้ำด้วยลูปสามชนิด เริ่มจากลูป for ที่เหมาะเมื่อรู้จำนวนรอบ พร้อมส่วนประกอบสามส่วน (ค่าเริ่ม เงื่อนไข ปรับค่า) และกับดักความคลาดเคลื่อนที่ขอบเขต ต่อด้วยลูป while ที่ตรวจเงื่อนไขก่อนทำและ do-while ที่ทำก่อนอย่างน้อยหนึ่งรอบ รวมถึงความเสี่ยงลูปไม่รู้จบเมื่อลืมปรับตัวนับ จากนั้นอธิบายการเลือกชนิดลูปตามลักษณะโจทย์ การใช้ตัวแปรสะสม sum += i; และตัวนับ count++; และการตั้งค่าเริ่มต้นตัวสะสมให้ถูก (0 สำหรับบวก, 1 สำหรับคูณ) สุดท้ายคือการควบคุมการวนซ้ำด้วย break ที่ออกจากลูป และ continue ที่ข้ามไปรอบถัดไป ทักษะเหล่านี้เป็นพื้นฐานของการประมวลผลซ้ำในบทถัดไป

สิ่งที่ส่งต่อไปยังบทถัดไป

สิ่งที่ส่งต่อค่า/รูปแบบใช้ในบท
ลูป for, while และ do-whilefor (เริ่ม; เงื่อนไข; ปรับค่า), while, do-whileบท 6 (ฟังก์ชัน), บท 7 (อาร์เรย์) เป็นต้นไป
ตัวแปรนับและตัวแปรสะสมcount++;, sum += i;, เริ่ม 0 สำหรับบวก / 1 สำหรับคูณทุกบทที่มีการประมวลผลซ้ำ
การควบคุมการวนซ้ำbreak ออกจากลูป, continue ข้ามรอบบท 7 (วนอาร์เรย์) เป็นต้นไป

สรุปคำสั่ง

คำสั่งหน้าที่
gcc -std=c11 -Wall ch5_sum.c -o ch5_sumคอมไพล์ซอร์สโค้ด ch5_sum.c เป็นไฟล์ปฏิบัติการ ch5_sum พร้อมเปิดการแจ้งเตือน
gcc -std=c11 -Wall ch5_loops.c -o ch5_loopsคอมไพล์ซอร์สโค้ด ch5_loops.c เป็นไฟล์ปฏิบัติการ ch5_loops พร้อมเปิดการแจ้งเตือน
./ch5_sum หรือ ./ch5_loopsรันไฟล์ปฏิบัติการที่คอมไพล์แล้ว

แบบฝึกหัดท้ายบท

ตอนที่ 1 ฝึกทบทวน (15 ข้อ ตรวจคำตอบให้ทันที)

ตอนที่ 2 แบบฝึกหัดเขียนตอบ (5 ข้อ)

กลุ่ม ก (เข้าใจ)

  • ก1 (LLO5.1) จงอธิบายส่วนประกอบสามส่วนของลูป for พร้อมระบุหน้าที่ของแต่ละส่วน
  • ก2 (LLO5.1) จงอธิบายความแตกต่างระหว่างลูป while กับ do-while เมื่อเงื่อนไขเป็นเท็จตั้งแต่แรก

กลุ่ม ข (ประยุกต์)

  • ข1 (LLO5.2) จงเขียนโปรแกรมภาษา C ที่รับจำนวนเต็มบวก n ด้วย scanf แล้วใช้ลูปคำนวณผลรวมของเลขคู่ในช่วง 1 ถึง n พร้อมตั้งค่าเริ่มต้นตัวสะสมให้ถูกต้อง แล้วคอมไพล์และรันให้ได้ผลจริง
  • ข2 (LLO5.3) จงเขียนโปรแกรมที่วนแสดงจำนวน 1 ถึง 20 แต่ข้ามจำนวนที่หารด้วย 3 ลงตัวด้วย continue และหยุดลูปด้วย break เมื่อถึงจำนวนที่มากกว่า 15

กลุ่ม ค (วิเคราะห์)

  • ค1 (LLO5.2) จากโปรแกรมคำนวณแฟกทอเรียลที่ตั้งค่าเริ่มต้นตัวสะสมเป็น 0 และลูป while ที่ลืมคำสั่งปรับตัวนับ จงวิเคราะห์ว่าเกิดข้อผิดพลาดใดได้บ้าง และเสนอวิธีแก้ให้ถูกต้อง

การศึกษาค้นคว้าเพิ่มเติม

  1. ศึกษาโครงสร้างควบคุมแบบวนซ้ำ for, while และ do-while เพิ่มเติมจาก Kernighan และ Ritchie (1988) บทที่ 3 ว่าด้วยการควบคุมการไหล แล้วทดลองแปลงลูป for เป็น while ที่ให้ผลเท่ากัน
  2. ทบทวนความหมายของคำสั่ง break และ continue ในมาตรฐานภาษา C ตาม ISO/IEC 9899 เพื่อเข้าใจขอบเขตการทำงานของทั้งสองคำสั่งภายในลูป
  3. ทดลองออกแบบโปรแกรมที่ใช้ตัวแปรสะสมหลายตัวในลูปเดียว เช่น นับทั้งจำนวนเลขคู่และผลรวม แล้วเปรียบเทียบความกระชับกับการวนหลายลูป

เอกสารอ้างอิง

International Organization for Standardization. (2011). ISO/IEC 9899:2011 — Information technology — Programming languages — C. ISO.

Kernighan, B. W., & Ritchie, D. M. (1988). The C programming language (2nd ed.). Prentice Hall.

ตำราหลักภาษาไทยของรายวิชา. (ผู้สอนเติมรายละเอียดบรรณานุกรม — เพิ่มตามมติผู้สอน 21 ต.ค. 2569).

แหล่งปฐมภูมิที่ตรวจประกอบการปรับปรุง 23 ตุลาคม 2569

ใช้เอกสารข้างต้นตรวจหลักการก่อนเรียบเรียงส่วนขยายและสร้างข้อมูลสมมติ ตัวอย่างใหม่ที่เป็นโปรแกรม C ครบไฟล์ตรวจด้วย GCC 12.2.0 (Debian Linux) และ GCC 6.3.0 (MinGW-w64), C11 และคำเตือน -Wall -Wextra -Wpedantic เมื่อ 23 ตุลาคม 2569 แยกจากผลทดสอบ GCC 13.3.0 ของตัวอย่างเดิม ค่ารูปแบบและผลคำนวณตรวจจากการรัน ส่วนเวลา CPU ขึ้นกับสภาพแวดล้อม ไม่ใช่ผลตายตัว

แหล่งปฐมภูมิเพิ่มสำหรับชุดสอนพร้อมใช้บทที่ 5

ตรวจเอกสารก่อนเขียนส่วนเพิ่มเมื่อ 23 ตุลาคม 2569 ตัวอย่างใช้ C11 ไม่ใช้ส่วนขยาย GNU ชุดข้อมูลเป็นข้อมูลสมมติและผลลัพธ์ของการคำนวณ เวลาในแผนสอนไม่ใช่ผลวัดจากการอ่านบทความ

By Watcharin Sarachai