ฟังก์ชัน malloc() ในไลบรารี C พร้อมตัวอย่าง

⚡ สรุปอย่างชาญฉลาด

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

  • 🧠 การจัดสรรเวลาทำงาน: malloc() จะจองหน่วยความจำระหว่างการทำงานเมื่อไม่ทราบขนาดที่ต้องการในระหว่างการคอมไพล์
  • 📦 ไวยากรณ์: พอยเตอร์ประเภท cast_type จะได้รับ malloc(byte_size) ซึ่งโดยทั่วไปจะกำหนดขนาดด้วย sizeof เพื่อความเข้ากันได้กับหลายแพลตฟอร์ม
  • ⚠️ ตรวจสอบค่าว่าง: คำขอที่ล้มเหลวจะส่งคืนพอยเตอร์ NULL ดังนั้นโปรดตรวจสอบความถูกต้องของผลลัพธ์ก่อนที่จะเข้าถึงค่าที่พอยเตอร์ชี้อยู่
  • การคำนวณเลขชี้: บล็อกนี้ต่อเนื่องกัน ดังนั้นให้ใช้ + แทน ++ เพื่อเข้าถึงองค์ประกอบของอาร์เรย์
  • 🧹 เพิ่มพื้นที่ว่างในหน่วยความจำ: ปล่อยหน่วยความจำทุกบล็อกด้วยเมธอด free() เพื่อป้องกันการรั่วไหลของหน่วยความจำและพอยเตอร์ที่ชี้ไปยังตำแหน่งที่ไม่ถูกต้อง
  • 🤖 ความช่วยเหลือจากเอไอ: ผู้ช่วย AI และ GitHub Copilot ร่างการเรียกใช้ malloc() และทำเครื่องหมายว่าไม่มีคำสั่ง free()

ฟังก์ชัน malloc() ในภาษา C

malloc ใน C คืออะไร?

ฟังก์ชัน malloc() ย่อมาจากการจัดสรรหน่วยความจำ เป็นฟังก์ชันที่ใช้ในการจัดสรรบล็อกหน่วยความจำแบบไดนามิก จะสงวนพื้นที่หน่วยความจำตามขนาดที่ระบุและส่งกลับตัวชี้ null ที่ชี้ไปยังตำแหน่งหน่วยความจำ ตัวชี้ที่ส่งคืนมักจะเป็นประเภทโมฆะ หมายความว่าเราสามารถกำหนดฟังก์ชัน malloc ให้กับพอยน์เตอร์ใดก็ได้

malloc ใน C คืออะไร

วากยสัมพันธ์

ptr = (cast_type *) malloc (byte_size);

ที่นี่

  • ptr เป็นตัวชี้ของ cast_type
  • ฟังก์ชัน malloc ส่งคืนตัวชี้ไปยังหน่วยความจำที่จัดสรรเป็น byte_size
Example: ptr = (int *) malloc (50)

เมื่อดำเนินการคำสั่งนี้สำเร็จ พื้นที่หน่วยความจำ 50 ไบต์จะถูกสงวนไว้ ที่อยู่ของไบต์แรกของพื้นที่สงวนถูกกำหนดให้กับตัวชี้ ptr ประเภท int

พิจารณาอีกตัวอย่างหนึ่งของการนำ malloc ไปใช้งาน:

#include <stdlib.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */
    if (ptr != NULL) {
      *(ptr + 5) = 480; /* assign 480 to sixth integer */
      printf("Value of the 6th integer is %d",*(ptr + 5));
    }
}

Output:

Value of the 6th integer is 480

โปรดสังเกตว่ามีการใช้ sizeof(*ptr) แทน sizeof(int) เพื่อให้โค้ดมีความเสถียรมากขึ้นเมื่อมีการแปลงชนิดของการประกาศ *ptr ไปเป็นชนิดข้อมูลอื่นในภายหลัง

การจัดสรรอาจล้มเหลวหากหน่วยความจำไม่เพียงพอ ในกรณีนี้จะส่งกลับตัวชี้ NULL ดังนั้นคุณควรรวมโค้ดเพื่อตรวจสอบตัวชี้ NULL

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

