บทนำ: ทำไมการตั้งชื่อถึงเป็น “กระดุมเม็ดแรก” ของการเขียนโค้ด?

สวัสดีครับน้องๆ วิศวกรและเพื่อนนักพัฒนาชาว www.123microcontroller.com ทุกคน! วันนี้วิศวกรขอบตาดำๆ จะมาชวนคุยเรื่องที่ดูเหมือนจะเป็นแค่เรื่องของสไตล์หรือความสวยงาม แต่แท้จริงแล้วมันคือ “กระดุมเม็ดแรก” ของการเขียนโค้ดฝังตัวให้ปลอดภัยไร้บั๊ก นั่นก็คือเรื่องของ Naming Conventions (ธรรมเนียมการตั้งชื่อ) ครับ

ในการเขียนโปรแกรมระดับฮาร์ดแวร์ การตั้งชื่อก็เหมือนกับการทำ Label หรือติดป้ายสายไฟในตู้คอนโทรล หากเราตั้งชื่อได้ดี โค้ดของเราก็จะอ่านง่าย เข้าใจได้ทันที และป้องกันการต่อสายผิด! ในบริบทของ Writing Safer C การใช้ Snake case หรือการเติม Prefixes ไม่ใช่แค่เรื่องของความชอบส่วนตัวครับ แต่มันคือ “กลยุทธ์ทางวิศวกรรม” ที่ปรมาจารย์สาย C ใช้เพื่อจัดการกับจุดอ่อนของภาษา C และหลีกเลี่ยงการชนกันของชื่อ (Name collisions) วันนี้เรามาดูเหตุผลเบื้องหลังของกฎเหล่านี้กันครับ!

ทำไมการตั้งชื่อถึงช่วยให้โค้ดปลอดภัยขึ้น?

ภาษา C เป็นภาษาที่ทรงพลังแต่ก็มีความดิบเถื่อนแฝงอยู่ ประการแรกคือภาษา C ไม่มีระบบจัดการขอบเขตชื่อ (Namespaces) แบบเดียวกับที่ภาษา C++ มี ด้วยเหตุนี้ ฟังก์ชันและตัวแปรแบบ Global ทั้งหมดจึงถูกเทรวมลงไปในพื้นที่เดียวกัน ทำให้เกิดความวุ่นวายและเสี่ยงต่อการตั้งชื่อซ้ำซ้อนได้ง่ายมาก

