ทำความรู้จักกับ errno.h ในภาษา C การจัดการข้อผิดพลาดอย่างมืออาชีพ
สรุปใจความสำคัญ
- errno คือตัวแปรที่ใช้เก็บรหัสข้อผิดพลาดในภาษา C โดยมีค่าเริ่มต้นเป็น 0
- ฟังก์ชันในไลบรารีมาตรฐานจะกำหนดค่าให้ errno เป็นค่าที่มากกว่า 0 เมื่อเกิดข้อผิดพลาด
- การใช้ perror() และ strerror() ช่วยให้ผู้เขียนโปรแกรมสามารถแปลงรหัสตัวเลขเป็นข้อความที่อธิบายข้อผิดพลาดได้
- ในระบบ POSIX และ Linux รหัสข้อผิดพลาดจะมีจำนวนมากกว่าที่มาตรฐาน C กำหนดไว้เพื่อรองรับการทำงานระดับระบบปฏิบัติการ
ในการเขียนโปรแกรมด้วยภาษา C การจัดการกับข้อผิดพลาด (Error Handling) เป็นสิ่งสำคัญอย่างยิ่ง เพื่อให้โปรแกรมทำงานได้อย่างเสถียรและสามารถระบุปัญหาที่เกิดขึ้นได้อย่างแม่นยำ หนึ่งในเครื่องมือหลักที่ภาษา C เตรียมไว้ให้คือไฟล์ส่วนหัว <errno.h> ซึ่งเป็นส่วนหนึ่งของไลบรารีมาตรฐานของภาษา C
อะไรคือ errno.h?
<errno.h> เป็นไฟล์ส่วนหัวที่กำหนดแมโคร (macros) สำหรับการรายงานและเรียกคืนสถานะข้อผิดพลาด โดยใช้สัญลักษณ์ errno (ย่อมาจาก "error number") ซึ่งทำหน้าที่เหมือนกับตัวแปรจำนวนเต็ม (integer variable) ที่ใช้เก็บค่ารหัสข้อผิดพลาดเมื่อฟังก์ชันในไลบรารีตรวจพบปัญหาในการทำงาน
การทำงานของ errno
เมื่อโปรแกรมเริ่มทำงาน ค่าของ errno จะถูกกำหนดให้เป็นศูนย์ (0) และฟังก์ชันในไลบรารีจะเก็บค่าที่มากกว่าศูนย์ลงใน errno เมื่อตรวจพบข้อผิดพลาด โดยมีหลักการทำงานดังนี้:
- การระบุข้อผิดพลาด: ฟังก์ชันส่วนใหญ่จะระบุว่าเกิดข้อผิดพลาดโดยการคืนค่าพิเศษ เช่น
NULLสำหรับฟังก์ชันที่คืนค่าเป็นพอยน์เตอร์ (pointers) หรือ-1สำหรับฟังก์ชันที่คืนค่าเป็นจำนวนเต็ม - การเปลี่ยนแปลงค่า: ฟังก์ชันใดๆ ในไลบรารีสามารถเปลี่ยนแปลงค่า
errnoได้ก่อนที่จะส่งคืนค่ากลับมา ไม่ว่าฟังก์ชันนั้นจะตรวจพบข้อผิดพลาดหรือไม่ก็ตาม - การรีเซ็ตค่า: ในบางกรณี ผู้เขียนโปรแกรมจำเป็นต้องกำหนดค่า
errnoให้เป็นศูนย์ก่อนเรียกใช้ฟังก์ชัน และตรวจสอบค่าหลังจากนั้นเพื่อยืนยันว่าเกิดข้อผิดพลาดขึ้นจริง
โครงสร้างและประเภทข้อมูล
ในปัจจุบัน แมโคร errno มักจะถูกขยายเป็น lvalue ที่มีประเภทเป็น int โดยอาจมีการใช้ตัวระบุประเภท extern หรือ volatile ขึ้นอยู่กับแพลตฟอร์ม เพื่อรองรับการทำงานแบบหลายเธรด (Multi-threading) ทำให้แต่ละเธรดสามารถมีรหัสข้อผิดพลาดของตนเองได้ (Thread-local error number)
รหัสข้อผิดพลาดและมาตรฐาน POSIX
ไฟล์ส่วนหัว <errno.h> กำหนดแมโครที่ขยายเป็นค่าคงที่จำนวนเต็มเพื่อแทนรหัสข้อผิดพลาดต่างๆ แม้ว่ามาตรฐาน C จะกำหนดให้มีรหัสพื้นฐานเพียงไม่กี่ตัว แต่ระบบปฏิบัติการที่รองรับมาตรฐาน POSIX (เช่น Linux, AIX หรือ Solaris) จะมีการกำหนดรหัสข้อผิดพลาดเพิ่มเติมอีกมากมาย เช่น EACCES ซึ่งใช้เมื่อไม่สามารถเปิดไฟล์เพื่ออ่านได้
การแปลรหัสข้อผิดพลาดเป็นข้อความ
เนื่องจากตัวเลขรหัสข้อผิดพลาดนั้นเข้าใจยาก ผู้เขียนโปรแกรมสามารถแปลงค่า errno ให้เป็นข้อความที่อธิบายรายละเอียดได้โดยใช้เครื่องมือดังนี้:
- strerror: ฟังก์ชันที่กำหนดใน
<string.h>ใช้สำหรับแปลงรหัสข้อผิดพลาดเป็นสตริงข้อความ - perror: ฟังก์ชันที่กำหนดใน
<stdio.h>ใช้สำหรับพิมพ์ข้อความข้อผิดพลาดออกทางหน้าจอ (standard error stream) โดยตรง - strerror_r: เวอร์ชันที่ปลอดภัยสำหรับการทำงานแบบหลายเธรด (Thread-safe) เพื่อหลีกเลี่ยงปัญหาที่เกิดขึ้นกับ
strerrorในระบบ Unix-like
ข้อควรระวังในการใช้งาน
สำหรับผู้ที่ใช้งาน GNU C Library (GLIBC) รหัสข้อผิดพลาด POSIX จะถูกรวมอยู่ใน <errno.h> ด้วย อย่างไรก็ตาม ไม่ควรยึดติดกับค่าตัวเลขของรหัสข้อผิดพลาด เนื่องจากค่าตัวเลขอาจแตกต่างกันไปตามเวอร์ชันของ Unix หรือแพลตฟอร์มที่ใช้งาน ควรใช้ชื่อแมโคร (เช่น EACCES, ENOENT) แทนการใช้ตัวเลขโดยตรง เพื่อให้โค้ดมีความสามารถในการพกพา (Portability) และอ่านง่ายขึ้น
คำถามที่พบบ่อย
errno คืออะไร?
errno คือแมโครในไฟล์ส่วนหัว <errno.h> ของภาษา C ที่ทำหน้าที่เป็นตัวแปรจำนวนเต็มสำหรับเก็บรหัสข้อผิดพลาดที่เกิดขึ้นจากการเรียกใช้ฟังก์ชันในไลบรารีมาตรฐาน
ทำไมต้องใช้ perror() แทนที่จะพิมพ์ค่า errno โดยตรง?
เพราะค่า errno เป็นตัวเลขจำนวนเต็ม ซึ่งเข้าใจยากสำหรับผู้ใช้และนักพัฒนา การใช้ perror() จะช่วยพิมพ์ข้อความอธิบายข้อผิดพลาดที่สอดคล้องกับรหัสนั้นๆ ออกทาง standard error stream โดยอัตโนมัติ
errno ปลอดภัยสำหรับการทำงานแบบ Multi-threading หรือไม่?
ในคอมไพเลอร์และไลบรารีสมัยใหม่ errno มักจะถูกนำเสนอในรูปแบบของ Thread-local storage ทำให้แต่ละเธรดมีค่า errno ของตนเอง และไม่ส่งผลกระทบต่อกัน จึงมีความปลอดภัยในการใช้งานแบบหลายเธรด