Sign in

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

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

บทที่ 4 โครงสร้างควบคุมแบบทางเลือก

ShareFacebookXLinkedInLINE

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

บทนี้ว่าด้วยโครงสร้างควบคุมแบบทางเลือกของโปรแกรมภาษา C ต่อยอดจากการรับข้อมูลด้วย scanf ในบทที่ 3 และตัวแปรกับตัวดำเนินการในบทที่ 2 โดยอธิบายตัวดำเนินการเปรียบเทียบและตรรกะสำหรับสร้างเงื่อนไข แล้วนำเงื่อนไขไปควบคุมเส้นทางการทำงานของโปรแกรมด้วยคำสั่ง if, if-else, if-else if, nested if และ switch เพื่อให้โปรแกรมตัดสินใจเลือกทำงานแตกต่างกันตามข้อมูลที่รับเข้ามาได้

ผลลัพธ์ข้อความหลักฐานCLO
LLO4.1อธิบายและใช้ตัวดำเนินการเปรียบเทียบ (< <= > >= == !=) และตรรกะ (&&, ||, !) สร้างเงื่อนไขงานข้อ 1 (ปฏิบัติการที่ 4 Lab A)CLO1
LLO4.2เลือกใช้ if / if-else / if-else if / nested if ให้สอดคล้องกับเงื่อนไขของโจทย์งานข้อ 2 (ปฏิบัติการที่ 4 Lab A)CLO2
LLO4.3ใช้ switch เลือกหลายทางจากค่าที่ไม่ต่อเนื่อง พร้อมระบุบทบาทของ break และ defaultงานข้อ 3 (ปฏิบัติการที่ 4 Lab B)CLO2
LLO4.4ปฏิบัติตามมาตรฐานการเขียนโค้ด (การจัดย่อหน้า การตั้งชื่อสื่อความหมาย การใส่คอมเมนต์) ในงานโค้ดของบทนี้โค้ดทุกข้อที่ส่งในบทนี้CLO5

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

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

บทที่ 4 เป็นจุดที่โปรแกรมเริ่มตัดสินใจเลือกเส้นทางการทำงานได้เอง จึงเป็นสะพานเชื่อมความรู้เรื่องการรับข้อมูลในบทที่ 3 ไปสู่โครงสร้างควบคุมแบบวนซ้ำในบทที่ 5 งานทั้งสามข้อในบทนี้สะสมหลักฐานของ CLO1 และ CLO2 ต่อเนื่องจากบทก่อน ใบงานบทนี้คิด 10 คะแนนตามตารางผลลัพธ์ด้านล่าง และเป็นหนึ่งใน 13 ชิ้นที่มีน้ำหนักเท่ากันในหมวดใบงานบทที่ 1–13 รวม 15% ของคะแนนรายวิชา ตามแผนคะแนนฉบับที่ 4 (ดูบันทึกสำหรับผู้สอนท้ายบท)

วิธีสอน

  1. ทบทวนการรับข้อมูลด้วย scanf จากบทที่ 3 แล้วตั้งโจทย์ที่ต้องตัดสินใจจากค่าที่รับเข้ามา
  2. อธิบายตัวดำเนินการเปรียบเทียบและตรรกะ โดยชี้ว่าเงื่อนไขให้ผลเป็นจริง (1) หรือเท็จ (0)
  3. สาธิตคำสั่ง if, if-else และ if-else if บนเครื่องจริง พร้อมแสดงผลที่เปลี่ยนไปตามค่าที่ป้อน
  4. แนะนำคำสั่ง switch สำหรับการเลือกหลายทางจากค่าที่ไม่ต่อเนื่อง พร้อมอธิบายบทบาทของ break และ default
  5. ให้ลงมือปฏิบัติตามใบงาน โดยกำหนดจุดตรวจระหว่างทางเพื่อให้ข้อมูลป้อนกลับทันที
  6. สรุปข้อผิดพลาดที่พบบ่อย เช่น การใช้ = แทน == และการลืม break ใน switch เพื่อป้องกันความเข้าใจคลาดเคลื่อน

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

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

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

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

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

เงื่อนไขผ่าน: งานข้อ 1 และงานข้อ 2 ประเมินจากไฟล์เดียวกัน (ch4_grade.c) โดยงานข้อ 1 ตรวจความถูกต้องของเงื่อนไขตรวจช่วง และงานข้อ 2 ตรวจผลการตัดเกรด ดังนั้นเงื่อนไขตรวจช่วงในงานข้อ 1 ต้องถูกต้องก่อนจึงจะประเมินการตัดเกรดในงานข้อ 2 ได้ครบทุกกรณี

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

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

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

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

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

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

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

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

บทนำ

โปรแกรมที่มีประโยชน์ส่วนใหญ่ต้องตัดสินใจเลือกทำงานต่างกันตามข้อมูลที่รับเข้ามา เช่น ตัดเกรดจากคะแนน หรือเลือกเมนูจากหมายเลขที่ป้อน บทที่ 3 ได้แนะนำการรับข้อมูลด้วย scanf และบทที่ 2 ได้แนะนำตัวแปรและตัวดำเนินการ บทนี้จึงนำความรู้นั้นมาต่อยอดด้วยการสร้างเงื่อนไขและควบคุมเส้นทางการทำงานของโปรแกรม โดยเริ่มจากตัวดำเนินการเปรียบเทียบและตรรกะ แล้วอธิบายคำสั่ง if, if-else, if-else if, nested if และ switch

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

สิ่งที่ได้จากบทนี้ใช้ต่อในบท
ตัวดำเนินการเปรียบเทียบและตรรกะสำหรับสร้างเงื่อนไขทุกบทที่มีการตัดสินใจ
คำสั่ง if, if-else, if-else if และ nested ifบท 5 (วนซ้ำ), บท 6 (ฟังก์ชัน) เป็นต้นไป
คำสั่ง switch พร้อม break และ defaultทุกบทที่เลือกหลายทางจากค่าที่ไม่ต่อเนื่อง

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

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

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

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

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

ต่อจากบทที่ 3 การรับเข้าและการแสดงผล ให้แยกการอ่านข้อมูลสำเร็จออกจากการตัดสินใจตามกติกา เริ่มจากเขียนผลที่ต้องการเป็นช่วงหรือตารางตัดสินใจ ก่อนเลือก if, else if หรือ switch แล้วทำนายเส้นทางก่อนรัน

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

เกณฑ์คะแนนและระดับผลในโปรแกรมเป็นกติกาสมมติสำหรับฝึกเงื่อนไข ไม่ใช่ประกาศเกรดทางการของรายวิชา งานส่ง LLO/CLO และการประเมินยังยึดประกาศเดิม

4.1 ตัวดำเนินการเปรียบเทียบและตรรกะ

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

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

ตัวดำเนินการความหมายตัวอย่างนิพจน์ผล
<น้อยกว่า5 < 81
<=น้อยกว่าหรือเท่ากับ8 <= 81
>มากกว่า5 > 80
>=มากกว่าหรือเท่ากับ5 >= 80
==เท่ากับ5 == 51
!=ไม่เท่ากับ5 != 81

ตามมาตรฐานภาษา C ชนิดของนิพจน์ที่ใช้ตัวดำเนินการเปรียบเทียบเป็น int เสมอ และค่าเป็น 1 เมื่อความสัมพันธ์เป็นจริง และเป็น 0 เมื่อเป็นเท็จ จึงนำผลนี้ไปใช้เป็นเงื่อนไขของ if หรือ switch หรือเก็บในตัวแปร int ได้โดยตรง

ตัวดำเนินการตรรกะมีสามตัว ได้แก่ && (และ) ให้ผลจริงเมื่อทั้งสองเงื่อนไขจริง, \|\| (หรือ) ให้ผลจริงเมื่อมีอย่างน้อยหนึ่งเงื่อนไขจริง และ ! (นิเสธ) กลับค่าจริงเป็นเท็จและเท็จเป็นจริง เช่น score >= 0 && score <= 100 เป็นจริงเมื่อคะแนนอยู่ในช่วง 0 ถึง 100 เท่านั้น ตารางค่าความจริง (truth table) ด้านล่างสรุปผลของ && และ \|\| สำหรับทุกคู่ของค่า โดย 1 แทนจริงและ 0 แทนเท็จ

ABA && BA || B
0000
0101
1001
1111

ส่วนตัวดำเนินการ ! ใช้กับค่าเดียว ดังตาราง

A!A
01
10

ใบงาน 1: ค่าที่ใช้เป็นเงื่อนไขกับผลของตัวเปรียบเทียบ

ก่อนรันโปรแกรมต่อไปนี้ ให้ทำนายแยกกันว่า if (value) จะเข้าทางใด ผล !value เป็นเท่าไร และผล value > 0 เป็นเท่าไร อย่าถือว่าทั้งสามคำถามถามเรื่องเดียวกัน

C
/* truth_value.c: Distinguish a condition from a positive comparison. */
#include <stdio.h>
 
int main(void)
{
    int value;
    if (scanf("%d", &value) != 1) {
        puts("Input error");
        return 1;
    }
    if (value) {
        puts("condition=true");
    } else {
        puts("condition=false");
    }
    printf("not=%d positive=%d\n", !value, value > 0);
    return 0;
}
valueทางของ if (value) ที่คาด!value ที่คาดvalue > 0 ที่คาดสิ่งที่ต่างจากคำทำนาย
0ก่อนรันก่อนรันก่อนรันหลังรัน
1ก่อนรันก่อนรันก่อนรันหลังรัน
2ก่อนรันก่อนรันก่อนรันหลังรัน
-3ก่อนรันก่อนรันก่อนรันหลังรัน

ค่าลบช่วยเปิดเผยความสับสนระหว่าง “ไม่เป็นศูนย์” กับ “เป็นจำนวนบวก” ส่วนตัวเปรียบเทียบและตัวดำเนินการตรรกะให้ผลเป็น int ค่า 0 หรือ 1 ตามกติกาของภาษา อ่านหลักการได้จาก GNU C: Logical Operators

ตัวอย่างที่ทดสอบจริง โปรแกรมต่อไปนี้กำหนด a = 5 และ b = 8 แล้วแสดงผลของแต่ละนิพจน์ เพื่อยืนยันว่าตัวดำเนินการเปรียบเทียบและตรรกะให้ผลเป็นจำนวนเต็ม 0 หรือ 1

C
/* ops_demo.c : แสดงว่าตัวดำเนินการเปรียบเทียบและตรรกะให้ผลเป็น int 0 หรือ 1 */
#include <stdio.h>
 
int main(void) {
    int a = 5, b = 8;
    printf("a = %d, b = %d\n", a, b);
    printf("a < b  = %d\n", a < b);
    printf("a > b  = %d\n", a > b);
    printf("a == b = %d\n", a == b);
    printf("a != b = %d\n", a != b);
    printf("(a < b) && (b < 10) = %d\n", (a < b) && (b < 10));
    printf("(a > b) || (b < 10) = %d\n", (a > b) || (b < 10));
    printf("!(a == b)           = %d\n", !(a == b));
    return 0;
}

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

TEXT
a = 5, b = 8
a < b  = 1
a > b  = 0
a == b = 0
a != b = 1
(a < b) && (b < 10) = 1
(a > b) || (b < 10) = 1
!(a == b)           = 1

ตารางด้านล่างไล่การประเมินของแต่ละนิพจน์ทีละขั้นด้วยค่า a = 5 และ b = 8

นิพจน์แทนค่าผล
a < b5 < 81
a > b5 > 80
a == b5 == 80
a != b5 != 81
(a < b) && (b < 10)1 && 11
(a > b) || (b < 10)0 || 11
!(a == b)!01

ใบงาน 2: เขียนช่วงให้ครบก่อนเชื่อมเงื่อนไข

ข้อกำหนดคะแนนตัวอย่างยอมรับ 0–100 รวมปลายทั้งสอง ให้เขียนความจริงของแต่ละส่วนก่อนนำมารวม แล้วแยกเงื่อนไข “อยู่ในช่วง” ออกจาก “อยู่นอกช่วง”

คะแนนscore >= 0score <= 100ใช้ AND แล้วอยู่ในช่วงหรือไม่ถ้าใช้ OR จะผิดตรงไหน
-1บันทึกเองบันทึกเองบันทึกเองอธิบาย
0บันทึกเองบันทึกเองบันทึกเองอธิบาย
50บันทึกเองบันทึกเองบันทึกเองอธิบาย
100บันทึกเองบันทึกเองบันทึกเองอธิบาย
101บันทึกเองบันทึกเองบันทึกเองอธิบาย

เปรียบเทียบกับ score < 0 || score > 100 ว่ากำลังถามเรื่องใด และต้องใช้ปลายช่วงแบบรวมค่าหรือไม่รวมค่าอย่างไร ไม่เขียน 0 <= score <= 100 แทนการตรวจช่วง เพราะ C จัดกลุ่มและประเมินตัวเปรียบเทียบเป็นนิพจน์ต่อกัน ไม่ใช่สัญกรณ์ช่วงแบบคณิตศาสตร์

ระดับที่ 3: ตัวดำเนินการ && และ \|\| ทำงานแบบลัดวงจร (short-circuit) คือเมื่อผลลัพธ์ทราบได้จากเงื่อนไขซ้ายแล้ว เงื่อนไขขวาจะไม่ถูกประเมิน สำหรับ && หากเงื่อนไขซ้ายเป็นเท็จ (เท่ากับ 0) ผลทั้งนิพจน์เป็นเท็จแน่นอน เงื่อนไขขวาจึงไม่ถูกประเมิน ส่วน \|\| หากเงื่อนไขซ้ายเป็นจริง (ไม่เท่ากับ 0) ผลทั้งนิพจน์เป็นจริงแน่นอน เงื่อนไขขวาจึงไม่ถูกประเมิน นอกจากนี้ตัวดำเนินการทั้งสองยังมีจุดลำดับ (sequence point) หลังการประเมินเงื่อนไขซ้าย กล่าวคือผลข้างเคียงของเงื่อนไขซ้ายเสร็จสมบูรณ์ก่อนที่เงื่อนไขขวาจะเริ่มทำงาน

ใบงาน 3: ลัดวงจรต้องป้องกันก่อนคำนวณ

กำหนด b = 20 ในเงื่อนไข a != 0 && b / a > 1 ให้ไล่จากส่วนซ้ายก่อน หากผลทั้งนิพจน์ตัดสินได้แล้วให้ทำเครื่องหมายว่า “ไม่ประเมิน” ทางขวา ไม่เติมผลหารที่ไม่ควรเกิดขึ้น

aผล a != 0ทางขวาถูกประเมินหรือไม่ผลรวมเงื่อนไขเหตุผล
0ก่อนรันก่อนรันก่อนรันระบุจุดที่ป้องกัน
4ก่อนรันก่อนรันก่อนรันไล่ผลหาร
20ก่อนรันก่อนรันก่อนรันแยกผลเท็จจากการข้าม

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

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

โปรแกรมต่อไปนี้สาธิตการลัดวงจรของ && เพื่อหลีกเลี่ยงการหารด้วยศูนย์ เมื่อ a เป็น 0 เงื่อนไขซ้าย a != 0 เป็นเท็จ นิพจน์ b / a > 1 ทางขวาจึงไม่ถูกคำนวณ โปรแกรมจึงไม่เกิดการหารด้วยศูนย์

C
/* shortcircuit.c : สาธิตการลัดวงจรของ && หลีกเลี่ยงการหารด้วยศูนย์ */
#include <stdio.h>
 
int main(void) {
    int a = 0, b = 20;
    if (a != 0 && b / a > 1) {
        printf("b/a มากกว่า 1\n");
    } else {
        printf("ข้ามการคำนวณ b/a เพราะ a เป็น 0 (ลัดวงจร)\n");
    }
    a = 4;
    if (a != 0 && b / a > 1) {
        printf("b/a = %d มากกว่า 1\n", b / a);
    }
    return 0;
}

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

TEXT
ข้ามการคำนวณ b/a เพราะ a เป็น 0 (ลัดวงจร)
b/a = 5 มากกว่า 1

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

คำถามตรวจความเข้าใจ 4.1: เงื่อนไข score >= 0 && score <= 100 ต่างจาก score >= 0 \|\| score <= 100 อย่างไร

4.2 คำสั่ง if และ if-else

ระดับที่ 1: คำสั่ง if ใช้สั่งให้โปรแกรมทำงานชุดหนึ่งเมื่อเงื่อนไขเป็นจริง หากต้องการให้มีทางเลือกสำรองเมื่อเงื่อนไขเป็นเท็จ ใช้ else ต่อท้าย

ระดับที่ 2: รูปแบบพื้นฐานของคำสั่ง if และ if-else เป็นดังนี้

C
if (score >= 60) {
    printf("ผ่าน\n");
} else {
    printf("ไม่ผ่าน\n");
}

เงื่อนไขเขียนอยู่ในวงเล็บหลัง if และคำสั่งที่จะทำเมื่อเงื่อนไขจริงอยู่ในบล็อกวงเล็บปีกกา { } ถ้าเงื่อนไขเป็นเท็จโปรแกรมจะข้ามไปทำคำสั่งในบล็อกของ else แทน การใส่วงเล็บปีกกาแม้มีคำสั่งเดียวช่วยให้โครงสร้างชัดเจนและเพิ่มคำสั่งภายหลังได้ง่าย ตามมาตรฐานภาษา C เงื่อนไขของ if เป็นนิพจน์ชนิดสเกลาร์ (scalar) ใด ๆ โดยโปรแกรมจะทำคำสั่งในบล็อกจริงเมื่อเงื่อนไขมีค่าไม่เท่ากับ 0 และข้ามไปทำบล็อกของ else เมื่อเงื่อนไขมีค่าเท่ากับ 0

ตารางด้านล่างไล่การทำงานของบล็อก if-else ข้างต้นสำหรับคะแนนตัวอย่างสามค่า

scorescore >= 60บล็อกที่ทำงานข้อความ
851 (จริง)บล็อก ifผ่าน
601 (จริง)บล็อก ifผ่าน
590 (เท็จ)บล็อก elseไม่ผ่าน

ระดับที่ 3: การจัดย่อหน้าภายในบล็อกด้วย 4 ช่องว่างช่วยให้อ่านง่ายและเห็นขอบเขตของแต่ละทางเลือกชัดเจน กับดักที่พบบ่อยคือการละวงเล็บปีกกาเมื่อมีหลายคำสั่ง ทำให้มีเพียงคำสั่งแรกเท่านั้นที่อยู่ภายใต้เงื่อนไข ส่วนคำสั่งที่เหลือทำงานเสมอโดยไม่ขึ้นกับเงื่อนไข ซึ่งเป็นสาเหตุของผลลัพธ์ที่คลาดเคลื่อนและตรวจพบได้ยาก อีกกับดักหนึ่งคือการเทียบค่าทศนิยมด้วย == โดยตรง เนื่องจากค่าทศนิยมอาจมีความคลาดเคลื่อนจากการปัดเศษ การเทียบช่วงด้วย <, <=, >, >= จึงเหมาะกว่าสำหรับค่าชนิดทศนิยม

คำถามตรวจความเข้าใจ 4.2: เหตุใดการใช้ = แทน == ในเงื่อนไขของ if จึงทำให้ผลผิด

ใบงาน 4: ต้องการหลายป้ายหรือเพียงทางเลือกเดียว

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

ค่าป้ายที่ต้องได้ตามโจทย์ถ้าใช้ if สองตัวอิสระถ้าใช้ if ต่อด้วย else ifแบบใดตรงความต้องการ
8บันทึกเองทำนายทำนายอธิบาย
12บันทึกเองทำนายทำนายอธิบาย
15บันทึกเองทำนายทำนายอธิบาย
9บันทึกเองทำนายทำนายอธิบาย

ทดลองด้วยคำสั่งเงื่อนไขที่เปรียบเทียบ number % 2 == 0 และ number > 10 แล้วบันทึกว่ามีข้อความกี่บรรทัด จากนั้นกลับไปอธิบายว่าทำไมสาย else if ของระดับคะแนนจึงต้องหยุดเมื่อพบเงื่อนไขแรกที่จริง

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

4.3 เงื่อนไขหลายทาง: if-else if และ nested if

ระดับที่ 1: เมื่อมีทางเลือกมากกว่าสองทาง ใช้ if-else if ต่อเงื่อนไขหลายชั้น โปรแกรมจะตรวจเงื่อนไขทีละข้อจากบนลงล่าง แล้วทำคำสั่งของเงื่อนไขแรกที่เป็นจริงเพียงชุดเดียว

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

C
if (score >= 80) {
    printf("เกรด A\n");
} else if (score >= 70) {
    printf("เกรด B\n");
} else if (score >= 60) {
    printf("เกรด C\n");
} else {
    printf("เกรด F\n");
}

หากเรียงจากค่าต่ำขึ้นไป เงื่อนไข score >= 60 จะเป็นจริงสำหรับคะแนนสูงด้วย ทำให้ได้เกรดผิด การเรียงลำดับที่ถูกต้องและใช้ else if จึงรับประกันว่าแต่ละคะแนนตรงกับเกรดเดียว

ตารางด้านล่างไล่การตรวจเงื่อนไขของบล็อก if-else if ข้างต้นทีละขั้นสำหรับคะแนนตัวอย่างสี่ค่า เครื่องหมาย — หมายถึงเงื่อนไขนั้นไม่ถูกประเมินเพราะโปรแกรมหยุดที่เงื่อนไขก่อนหน้าที่เป็นจริงแล้ว

scorescore >= 80score >= 70score >= 60elseผลเกรด
951 (จริง)———A
730 (เท็จ)1 (จริง)——B
650 (เท็จ)0 (เท็จ)1 (จริง)—C
400 (เท็จ)0 (เท็จ)0 (เท็จ)ทำงานF

ระดับที่ 3: nested if คือการวาง if ไว้ภายในบล็อกของ if อีกชั้นหนึ่ง ใช้เมื่อการตัดสินใจขึ้นกับเงื่อนไขหลายระดับ เช่น ตรวจว่าคะแนนอยู่ในช่วงที่ถูกต้องก่อน แล้วจึงตัดสินผ่าน/ไม่ผ่านภายใน โปรแกรมต่อไปนี้สาธิตการใช้ nested if โดยชั้นนอกตรวจว่าคะแนนอยู่ในช่วง 0–100 ด้วย score >= 0 && score <= 100 ก่อน แล้วชั้นในจึงตัดสินผ่าน/ไม่ผ่าน

C
/* nested_if.c : ตรวจช่วงก่อนด้วย nested if แล้วจึงตัดสินผ่าน/ไม่ผ่าน */
#include <stdio.h>
 
int main(void) {
    int score;
    printf("ป้อนคะแนน (0-100): ");
    if (scanf("%d", &score) != 1) {
        puts("Input error");
        return 1;
    }
 
    if (score >= 0 && score <= 100) {
        if (score >= 60) {
            printf("อยู่ในช่วง และผ่าน\n");
        } else {
            printf("อยู่ในช่วง แต่ไม่ผ่าน\n");
        }
    } else {
        printf("คะแนนไม่ถูกต้อง\n");
    }
    return 0;
}

ผลการรันจริง (ทดสอบแล้วด้วย gcc 13.3.0 ป้อนคะแนน 75, 40 และ 150 ตามลำดับ)

TEXT
ป้อนคะแนน (0-100): อยู่ในช่วง และผ่าน
ป้อนคะแนน (0-100): อยู่ในช่วง แต่ไม่ผ่าน
ป้อนคะแนน (0-100): คะแนนไม่ถูกต้อง

ตารางด้านล่างไล่การทำงานของทั้งสองชั้นสำหรับคะแนนตัวอย่าง เมื่อชั้นนอกเป็นเท็จ ชั้นในจะไม่ถูกประเมิน (—)

scoreชั้นนอก score >= 0 && score <= 100ชั้นใน score >= 60ข้อความ
751 (จริง)1 (จริง)อยู่ในช่วง และผ่าน
401 (จริง)0 (เท็จ)อยู่ในช่วง แต่ไม่ผ่าน
1500 (เท็จ)—คะแนนไม่ถูกต้อง

การซ้อนหลายชั้นทำให้โค้ดอ่านยากขึ้น หากเงื่อนไขสามารถรวมด้วยตัวดำเนินการตรรกะหรือเปลี่ยนเป็น if-else if ได้ ควรเลือกรูปแบบที่อ่านง่ายกว่าเพื่อลดความซับซ้อน

คำถามตรวจความเข้าใจ 4.3: เหตุใดการเรียงลำดับเงื่อนไขใน if-else if จึงมีผลต่อความถูกต้องของการตัดเกรด

กรณีศึกษาต่อเนื่อง: อนุญาตเข้ากิจกรรมเมื่อครบสองเงื่อนไข

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

C
/* access_decision.c: Apply a fictional two-condition access rule. */
#include <stdio.h>
 
int main(void)
{
    int registered;
    int has_pass;
    if (scanf("%d %d", &registered, &has_pass) != 2) {
        puts("Input error");
        return 1;
    }
    if (registered < 0 || registered > 1 ||
        has_pass < 0 || has_pass > 1) {
        puts("Range error");
        return 1;
    }
 
    if (registered && has_pass) {
        puts("Allowed");
    } else if (!registered && !has_pass) {
        puts("Register and bring pass");
    } else if (!registered) {
        puts("Register first");
    } else {
        puts("Bring pass");
    }
    return 0;
}
ลงทะเบียนมีบัตรสิ่งที่ขาดหรือผลที่ต้องการแขนงที่คาดว่าจะทำงานผลจริง
00ระบุก่อนรันทำนายหลังรัน
01ระบุก่อนรันทำนายหลังรัน
10ระบุก่อนรันทำนายหลังรัน
11ระบุก่อนรันทำนายหลังรัน

แยกกฎ input ว่ารับเฉพาะ 0/1 ออกจากกฎภาษา C ที่ถือค่าที่ไม่เป็นศูนย์เป็นจริง โปรแกรมนี้ปฏิเสธ 2 เพื่อรักษาข้อตกลงของข้อมูล ไม่ใช่เพราะ C มอง 2 เป็นเท็จ ให้ลองค่าที่ไม่ตรงชนิดหรือผิดขอบเขตแล้วตรวจว่าจบก่อนพิจารณาสิทธิ์

4.4 คำสั่ง switch

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

ระดับที่ 2: switch เทียบค่าของนิพจน์กับแต่ละ case เมื่อพบค่าที่ตรงจะทำคำสั่งของ case นั้น และ break ใช้ออกจาก switch ส่วน default คือกรณีที่ไม่ตรงกับ case ใดเลย

