- 1 1. บทนำ
- 2 2. malloc คืออะไร?
- 3 3. วิธีการใช้งานพื้นฐานของ malloc
- 4 4. ความสำคัญของการคืนหน่วยความจำด้วย free()
- 5 5. ความสำคัญของการตรวจสอบ NULL
- 6 6. ความแตกต่างระหว่าง malloc และ calloc
- 7 7. ตัวอย่างการใช้งานจริง: การจัดสรรสตริงแบบไดนามิกด้วย malloc
- 8 8. การใช้งาน malloc กับโครงสร้าง (Struct)
- 9 9. ข้อผิดพลาดที่พบบ่อยในการใช้งาน malloc
- 10 10. สรุป
1. บทนำ
เมื่อเริ่มเขียนโปรแกรมด้วยภาษา C ในตอนแรกมักจะจัดการหน่วยความจำโดยใช้อาร์เรย์เป็นส่วนใหญ่ อย่างไรก็ตาม เมื่อโปรแกรมซับซ้อนมากขึ้น ก็มีสถานการณ์ที่ต้องการจัดการหน่วยความจำได้อย่างยืดหยุ่นมากขึ้น ในสถานการณ์เช่นนั้น “การจัดสรรหน่วยความจำแบบไดนามิก” จะเข้ามามีบทบาทสำคัญ malloc
เป็นฟังก์ชันที่เป็นตัวแทน ซึ่งช่วยให้สามารถจัดสรรหน่วยความจำที่จำเป็นในระหว่างการทำงานของโปรแกรมแบบไดนามิกได้
ยกตัวอย่างเช่น malloc
เปรียบเสมือน “อาหารที่ทำหลังจากสั่ง” ส่วนหน่วยความจำที่กำหนดไว้ล่วงหน้า (อาร์เรย์) อาจเปรียบได้กับ “อาหารแบบบุฟเฟต์” กระบวนการพื้นฐานคือการ “สั่ง” หน่วยความจำเท่าที่คุณต้องการโดยใช้ malloc
และเมื่อใช้งานเสร็จแล้วก็ “นำจานออกไป (คืนหน่วยความจำด้วยฟังก์ชัน free)” ในบทความนี้ เราจะมาดูรายละเอียดเกี่ยวกับ malloc
กัน
2. malloc
คืออะไร?
malloc
เป็นตัวย่อของ “memory allocation” (การจัดสรรหน่วยความจำ) และเป็นฟังก์ชันสำหรับจัดสรรหน่วยความจำแบบไดนามิกในภาษา C ในระหว่างการทำงานของโปรแกรม จะจัดสรรหน่วยความจำตามขนาดที่ระบุและส่งคืนที่อยู่เริ่มต้นของหน่วยความจำนั้น ซึ่งช่วยให้สามารถใช้หน่วยความจำเท่าที่จำเป็นระหว่างการทำงานของโปรแกรม และสามารถจัดการหน่วยความจำได้อย่างยืดหยุ่น ซึ่งเป็นเรื่องยากสำหรับอาร์เรย์ที่มีขนาดคงที่
ในโค้ดจริง จะใช้ malloc
ดังนี้
int *array = (int*)malloc(10 * sizeof(int));
ในตัวอย่างนี้ ได้จัดสรรอาร์เรย์ประเภทจำนวนเต็มจำนวน 10 ตัว ที่นี่สิ่งสำคัญคือ malloc
ส่งคืนที่อยู่เริ่มต้นของหน่วยความจำที่จัดสรร ดังนั้น ประเภทอาจไม่ตรงกันตามปกติ ดังนั้นจึงเป็นเรื่องปกติที่จะทำการแปลงประเภท (cast) ให้เป็นประเภทที่ต้องการ ข้างต้นใช้ (int*)
เพื่อแปลงเป็นพอยน์เตอร์ประเภทจำนวนเต็ม
3. วิธีการใช้งานพื้นฐานของ malloc
ตอนนี้ เรามาดูวิธีการใช้งาน malloc
อย่างละเอียดกันหน่อย ก่อนอื่น รูปแบบการใช้งานพื้นฐานของ malloc
เป็นดังนี้
void* malloc(size_t size);
ฟังก์ชัน malloc
รับอาร์กิวเมนต์คือขนาดของหน่วยความจำที่ต้องการจัดสรร (จำนวนไบต์) จากนั้นจะจัดสรรพื้นที่หน่วยความจำขนาดนั้น และหากสำเร็จ จะส่งคืนที่อยู่เริ่มต้นของพื้นที่นั้น ประเภทที่ส่งคืนคือ void*
ซึ่งเป็นพอยน์เตอร์ทั่วไปที่สามารถแปลงเป็นประเภทใดก็ได้ ตัวอย่างเช่น ใช้ดังนี้
int *array = (int*)malloc(10 * sizeof(int));
ที่นี่ sizeof(int)
ใช้เพื่อหาขนาดของหน่วยความจำที่จะจัดสรร การทำเช่นนี้ช่วยให้สามารถจัดสรรหน่วยความจำขนาดที่ถูกต้องได้ในสภาพแวดล้อมที่แตกต่างกัน เมื่อใช้หน่วยความจำที่จัดสรรแล้ว สิ่งสำคัญคือต้องคืนหน่วยความจำนั้นโดยใช้ฟังก์ชัน free
หากไม่คืน หน่วยความจำรั่วไหล (memory leak) จะเกิดขึ้น

