← กลับไปยังบทความทั้งหมด

ทำความรู้จักกับ 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 ของตนเอง และไม่ส่งผลกระทบต่อกัน จึงมีความปลอดภัยในการใช้งานแบบหลายเธรด