ฟังก์ชัน Malloc ยังสามารถใช้ได้กับชนิดข้อมูลอักขระและชนิดข้อมูลที่ซับซ้อน เช่น โครงสร้างอีกด้วย

เหตุใดจึงต้องใช้ malloc() ในภาษา C?

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

การจัดสรรหน่วยความจำแบบไดนามิกด้วย malloc() เป็นตัวเลือกที่เหมาะสมในหลายสถานการณ์ทั่วไป:

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

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

วิธีการคืนหน่วยความจำที่จัดสรรโดย malloc()

หน่วยความจำที่จองโดยฟังก์ชัน malloc() จะอยู่บนฮีปและจะไม่ถูกปล่อยโดยอัตโนมัติ เมื่อโปรแกรมทำการจัดสรรหน่วยความจำโดยไม่ปล่อยหน่วยความจำ มันจะค่อยๆ ใช้หน่วยความจำที่มีอยู่ทั้งหมด ซึ่งเป็นข้อบกพร่องที่เรียกว่าหน่วยความจำรั่วไหล ฟังก์ชัน free() จะส่งคืนบล็อกที่เสร็จสมบูรณ์แล้วให้กับระบบ เพื่อให้สามารถนำพื้นที่นั้นกลับมาใช้ใหม่ได้

ฟังก์ชัน free() รับอาร์กิวเมนต์เพียงตัวเดียว คือพอยเตอร์ที่ฟังก์ชัน malloc() ส่งคืน และปล่อยบล็อกทั้งหมดในคราวเดียว:

int *ptr;
ptr = malloc(10 * sizeof(int));
if (ptr != NULL) {
    /* use the allocated memory here */
    free(ptr);   /* release the block */
    ptr = NULL;  /* avoid a dangling pointer */
}

ปฏิบัติตามกฎเหล่านี้เพื่อจัดการหน่วยความจำแบบไดนามิกอย่างปลอดภัย:

  • เรียกฟังก์ชัน free() เพียงครั้งเดียวสำหรับการจัดสรรหน่วยความจำ (malloc()) ที่สำเร็จแต่ละครั้ง การปลดปล่อยบล็อกเดียวกันสองครั้งจะทำให้เกิดพฤติกรรมที่ไม่แน่นอน
  • หลังจากปล่อยหน่วยความจำแล้ว ให้ตั้งค่าตัวชี้เป็น NULL เพื่อป้องกันไม่ให้ถูกนำกลับมาใช้ใหม่เป็นตัวชี้ที่ชี้ไปยังตำแหน่งที่ไม่ถูกต้อง (dangling pointer)
  • ห้ามใช้ free() กับพอยเตอร์ที่ malloc() ไม่ได้ส่งคืนมา รวมถึงพอยเตอร์ที่ถูกเลื่อนด้วยการคำนวณเลขคณิตของพอยเตอร์ด้วย
  • จัดสรรหน่วยความจำทุกครั้งให้ตรงกับการปล่อยหน่วยความจำที่เหมาะสม ก่อนที่ตัวชี้จะอยู่นอกขอบเขตการใช้งาน

Keeping การจัดสรรและการปล่อยหน่วยความจำอย่างสมดุลเป็นหัวใจสำคัญของการจัดการหน่วยความจำด้วยตนเองในภาษาซี และช่วยป้องกันทั้งการรั่วไหลและการหยุดทำงานที่เกิดจากการนำหน่วยความจำที่ถูกปล่อยไปแล้วกลับมาใช้ใหม่

malloc() กับ calloc(): ความแตกต่างที่สำคัญ

malloc() และ การโทร () ทั้งสองฟังก์ชันใช้สำหรับจองหน่วยความจำฮีป แต่แตกต่างกันที่อาร์กิวเมนต์และวิธีการจัดการบล็อกใหม่ ฟังก์ชัน malloc() รับอาร์กิวเมนต์หนึ่งตัว คือจำนวนไบต์ทั้งหมด และปล่อยให้เนื้อหาไม่มีค่าเริ่มต้น ดังนั้นบล็อกจึงเก็บค่าขยะที่ไม่สามารถคาดเดาได้จนกว่าโปรแกรมจะเขียนลงไป ในขณะที่ฟังก์ชัน calloc() รับอาร์กิวเมนต์สองตัว คือจำนวนองค์ประกอบและขนาดของแต่ละองค์ประกอบ และจะล้างทุกไบต์ให้เป็นศูนย์ก่อนที่จะส่งคืนตัวชี้