C
switch (choice) {
    case 1:
        printf("เมนูที่ 1\n");
        break;
    case 2:
        printf("เมนูที่ 2\n");
        break;
    default:
        printf("ไม่มีเมนูนี้\n");
}

ตามมาตรฐานภาษา C นิพจน์ที่ควบคุม switch ต้องเป็นชนิดจำนวนเต็ม (เช่น int หรือ char) และค่าของ case แต่ละตัวต้องเป็นนิพจน์ค่าคงที่จำนวนเต็ม (integer constant expression) ที่ไม่ซ้ำกัน ส่วน default มีได้มากสุดหนึ่งตัวและจะมีหรือไม่มีก็ได้ หากค่าของนิพจน์ไม่ตรงกับ case ใดและไม่มี default โปรแกรมจะไม่ทำคำสั่งใดในบล็อก switch เลย

หากลืม break ใน case หนึ่ง โปรแกรมจะทำคำสั่งของ case ถัดไปต่อเนื่องกันไปจนกว่าจะพบ break หรือจบ switch พฤติกรรมนี้เรียกว่า fall-through ซึ่งมักไม่ใช่สิ่งที่ตั้งใจ โปรแกรมต่อไปนี้จงใจละ break ของ case 1 เพื่อให้เห็นผลของ fall-through เมื่อ choice เป็น 1

C
/* fallthrough_bug.c : สาธิตผลของการลืม break (fall-through โดยไม่ตั้งใจ) */
#include <stdio.h>
 
int main(void) {
    int choice = 1;
    switch (choice) {
        case 1:
            printf("เมนูที่ 1\n");
        case 2:
            printf("เมนูที่ 2\n");
            break;
        case 3:
            printf("เมนูที่ 3\n");
            break;
    }
    return 0;
}

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

TEXT
เมนูที่ 1
เมนูที่ 2

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

ใบงาน 5: เริ่มตรง case ใด และจบตรงไหน

เมื่ออ่านตัวเลือกได้แล้ว ให้ติดตามตำแหน่งที่ switch กระโดดเข้าและคำสั่งที่ทำต่อจนเจอ break หรือจบบล็อก อย่าถือว่าป้าย case ถัดไปทำหน้าที่หยุดงานเอง

โปรแกรมที่พิจารณาข้อมูลเริ่มตรงไหนข้อความที่คาดจุดที่หยุด
เมนูที่มี break1, 2, 3 ทดลองทีละค่าบันทึกก่อนรันอธิบาย
ตัวอย่างลืม break1บันทึกก่อนรันอธิบาย
ตัวอย่างลืม break2บันทึกก่อนรันอธิบาย
กลุ่มวัน 1–73, 6, 9 ทดลองทีละค่าบันทึกก่อนรันอธิบาย

กรณีตัวอย่างลืม break เป็นโค้ดที่ตั้งใจให้เห็น fall-through ไม่ใช่ต้นแบบเมนูที่แยกงาน แต่ยังเป็นพฤติกรรมที่ C กำหนดไว้ คอมไพเลอร์บางรุ่นอาจเตือน ส่วนการวาง case หลายป้ายติดกันเพื่อใช้ชุดคำสั่งเดียวกันเป็นอีกเจตนาหนึ่งที่ต้องอธิบายให้ได้

ใช้ไวยากรณ์ C11 ในบทนี้ ไม่ใช้ช่วง case 1 ... 5 ที่บางคอมไพเลอร์อนุญาตเป็นส่วนขยาย และไม่ใช้สตริงเป็น selector โดยตรง หลักการเลือกและการไหลต่ออ่านได้จาก GNU C: switch Statement โดยแยกส่วนขยาย GNU ออกจากตัวอย่างมาตรฐานที่ใช้เรียน

การใช้ fall-through อย่างตั้งใจ บางครั้งต้องการให้หลายค่าทำงานชุดเดียวกัน จึงวาง case หลายตัวติดกันโดยไม่ใส่คำสั่งคั่น เป็นการใช้ fall-through อย่างตั้งใจ โปรแกรมต่อไปนี้รับหมายเลขวัน 1–7 แล้วรวม case 1 ถึง case 5 ให้แสดง "วันทำงาน" และรวม case 6 กับ case 7 ให้แสดง "วันหยุดสุดสัปดาห์"

C
/* switch_group.c : รวมหลาย case เข้าด้วยกันโดยใช้ fall-through อย่างตั้งใจ */
#include <stdio.h>
 
int main(void) {
    int day;
    printf("ป้อนหมายเลขวัน (1-7): ");
    if (scanf("%d", &day) != 1) {
        puts("Input error");
        return 1;
    }
 
    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");
    }
    return 0;
}

ผลการรันจริง (ทดสอบแล้วด้วย gcc 13.3.0 ป้อนหมายเลข 3, 6 และ 9 ตามลำดับ)

TEXT
ป้อนหมายเลขวัน (1-7): วันทำงาน
ป้อนหมายเลขวัน (1-7): วันหยุดสุดสัปดาห์
ป้อนหมายเลขวัน (1-7): หมายเลขวันไม่ถูกต้อง

หมายเลข 3 ตรงกับ case 3 ซึ่งไม่มีคำสั่งของตัวเอง จึงไหลต่อลงไปจนถึงคำสั่ง printf("วันทำงาน\n") ที่ case 5 ส่วนหมายเลข 9 ไม่ตรงกับ case ใด จึงไปทำคำสั่งใน default

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

ประเด็นif-else ifswitch
ชนิดเงื่อนไขที่ตรวจได้เงื่อนไขใด ๆ รวมการเทียบช่วงเทียบค่ากับค่าคงที่เท่านั้น
ชนิดของค่าที่เทียบนิพจน์สเกลาร์ใด ๆ รวมทศนิยมจำนวนเต็มหรืออักขระเท่านั้น
ตรวจช่วง เช่น score >= 80ทำได้โดยตรงทำไม่ได้โดยตรง
การไหลต่อระหว่างทางเลือกไม่มี แต่ละทางเลือกแยกกันมี (fall-through) ต้องใส่ break
กรณีที่ไม่ตรงเงื่อนไขใดใช้ elseใช้ default

คำถามตรวจความเข้าใจ 4.4: ถ้าลืม break ใน case หนึ่งของ switch จะเกิดอะไรขึ้น

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

ตัวอย่างต่อไปนี้เป็นโค้ดฝึกแยกจากงานที่ต้องส่ง แสดงข้อผิดพลาดที่สัมพันธ์กับเนื้อหาบท พร้อมข้อความวินิจฉัยจริง วิธีอ่าน และฉบับแก้ คำว่า 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: ใช้การกำหนดค่าแทนการเปรียบเทียบ

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

โจทย์กำหนดผ่านเมื่อคะแนนตั้งแต่ 50 แต่ = เป็น assignment จึงเขียนค่า 50 ลง score แล้วใช้ค่าไม่เป็นศูนย์เป็นจริง คะแนนเริ่ม 0 จึงถูกเปลี่ยนและเลือก PASS อย่างผิดเจตนา การเขียนเช่นนี้มีความหมายตามภาษา จึงอาจเป็น warning ไม่ใช่ syntax error

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

C
#include <stdio.h>
int main(void)
{
    int score = 0;
    if (score = 50) {
        puts("PASS");
    } else {
        puts("FAIL");
    }
    printf("score=%d\n", score);
    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:5:9: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
    5 |     if (score = 50) {
      |         ^~~~~

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

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

stdout:

TEXT
PASS
score=50

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

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

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

TEXT
FAIL
score=0

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

suggest parentheses around assignment used as truth value หมายถึง compiler สงสัยการกำหนดค่าในตำแหน่งเงื่อนไข การใส่วงเล็บซ้อนอาจทำให้คำเตือนหายแต่ยังเปลี่ยน score และเลือกสาขาผิด ต้องแก้ตามข้อกำหนด ไม่ใช่ตามวิธีทำให้ log เงียบ

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

C
#include <stdio.h>
int main(void)
{
    int score = 0;
    if (score >= 50) {
        puts("PASS");
    } else {
        puts("FAIL");
    }
    printf("score=%d\n", score);
    return 0;
}

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

ใช้ >= ตามกติกาผ่านขั้นต่ำและไม่เปลี่ยนค่าคะแนน ทดสอบอย่างน้อย 0, 49, 50 และ 100 เพื่อดูจุดแบ่งสาขา การเปลี่ยนเป็น == อย่างเดียวก็ยังไม่ตรงโจทย์สำหรับคะแนนมากกว่า 50

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

TEXT
FAIL
score=0

ตัวอย่างที่ผิด 2: semicolon ทำให้ if มีคำสั่งว่าง

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

semicolon หลัง if เป็น empty statement ที่ถูกควบคุมโดยเงื่อนไข ส่วน block ถัดมาเป็นอีกคำสั่งหนึ่งที่ทำงานเสมอ การจัดย่อหน้าทำให้ดูเหมือน PASS อยู่ใต้ if แต่ compiler อ่านจากโครงสร้างภาษา ไม่ใช่ระยะเยื้อง

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

C
#include <stdio.h>
int main(void)
{
    int score = 20;
    if (score >= 50);
    {
        puts("PASS");
    }
    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:5:21: warning: suggest braces around empty body in an 'if' statement [-Wempty-body]
    5 |     if (score >= 50);
      |                     ^
bad.c:5:5: warning: this 'if' clause does not guard... [-Wmisleading-indentation]
    5 |     if (score >= 50);
      |     ^~
bad.c:6:5: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
    6 |     {
      |     ^

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

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

stdout:

TEXT
PASS

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

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

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

TEXT
FAIL

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

empty body in an if statement ชี้ว่าตัว if ควบคุมคำสั่งว่าง หากมี misleading-indentation เพิ่มเติม ให้เทียบกับปีกกาและ semicolon ตัวจริง ไม่ถือว่าการขยับย่อหน้าเปลี่ยนตรรกะได้

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

C
#include <stdio.h>
int main(void)
{
    int score = 20;
    if (score >= 50) {
        puts("PASS");
    } else {
        puts("FAIL");
    }
    return 0;
}

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

ลบ semicolon ที่เกินแล้วให้ block เป็น body ของ if เพิ่ม else เพื่อแสดงผลกรณีไม่ผ่าน ทดสอบข้อมูล 20 ซึ่งต้องไม่พิมพ์ PASS และข้อมูล 50 ซึ่งต้องเลือก PASS

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

TEXT
FAIL

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

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

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

ปฏิบัติการที่ 4 โครงสร้างควบคุมแบบทางเลือก

ปฏิบัติการนี้แบ่งเป็นสองส่วน ส่วนแรกฝึกสร้างเงื่อนไขด้วยตัวดำเนินการเปรียบเทียบและตรรกะ แล้วเลือกทำงานด้วย if-else if เพื่อตัดเกรด ส่วนที่สองฝึกเลือกหลายทางด้วย switch ทั้งสองส่วนทำแยกกันได้ แต่ควรทำตามลำดับเพื่อให้เห็นรูปแบบการเลือกทำงานที่ต่างกัน

ℹ️Note

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

Lab A: ตัดเกรดจากคะแนน

สถานการณ์ ต้องการรับคะแนนในช่วง 0–100 แล้วตัดเกรด A/B/C/F พร้อมตรวจค่านอกช่วงด้วยตัวดำเนินการตรรกะ เพื่อฝึกสร้างเงื่อนไขและเลือกทำงานหลายทาง

เป้าหมาย ฝึก LLO4.1 และ LLO4.2 คือสร้างเงื่อนไขด้วยตัวดำเนินการเปรียบเทียบและตรรกะ และเลือกใช้ if-else if ให้สอดคล้องกับเงื่อนไขของโจทย์

ขั้นปฏิบัติ

  1. สร้างไฟล์ชื่อ ch4_grade.c ด้วยโปรแกรมแก้ไขข้อความหรือ IDE
  2. พิมพ์ซอร์สโค้ดต่อไปนี้ โดยจัดย่อหน้า 4 ช่องว่างและใส่คอมเมนต์ส่วนหัว
C
/* ch4_grade.c : ตัดเกรดจากคะแนนด้วย if-else if และตรวจช่วงด้วยตรรกะ */
#include <stdio.h>
 
int main(void) {
    int score;
    printf("ป้อนคะแนน (0-100): ");
    if (scanf("%d", &score) != 1) {
        puts("Input error");
        return 1;
    }
 
    if (score < 0 || score > 100) {
        printf("คะแนนไม่ถูกต้อง\n");
    } else if (score >= 80) {
        printf("เกรด A\n");
    } else if (score >= 70) {
        printf("เกรด B\n");
    } else if (score >= 60) {
        printf("เกรด C\n");
    } else {
        printf("เกรด F\n");
    }
    return 0;
}
  1. คอมไพล์และรันด้วยคำสั่ง แล้วทดสอบด้วยคะแนนหลายค่า (เช่น 85, 72, 55 และ 120)
BASH
gcc -std=c11 -Wall ch4_grade.c -o ch4_grade
./ch4_grade

ผลการรันจริง (ทดสอบแล้วด้วย gcc 13.3.0 ป้อนคะแนน 85, 72, 55 และ 120 ตามลำดับ)

TEXT
ป้อนคะแนน (0-100): เกรด A
ป้อนคะแนน (0-100): เกรด B
ป้อนคะแนน (0-100): เกรด F
ป้อนคะแนน (0-100): คะแนนไม่ถูกต้อง

สังเกตว่าเงื่อนไข score < 0 || score > 100 ถูกตรวจก่อนเป็นอันดับแรก จึงดักค่านอกช่วงอย่าง 120 ได้ ส่วนคะแนน 55 ไม่เข้าเงื่อนไข A/B/C ใด จึงตกไปที่ else เป็นเกรด F ตารางด้านล่างไล่การตรวจเงื่อนไขของทั้งสี่ค่าทดสอบทีละขั้น เครื่องหมาย — หมายถึงเงื่อนไขที่ไม่ถูกประเมินเพราะโปรแกรมหยุดที่เงื่อนไขก่อนหน้าที่เป็นจริงแล้ว

scorescore < 0 || score > 100score >= 80score >= 70score >= 60elseผล
850 (เท็จ)1 (จริง)———เกรด A
720 (เท็จ)0 (เท็จ)1 (จริง)——เกรด B
550 (เท็จ)0 (เท็จ)0 (เท็จ)0 (เท็จ)ทำงานเกรด F
1201 (จริง)————คะแนนไม่ถูกต้อง

ตัดสินใจเอง A.1 ปรับโปรแกรมให้เพิ่มเกรด D สำหรับคะแนนในช่วง 50–59 โดยแทรกเงื่อนไข else if ให้อยู่ในลำดับที่ถูกต้อง และปรับให้เงื่อนไขยังครอบคลุมทุกช่วงคะแนนโดยไม่ซ้อนทับและไม่ขาด

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

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

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

อาการสาเหตุวิธีแก้
เงื่อนไขไม่ตรงกับการเปรียบเทียบที่ตั้งใจใช้ = แทน == ในการเปรียบเทียบเปลี่ยนเป็น == เมื่อต้องการเปรียบเทียบความเท่ากัน
คะแนนสูงได้เกรดต่ำเรียงลำดับเงื่อนไขจากค่าต่ำขึ้นไปเรียง else if จากค่าสูงไปค่าต่ำ
ค่านอกช่วงได้เกรดแทนข้อความแจ้งเตือนไม่ได้ตรวจค่านอกช่วงก่อนใส่ score < 0 || score > 100 เป็นเงื่อนไขแรก

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

ใบงานตรวจ Lab A: ทดสอบรอบจุดเปลี่ยน

ก่อนรัน ให้เขียนเกณฑ์สมมติของฉบับพื้นฐาน A/B/C/F แยกจากฉบับที่เพิ่ม D ตามงานต่อยอด ไม่เปลี่ยนผลคาดของฉบับหนึ่งไปปนอีกฉบับ เมื่อใส่คะแนนนอก 0–100 ต้องไปเส้นทางปฏิเสธก่อนจัดระดับ

กลุ่มข้อมูลที่ควรทดลองผลพื้นฐานที่คาดผลเมื่อเพิ่ม D ที่คาดจุดที่กำลังตรวจ
-1, 0, 1ก่อนรันก่อนรันขอบเขตล่างและข้อมูลผิด
49, 50, 59, 60ก่อนรันก่อนรันจุดเพิ่ม D และขอบ C
69, 70, 79, 80ก่อนรันก่อนรันจุดเปลี่ยน C/B/A
99, 100, 101ก่อนรันก่อนรันขอบเขตบนและข้อมูลผิด

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

Lab B: เมนูด้วย switch

สถานการณ์ ต้องการรับหมายเลขเมนู 1–3 แล้วแสดงรายการที่เลือก พร้อมจัดการกรณีที่ป้อนหมายเลขนอกเหนือจากที่กำหนดด้วย default เพื่อฝึกการเลือกหลายทางด้วย switch

เป้าหมาย ฝึก LLO4.3 คือใช้ switch เลือกหลายทางจากค่าที่ไม่ต่อเนื่อง พร้อมวาง break และ default ให้ถูกต้อง

ขั้นปฏิบัติ

  1. สร้างไฟล์ชื่อ ch4_menu.c ด้วยโปรแกรมแก้ไขข้อความหรือ IDE
  2. พิมพ์ซอร์สโค้ดต่อไปนี้ โดยจัดย่อหน้า 4 ช่องว่างและใส่คอมเมนต์ส่วนหัว
C
/* ch4_menu.c : เลือกการทำงานด้วย switch */
#include <stdio.h>
 
int main(void) {
    int choice;
    printf("เลือกเมนู (1-3): ");
    if (scanf("%d", &choice) != 1) {
        puts("Input error");
        return 1;
    }
 
    switch (choice) {
        case 1:
            printf("เลือกเมนูที่ 1: ข้าวผัด\n");
            break;
        case 2:
            printf("เลือกเมนูที่ 2: ก๋วยเตี๋ยว\n");
            break;
        case 3:
            printf("เลือกเมนูที่ 3: ส้มตำ\n");
            break;
        default:
            printf("ไม่มีเมนูนี้\n");
    }
    return 0;
}
  1. คอมไพล์และรันด้วยคำสั่ง แล้วทดสอบด้วยหมายเลข 2 และ 5
BASH
gcc -std=c11 -Wall ch4_menu.c -o ch4_menu
./ch4_menu

ผลการรันจริง (ทดสอบแล้วด้วย gcc 13.3.0 ป้อนหมายเลข 2 และ 5 ตามลำดับ)

TEXT
เลือกเมนู (1-3): เลือกเมนูที่ 2: ก๋วยเตี๋ยว
เลือกเมนู (1-3): ไม่มีเมนูนี้

สังเกตว่าหมายเลข 2 ตรงกับ case 2 จึงแสดงก๋วยเตี๋ยวแล้วออกจาก switch ด้วย break ส่วนหมายเลข 5 ไม่ตรงกับ case ใด จึงไปทำคำสั่งใน default ตารางด้านล่างสรุปเส้นทางการทำงานของทั้งสองค่าทดสอบ

choicecase ที่ตรงคำสั่งที่ทำงานออกด้วย
2case 2เลือกเมนูที่ 2: ก๋วยเตี๋ยวbreak
5ไม่ตรง case ใดไม่มีเมนูนี้จบ switch

ตัดสินใจเอง B.1 เพิ่มเมนูที่ 4 ของตนเองลงในโปรแกรม โดยเขียน case 4 พร้อมข้อความและ break ให้ครบ แล้วทดสอบทั้งกรณีที่เลือกเมนู 4 และกรณีที่ป้อนหมายเลขนอกเหนือจากที่กำหนดเพื่อตรวจ default

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

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

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

อาการสาเหตุวิธีแก้
เลือกเมนูหนึ่งแต่แสดงหลายรายการลืม break ทำให้ไหลต่อ (fall-through)ใส่ break ปิดท้ายทุก case
ป้อนหมายเลขนอกช่วงแล้วไม่มีผลลัพธ์ไม่มี defaultเพิ่ม default เพื่อจัดการกรณีอื่น
คอมไพล์ไม่ผ่านที่ caseใช้ค่าที่ไม่ใช่ค่าคงที่จำนวนเต็มหรืออักขระใน caseใช้เฉพาะค่าคงที่จำนวนเต็มหรืออักขระกับ case

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

ใบงานตรวจ Lab B: เพิ่มเมนูโดยไม่ทำทางเดิมเสีย

เขียนรายการค่าที่รองรับก่อนเพิ่ม case 4 แล้วทดสอบเมนูเดิม 1–3 ซ้ำด้วย ตรวจทั้งเมนูใหม่และค่าที่ไม่ตรงป้ายใด เช่น 0 หรือ 5 รวมถึงข้อความแทนตัวเลข

รอบทดลองสิ่งที่ต้องบันทึก
ก่อนเพิ่มเมนูเส้นทางและข้อความของ 1–3
หลังเพิ่มเมนู 4ข้อความของ 4 และจุด break
ตรวจทางเดิมผลของ 1–3 ยังตรงกติกาเดิม
ตรวจค่าที่ไม่รองรับไป default โดยไม่แสดงเมนูอื่น
อ่านไม่สำเร็จรายงานปัญหาการอ่าน ไม่ใช้ค่าไปเข้า switch

การทดลองหนึ่งครั้งเลือกหนึ่งเมนูตามงานเดิม ไม่ต้องเพิ่มลูปหรือระบบเมนูซ้ำในบทนี้ หลังแก้แต่ละครั้งให้แปลและรันฉบับใหม่ พร้อมรักษาคำทำนายก่อนทดลองไว้ใช้เทียบหลักฐาน

เก็บกวาด

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

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

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

  1. ค่าที่ใช้เป็นเงื่อนไขกับผลของตัวเปรียบเทียบใน C ต่างกันอย่างไร ใช้ค่าลบประกอบคำอธิบาย
  2. ทำไมการตรวจว่าอยู่ในช่วงจึงต่างจากการตรวจว่านอกช่วง
  3. ยกสองเหตุผลที่ทำให้เงื่อนไขลัดวงจรตัวอย่างเป็นเท็จ โดยไม่คิดผลหารที่ไม่ควรถูกประเมิน
  4. เมื่อไรควรใช้ if สองตัวอิสระ และเมื่อไรควรเลือกสาย else if
  5. กรณีรอบจุดเปลี่ยนใดช่วยพบความผิดของเครื่องหมายรวมขอบเขต
  6. ป้าย case, break และ default ทำหน้าที่ต่างกันอย่างไร

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

สรุป

บทนี้นำการรับข้อมูลจากบทที่ 3 และตัวดำเนินการจากบทที่ 2 มาต่อยอดให้โปรแกรมตัดสินใจเลือกเส้นทางการทำงานได้ เริ่มจากตัวดำเนินการเปรียบเทียบ (< <= > >= == !=) และตรรกะ (&&, \|\|, !) ที่ให้ผลเป็นจริง (1) หรือเท็จ (0) พร้อมพฤติกรรมลัดวงจรและความต่างระหว่าง == กับ = ต่อด้วยคำสั่ง if และ if-else สำหรับสองทางเลือก if-else if และ nested if สำหรับหลายทางเลือกโดยเน้นลำดับการตรวจเงื่อนไข และปิดท้ายด้วย switch สำหรับเลือกจากค่าคงที่ไม่ต่อเนื่องพร้อมบทบาทของ break และ default ทักษะเหล่านี้เป็นพื้นฐานที่จะใช้ร่วมกับโครงสร้างควบคุมแบบวนซ้ำในบทถัดไป

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

สิ่งที่ส่งต่อค่า/รูปแบบใช้ในบท
ตัวดำเนินการเปรียบเทียบและตรรกะ< <= > >= == !=, &&, ||, !บท 5 เป็นต้นไป (เงื่อนไขของลูป)
คำสั่ง if, if-else, if-else if, nested ifif (เงื่อนไข) { ... } else { ... }บท 5–6 เป็นต้นไป
คำสั่ง switch พร้อม break และ defaultswitch (นิพจน์) { case ค่า: ... break; default: ... }ทุกบทที่เลือกจากค่าคงที่ไม่ต่อเนื่อง

สรุปคำสั่ง

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

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

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

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

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

  • ก1 (LLO4.1) จงอธิบายความหมายของตัวดำเนินการเปรียบเทียบ < <= > >= == != พร้อมระบุผลลัพธ์ที่เป็นจริงหรือเท็จของแต่ละตัวอย่าง
  • ก2 (LLO4.1) จงอธิบายความต่างระหว่าง &&, \|\| และ ! พร้อมยกตัวอย่างนิพจน์เงื่อนไขของแต่ละตัว

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

  • ข1 (LLO4.2) จงเขียนโปรแกรมภาษา C ที่รับจำนวนเต็มหนึ่งจำนวนด้วย scanf แล้วใช้ if-else ตรวจและแสดงว่าจำนวนนั้นเป็นเลขคู่หรือเลขคี่ แล้วคอมไพล์และรันให้ได้ผลจริง
  • ข2 (LLO4.3) จงเขียนโปรแกรมที่รับหมายเลขวันในสัปดาห์ (1–7) ด้วย scanf แล้วใช้ switch แสดงชื่อวัน พร้อมใส่ default สำหรับหมายเลขที่ไม่ถูกต้อง

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

  • ค1 (LLO4.2) จากโปรแกรมตัดเกรดที่เรียงเงื่อนไข if-else if จากค่าต่ำไปค่าสูง จงวิเคราะห์ว่าเหตุใดผลลัพธ์จึงผิด และเสนอวิธีแก้ให้ถูกต้อง

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

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

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

cppreference.com. (2024). if statement, switch statement, comparison operators, logical operators — C language reference. สืบค้นจาก https://en.cppreference.com/w/c/language

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).

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

  • GNU C: Logical Operators — ค่าจริง ผลตรรกะ และการลัดวงจร
  • GNU C: if Statement — การเลือกแขนงจากค่าศูนย์หรือไม่เป็นศูนย์
  • GNU C: switch Statement — ป้าย case การไหลต่อและ break โดยแยกส่วนขยาย GNU ออกจาก C11

ตรวจเนื้อหาเอกสารก่อนเขียนส่วนขยายเมื่อ 23 ตุลาคม 2569 ตัวอย่างใหม่และฉบับเพิ่มการตรวจผลอ่านยืนยันด้วย GCC 12.2.0 (Debian Linux) และ GCC 6.3.0 (MinGW-w64) แยกจากผลตัวอย่างเดิมที่ระบุ GCC 13.3.0 ใช้จำนวนเต็มที่แทนด้วยชนิดได้และไม่รันกรณีหารด้วยศูนย์หรือข้อมูลที่มี undefined behavior เพื่อคาดหวังอาการ

By Watcharin Sarachai