เพื่อให้ระบบมีโครงสร้างที่เป็นระเบียบ (Writing Safer C) แหล่งข้อมูลระดับโลกและมาตรฐานอย่าง BARR-C ได้แนะนำเทคนิคการตั้งชื่อเพื่อควบคุมความเสี่ยงไว้ดังนี้ครับ:

  • Snake Case ทรงพลังและเป็นมาตรฐานดั้งเดิม: ในโลกของ C การใช้ตัวพิมพ์เล็กทั้งหมดแล้วคั่นคำด้วยเครื่องหมายขีดล่าง (Underscore) หรือที่เรียกว่า Snake case (เช่น list_count หรือ hours_worked) ถือเป็นรูปแบบเกือบจะสากล (Almost universal) สำหรับการตั้งชื่อตัวแปรและฟังก์ชัน แม้จะมีนักพัฒนาบางกลุ่มชอบใช้ Camel case (เช่น currentPage) แต่หัวใจสำคัญคือคุณต้องทำให้ชื่อสามารถอ่านและแยกแยะได้ง่ายที่สุดเพื่อลดข้อผิดพลาด
  • การใช้ Prefix เพื่อจำลอง Namespace และทำ Encapsulation: เนื่องจาก C ไม่มี Namespace เราจึงต้องใช้ Prefix (คำนำหน้า) เพื่อจัดระเบียบโค้ด!
    • Module Prefixes: มาตรฐาน BARR-C แนะนำให้ตั้งชื่อฟังก์ชันที่เป็นแบบ Public โดยนำหน้าด้วยชื่อโมดูลของมัน (เช่น sensor_read()) เพื่อหลีกเลี่ยงการตั้งชื่อซ้ำซ้อนกันในระบบ และเป็นการบ่งบอกว่าฟังก์ชันนี้จัดการกับข้อมูล (Data/Nouns) ก้อนไหนอยู่
    • Struct/Type Prefixes: สำหรับฟังก์ชันที่ทำงานกับ Struct เฉพาะเจาะจง มักจะใช้ชื่อ Struct เป็น Prefix (เช่น list_is_empty(struct list* lst))
    • Struct Member Prefixes: ย้อนกลับไปในอดีต คอมไพเลอร์ C มีตารางเก็บชื่อ (Global symbol table) แค่ตารางเดียว การใช้ Prefix หน้าชื่อสมาชิก (Member) ใน Struct จึงช่วยกันการชนกันของชื่อได้ และในปัจจุบัน การใช้ Prefix หน้า Member ก็ยังช่วยป้องกันปัญหาการไปตั้งชื่อซ้ำกับ Macro ที่นักพัฒนาคนอื่นอาจจะสร้างไว้ใน Header file ได้อีกด้วย (เช่น โครงสร้าง struct timespec ใช้ member ชื่อ tv_sec แทนที่จะเป็น sec เฉยๆ)
  • Screaming Snake Case สำหรับ Macros และ Constants: เพื่อแยกความแตกต่างระหว่างตัวแปรในโปรแกรมกับคำสั่งที่ทำงานโดย Preprocessor กฎข้อสำคัญคือให้ใช้ตัวพิมพ์ใหญ่ทั้งหมดคั่นด้วย Underscore (เช่น MAX_BUFFER_SIZE) สำหรับ Macro และค่าคงที่เสมอ การทำเช่นนี้ทำให้ผู้อ่านโค้ดรู้ทันทีว่าตัวระบุ (Identifier) นี้น่าจะถูกจัดการโดย Preprocessor และห้ามนำไปใช้งานผิดประเภท

Naming Conventions Diagram

ตัวอย่างการใช้ Naming Conventions ในโค้ดจริง

มาดูตัวอย่างการเขียนโค้ดควบคุมโมดูลเซ็นเซอร์แบบ Clean Code ที่นำแนวทาง Naming Conventions มาประยุกต์ใช้เพื่อความปลอดภัยกันครับ

#include <stdint.h>
#include <stdbool.h>

/* ✅ BEST PRACTICE: ใช้ Screaming Snake Case สำหรับ Constants และ Macros */
#define SENSOR_MAX_RETRY_COUNT (5U)

/* 
 * ✅ BEST PRACTICE: ใช้ Snake case และตั้งชื่อให้สื่อความหมายชัดเจน 
 * (ใช้ sensor_data แทนที่จะเป็น sd)
 */
typedef struct {
    uint32_t raw_value;
    bool     is_active;
} sensor_data_t;

/* 
 * ✅ BEST PRACTICE: ใช้ Module Prefix ('sensor_') นำหน้าฟังก์ชัน Public
 * เพื่อป้องกัน Name collision กับฟังก์ชัน read/init ของโมดูลอื่นๆ 
 * และใช้รูปแบบคำกริยา (Verb) ในการบ่งบอกพฤติกรรม
 */
bool sensor_init(sensor_data_t *sensor) {
    if (sensor == NULL) {
        return false;
    }
    
    // ✅ ตัวแปรภายในใช้ Snake case ที่สื่อความหมาย
    uint8_t retry_count = 0; 
    
    while (retry_count < SENSOR_MAX_RETRY_COUNT) {
        /* สมมติว่านี่คือการรีเซ็ตฮาร์ดแวร์ */
        sensor->raw_value = 0;
        sensor->is_active = true;
        retry_count++;
        break;
    }
    return sensor->is_active;
}

ข้อควรระวังในการตั้งชื่อที่วิศวกรต้องรู้

นอกจากการตั้งชื่อให้อ่านง่ายแล้ว มาตรฐาน ISO C และคู่มือ Secure Coding ยังมี “ข้อห้าม” ในการตั้งชื่อที่คุณต้องรู้เพื่อไม่ให้โค้ดเกิด Undefined Behavior ครับ:

  1. ห้ามแย่งพื้นที่สงวนของระบบ (Reserved Identifiers): คุณต้อง ห้าม ตั้งชื่อ Identifier ที่ขึ้นต้นด้วยเครื่องหมาย Underscore สองตัวติดกัน (__) หรือขึ้นต้นด้วย Underscore ตามด้วยตัวอักษรพิมพ์ใหญ่ (เช่น _Variable) โดยเด็ดขาด! เพราะชื่อเหล่านี้ถูก “สงวน” ไว้ให้คอมไพเลอร์หรือมาตรฐาน C ในอนาคตใช้ การไปตั้งชื่อทับอาจทำให้ระบบพังแบบไม่มีสาเหตุได้
  2. ตั้งชื่อที่อธิบายความหมาย (Descriptive Names): อย่ากลัวที่จะใช้ชื่อยาวๆ! C ไม่มีข้อจำกัดเรื่องความยาวสูงสุดของชื่อ ดังนั้นจงใช้ชื่ออย่าง current_page แทนที่จะใช้ตัวย่ออย่าง cp ซึ่งอ่านแล้วกำกวมและเดาความหมายได้ยาก
  3. การตั้งชื่อ Handle ต่างๆ (BARR-C Rule): หากคุณอ้างอิงมาตรฐาน BARR-C ตัวแปรใดๆ ที่ทำหน้าที่เป็น “Handle” หรือตัวจัดการออบเจกต์ที่ไม่ใช่ Pointer (เช่น file handles) ควรใช้ Prefix ขึ้นต้นด้วยตัว h_ เช่น h_input_file เพื่อให้โปรแกรมเมอร์รู้ทันทีว่ากำลังจัดการกับทรัพยากรประเภทไหน
  4. ความสม่ำเสมอคือหัวใจ (Consistency): ไม่ว่าโปรเจกต์ของคุณจะตัดสินใจใช้ Snake case, Camel case หรือรูปแบบผสมแบบใด ขอให้ “เลือกและใช้ให้เหมือนกันทั้งโปรเจกต์” เพราะการที่รูปแบบสะเปะสะปะ (เช่น makegreen(), makeGreen(), และ makeGREEN()) จะสร้างความสับสนอย่างรุนแรงและนำไปสู่ข้อผิดพลาดของโปรแกรมเมอร์ได้ง่ายมาก

สรุป (Conclusion)

ในการก้าวไปสู่การเป็นวิศวกรสายแข็งที่เขียน Safer C ได้นั้น การตั้งชื่อ (Naming Conventions) ไม่ใช่เรื่องของรสนิยม แต่เป็น “เครื่องมือ” ที่เราใช้จัดการกับความดิบของภาษา C การใช้ Snake case และ Prefixes จะช่วยสร้างเขตแดนของตัวแปร ลดการชนกันของชื่อ และทำให้เพื่อนร่วมทีมที่มาอ่านโค้ดต่อจากเราสามารถทำความเข้าใจตรรกะของฮาร์ดแวร์ได้ง่ายขึ้นราวกับมีพิมพ์เขียวชั้นดีครับ

ถ้าเพื่อนๆ ชอบบทความที่เจาะลึกทั้งทฤษฎีและมาตรฐานสากลแบบนี้ อย่าลืมแวะเข้ามาอัปเดตความรู้ พูดคุยแชร์เทคนิค และแชร์โค้ดสนุกๆ กันต่อที่เว็บ www.123microcontroller.com ของเรานะครับ การเขียนโปรแกรมที่ดีเริ่มต้นที่การตั้งชื่อที่ใช่! แล้วพบกันใหม่ในบทความหน้า Happy Coding ครับ!