ความแตกต่างในทางปฏิบัติสรุปได้เป็นสามประเด็นหลัก:

  • การเริ่มต้นใช้งาน: calloc() จะกำหนดค่าหน่วยความจำให้เป็นศูนย์ ในขณะที่ malloc() จะไม่ทำเช่นนั้น ทำให้ malloc() เร็วกว่าเล็กน้อยเมื่อค่าต่างๆ จะถูกเขียนทับอยู่แล้ว
  • เหตุผล: คุณเขียน malloc(n * size) แต่เขียน calloc(n, size) เพื่อจองอาร์เรย์เดียวกัน
  • กรณีการใช้งาน: เลือกใช้ calloc() เมื่อต้องการบล็อกหน่วยความจำที่สะอาดและมีค่าเป็นศูนย์ และเลือกใช้ malloc() เมื่อต้องการความเร็วและจัดการการเริ่มต้นใช้งานด้วยตนเอง

ฟังก์ชันทั้งสองถูกประกาศไว้ใน stdlib.h ทั้งคู่ส่งคืนพอยเตอร์แบบ void หรือ NULL เมื่อเกิดข้อผิดพลาด และทั้งคู่คืนหน่วยความจำด้วยฟังก์ชัน free() เดียวกัน

คำถามที่พบบ่อย

ในภาษา C การแปลงประเภทข้อมูลนั้นเป็นทางเลือกและมักไม่แนะนำ เนื่องจาก void* จะแปลงเป็นตัวชี้ไปยังวัตถุใดๆ โดยอัตโนมัติ และการแปลงประเภทข้อมูลอย่างชัดเจนอาจบดบังการขาดการรวมไฟล์ stdlib.h ได้ C++อย่างไรก็ตาม จำเป็นต้องมีการหล่อแบบ

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

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

หน่วยความจำรั่วไหลเกิดขึ้นเมื่อหน่วยความจำฮีปที่ได้จาก malloc() ไม่ได้รับการปล่อยคืนด้วย free() โปรแกรมจะยังคงจองพื้นที่ที่ไม่ได้ใช้งานแล้ว ซึ่งอาจทำให้หน่วยความจำที่มีอยู่หมดลงในที่สุด

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

malloc() เป็นฟังก์ชันจากไลบรารีที่ส่งคืนค่า void* และต้องการค่า sizeof เพื่อคำนวณจำนวนไบต์ C++ `new` เป็นตัวดำเนินการที่ส่งคืนพอยเตอร์แบบระบุชนิดและเรียกใช้คอนสตรัคเตอร์ ควรใช้ `malloc()` ร่วมกับ `free()` และใช้ `new` ร่วมกับ `delete()`

ใช่แล้ว ผู้ช่วยเขียนโค้ด AI จะร่างคำสั่ง malloc() ตรวจสอบค่า NULL และแจ้งเตือนหากขาดคำสั่ง free() หรือมีการรั่วไหลของหน่วยความจำ และยังสามารถอธิบายการคำนวณทางคณิตศาสตร์ของตัวชี้ได้อีกด้วย อย่างไรก็ตาม ควรคอมไพล์และทดสอบโค้ดที่สร้างขึ้นก่อนที่จะเชื่อถือเสมอ

นักบิน GitHub แนะนำบรรทัด malloc(), sizeof() และ free() ที่ตรงกันจากความคิดเห็นที่อธิบายถึงความตั้งใจของคุณ Revตรวจสอบคำแนะนำแต่ละข้อสำหรับข้อผิดพลาดในการตรวจสอบค่า NULL และการรั่วไหลของหน่วยความจำก่อนทำการคอมไพล์

สรุปโพสต์นี้ด้วย: