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

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

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

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

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

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

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

ฟังก์ชันมัลลอค ใช้เพื่อจัดสรรพื้นที่หน่วยความจำบล็อกเดียวในขณะที่ฟังก์ชัน calloc ใน C ใช้เพื่อจัดสรรพื้นที่หน่วยความจำหลายบล็อก แต่ละบล็อกที่จัดสรรโดย calloc ในการเขียนโปรแกรม C มีขนาดเท่ากัน

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

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

การจัดสรรหน่วยความจำแบบไดนามิกด้วย calloc() เหมาะสำหรับสถานการณ์ทั่วไปหลายประการ:

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

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

calloc() ไวยากรณ์:

ptr = (cast_type *) calloc (n, size);
  • ตัวอย่างคำสั่งข้างต้นของ calloc ในภาษา C ใช้ในการจัดสรรบล็อกหน่วยความจำ n ขนาดเท่ากัน
  • หลังจากจัดสรรพื้นที่หน่วยความจำแล้ว ไบต์ทั้งหมดจะถูกเตรียมใช้งานให้เป็นศูนย์
  • การขอ ตัวชี้ ซึ่งปัจจุบันอยู่ที่ไบต์แรกของพื้นที่หน่วยความจำที่จัดสรรจะถูกส่งกลับ

เมื่อใดก็ตามที่มีข้อผิดพลาดในการจัดสรรพื้นที่หน่วยความจำ เช่น หน่วยความจำไม่เพียงพอ ตัวชี้ null จะถูกส่งกลับดังแสดงในตัวอย่าง calloc ด้านล่าง

วิธีการใช้งาน calloc

โปรแกรม calloc ด้านล่างในภาษา C จะคำนวณผลรวมของลำดับเลขคณิต

#include <stdio.h>
    int main() {
        int i, * ptr, sum = 0;
        ptr = calloc(10, sizeof(int));
        if (ptr == NULL) {
            printf("Error! memory not allocated.");
            exit(0);
        }
        printf("Building and calculating the sequence sum of the first 10 terms \ n ");
        for (i = 0; i < 10; ++i) { * (ptr + i) = i;
            sum += * (ptr + i);
        }
        printf("Sum = %d", sum);
        free(ptr);
        return 0;
    }

ผลลัพธ์ของ calloc ในตัวอย่าง C:

 
Building and calculating the sequence sum of the first 10 terms
Sum = 45

ความแตกต่างระหว่าง calloc() และ malloc()

ทั้ง calloc() และ malloc() ต่างก็จองหน่วยความจำบนฮีปและส่งคืนพอยเตอร์แบบไม่มีเงื่อนไข หรือ NULL ในกรณีที่ล้มเหลว แต่ทั้งสองฟังก์ชันมีความแตกต่างกันในสามประเด็นสำคัญที่จะเป็นตัวกำหนดว่าควรใช้ฟังก์ชันใด

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

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

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

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

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

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

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

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

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

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

ฟังก์ชัน `calloc()` ถูกประกาศไว้ในไฟล์เฮดเดอร์ของไลบรารีมาตรฐาน `stdlib.h` ดังนั้นให้เพิ่ม `#include` ไว้ที่ส่วนบนสุดของไฟล์ต้นฉบับ หากไม่มีบรรทัดนี้ คอมไพเลอร์อาจแจ้งเตือนเกี่ยวกับการประกาศโดยปริยายและอ่านค่าตัวชี้ที่ส่งคืนมาผิดพลาด

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

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

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

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

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

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

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

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