4. ความสำคัญของการคืนหน่วยความจำด้วย free()
การจัดสรรหน่วยความจำแบบไดนามิกสะดวกจริง แต่มีข้อควรระวังหนึ่งข้อ คือ ต้องไม่ลืมคืนหน่วยความจำที่จัดสรรไว้ หากละเลย จะเกิดหน่วยความจำรั่วไหล ทำให้โปรแกรมสิ้นเปลืองหน่วยความจำจำนวนมาก
หน่วยความจำที่จัดสรรด้วย malloc
จะถูกคืนด้วย free()
ดังนี้
free(array);
หน่วยความจำที่ไม่ถูกคืนจะยังคงอยู่ในระบบจนกว่าโปรแกรมจะสิ้นสุด ซึ่งอาจกลายเป็นปัญหาสำคัญสำหรับโปรแกรมที่ทำงานเป็นเวลานาน หากเปรียบเทียบก็เหมือนกับการยืมจานด้วย malloc
และหากไม่คืนจานให้เรียบร้อยด้วย free
ห้องครัวก็จะเต็มไปด้วยจาน
5. ความสำคัญของการตรวจสอบ NULL
ฟังก์ชัน malloc
จะส่งคืนค่า NULL
หากไม่สามารถจัดสรรหน่วยความจำได้ เช่น ในกรณีที่ต้องการจัดสรรหน่วยความจำขนาดใหญ่เกินไปจนระบบไม่สามารถจัดสรรได้ เมื่อใช้ malloc
สิ่งสำคัญคือต้องตรวจสอบค่า NULL
เสมอ เพื่อยืนยันว่าหน่วยความจำถูกจัดสรรอย่างถูกต้องหรือไม่ ซึ่งเป็นวิธีการเขียนโปรแกรมที่ปลอดภัย
int *array = (int*)malloc(100000000 * sizeof(int));
if (array == NULL) {
// การจัดการเมื่อจัดสรรหน่วยความจำล้มเหลว
printf("Memory allocation failed.
");
return 1;
}
การตรวจสอบเช่นนี้ช่วยให้สามารถจัดการข้อผิดพลาดเมื่อการจัดสรรหน่วยความจำล้มเหลว การเพิ่มมาตรการความปลอดภัยเล็กน้อยในโค้ดจะช่วยป้องกันปัญหาใหญ่ที่อาจเกิดขึ้นในภายหลังได้
6. ความแตกต่างระหว่าง malloc
และ calloc
ในภาษา C มีฟังก์ชันอื่น ๆ สำหรับการจัดสรรหน่วยความจำแบบไดนามิก นอกเหนือจาก malloc
หนึ่งในนั้นคือ calloc
malloc
และ calloc
มีความคล้ายคลึงกันมาก แต่มีความแตกต่างที่สำคัญบางประการ malloc
จะจัดสรรหน่วยความจำตามขนาดที่ระบุเท่านั้น และเนื้อหาภายในจะไม่ได้รับการเริ่มต้น ในทางกลับกัน calloc
จะจัดสรรหน่วยความจำพร้อมทั้งเริ่มต้นหน่วยความจำที่จัดสรรทั้งหมดด้วยค่าศูนย์
วิธีการใช้งาน calloc
int *array = (int*)calloc(10, sizeof(int));
โค้ดนี้จะจัดสรรอาร์เรย์ประเภทจำนวนเต็มจำนวน 10 ตัว และเริ่มต้นแต่ละองค์ประกอบด้วยค่าศูนย์ ความแตกต่างหลักจาก malloc
คือ calloc
รับอาร์กิวเมนต์สองตัวคือ “จำนวนองค์ประกอบ” และ “ขนาดขององค์ประกอบ” เหตุผลที่โครงสร้างนี้สะดวกคือ ช่วยให้สามารถจัดสรรหน่วยความจำได้อย่างชัดเจนยิ่งขึ้นเมื่อจัดการข้อมูลที่มีหลายองค์ประกอบ เช่น อาร์เรย์
การเลือกใช้ฟังก์ชันใดขึ้นอยู่กับสถานการณ์ แต่หากต้องการการเริ่มต้นค่า calloc
จะสะดวกกว่า ในทางกลับกัน หากไม่ต้องการการเริ่มต้นค่า หรือให้ความสำคัญกับประสิทธิภาพ malloc
จะเหมาะสมกว่า
7. ตัวอย่างการใช้งานจริง: การจัดสรรสตริงแบบไดนามิกด้วย malloc
ในส่วนนี้ เราจะมาดูตัวอย่างการจัดสรรหน่วยความจำแบบไดนามิกสำหรับสตริงโดยใช้ malloc
เมื่อจัดการสตริงในภาษา C โดยปกติจะใช้อาร์เรย์ที่มีขนาดคงที่ อย่างไรก็ตาม หากความยาวของสตริงไม่ทราบจนกว่าจะถึงเวลาทำงาน หรือต้องการจัดการสตริงแบบไดนามิก malloc
จะมีประโยชน์
char *str = (char*)malloc(50 * sizeof(char));
if (str == NULL) {
printf("Memory allocation failed.
");
return 1;
}
sprintf(str, "Hello, World!");
printf("%s
", str);
free(str);
โค้ดนี้จะจัดสรรหน่วยความจำขนาด 50 ตัวอักษรแบบไดนามิก และเก็บสตริง “Hello, World!” ลงในพื้นที่นั้น อย่าลืมคืนหน่วยความจำด้วยฟังก์ชัน free
หลังจากใช้งาน การใช้ malloc
ช่วยให้สามารถจัดการหน่วยความจำได้อย่างยืดหยุ่น ซึ่งเป็นไปไม่ได้ด้วยอาร์เรย์ขนาดคงที่
8. การใช้งาน malloc
กับโครงสร้าง (Struct)
ต่อไป เรามาดูตัวอย่างการใช้ malloc
เพื่อจัดสรรหน่วยความจำแบบไดนามิกสำหรับโครงสร้าง (struct) โครงสร้างเป็นประเภทข้อมูลที่มีประสิทธิภาพซึ่งสามารถจัดการข้อมูลหลายประเภทพร้อมกันได้ และสามารถจัดการหน่วยความจำได้แบบไดนามิกเช่นกัน
typedef struct {
int id;
char *name;
} Person;
Person *p = (Person*)malloc(sizeof(Person));
if (p == NULL) {
printf("Memory allocation failed.
");
return 1;
}
p->name = (char*)malloc(50 * sizeof(char));
sprintf(p->name, "John Doe");
p->id = 1;
printf("ID: %d, Name: %s
", p->id, p->name);
free(p->name);
free(p);
โค้ดนี้จะจัดสรรหน่วยความจำแบบไดนามิกสำหรับโครงสร้าง Person
และยังจัดสรรหน่วยความจำแบบไดนามิกเพิ่มเติมสำหรับตัวแปรสมาชิก name
ด้วย ด้วยวิธีนี้ การใช้ malloc
ตามความจำเป็นสำหรับสมาชิกแต่ละตัวของโครงสร้าง ทำให้สามารถจัดการหน่วยความจำได้อย่างยืดหยุ่น
9. ข้อผิดพลาดที่พบบ่อยในการใช้งาน malloc
เราจะพูดถึงข้อผิดพลาดที่ผู้เริ่มต้นมักทำเมื่อใช้ malloc
ด้วย การหลีกเลี่ยงข้อผิดพลาดเหล่านี้จะช่วยให้สามารถเขียนโปรแกรมที่ปลอดภัยและมีประสิทธิภาพมากขึ้น
- ลืมคืนหน่วยความจำ
หากลืมคืนหน่วยความจำที่จัดสรรแบบไดนามิกด้วยfree()
จะเกิดหน่วยความจำรั่วไหล ซึ่งอาจเป็นปัญหาได้โดยเฉพาะในโปรแกรมที่ทำงานเป็นเวลานาน ไม่ว่าโปรแกรมจะซับซ้อนเพียงใดก็ตาม ควรทำให้เป็นนิสัยในการคืนหน่วยความจำที่จัดสรรไว้เสมอ - ละเว้นการตรวจสอบ
NULL
มักจะลืมว่าNULL
จะถูกส่งคืนหากการจัดสรรหน่วยความจำล้มเหลว ควรตรวจสอบค่าNULL
ทันทีหลังจากการจัดสรรหน่วยความจำเสมอ และนำการจัดการข้อผิดพลาดมาใช้ - เข้าถึงหน่วยความจำที่ไม่ได้เริ่มต้น
หน่วยความจำที่จัดสรรด้วยmalloc
อยู่ในสถานะที่ไม่ได้เริ่มต้น หากพยายามใช้งานโดยตรง อาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดได้ โดยเฉพาะอย่างยิ่งหากต้องการการเริ่มต้นค่า ควรพิจารณาใช้calloc
10. สรุป
malloc
เป็นเครื่องมือที่มีประสิทธิภาพในภาษา C และจำเป็นอย่างยิ่งเมื่อต้องจัดสรรหน่วยความจำแบบไดนามิก อย่างไรก็ตาม การใช้พลังของมันอย่างถูกต้องนั้นต้องการความเข้าใจที่ถูกต้องและการจัดการหน่วยความจำที่เหมาะสม มานำความรู้พื้นฐานที่นำเสนอในครั้งนี้ไปใช้จริง ตั้งแต่วิธีการใช้งานพื้นฐานไปจนถึงการประยุกต์ใช้กับโครงสร้างและสตริง เมื่อเขียนโปรแกรมครั้งต่อไป อย่าลืมสั่งหน่วยความจำด้วย malloc
และคืนให้เรียบร้อยเมื่อใช้งานเสร็จ!
คำถามที่พบบ่อย (FAQ)
- หากไม่สามารถจัดสรรหน่วยความจำด้วย
malloc
ได้ ควรทำอย่างไร?
หากการจัดสรรหน่วยความจำล้มเหลวNULL
จะถูกส่งคืน ดังนั้น ควรตรวจสอบค่าNULL
เสมอ และนำการจัดการข้อผิดพลาดที่เหมาะสมมาใช้ - ควรใช้
malloc
หรือcalloc
?
หากไม่ต้องการการเริ่มต้นค่าmalloc
จะเหมาะสมกว่า หากต้องการเริ่มต้นหน่วยความจำด้วยค่าศูนย์calloc
จะเหมาะสมกว่า