การจัดสรรหน่วยความจำแบบไดนามิกในภาษา C โดยใช้ฟังก์ชัน malloc(), calloc()

ก่อนที่คุณจะเรียนรู้การจัดสรร C Dynamic Memory มาทำความเข้าใจกันก่อน:

การจัดการหน่วยความจำใน C ทำงานอย่างไร

เมื่อคุณประกาศตัวแปรโดยใช้ชนิดข้อมูลพื้นฐาน คอมไพเลอร์ C จะจัดสรรพื้นที่หน่วยความจำสำหรับตัวแปรในกลุ่มหน่วยความจำที่เรียกว่า กอง.

ตัวอย่างเช่น ตัวแปร float โดยทั่วไปจะใช้เวลา 4 ไบต์ (ตามแพลตฟอร์ม) เมื่อมีการประกาศ เราสามารถตรวจสอบข้อมูลนี้ได้โดยใช้ ขนาดของ ตัวดำเนินการดังแสดงในตัวอย่างด้านล่าง

#include <stdio.h>
int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}

การส่งออกจะได้รับ:

 The size of float is 4 bytes

นอกจากนี้ อาร์เรย์ที่มีขนาดที่ระบุจะถูกจัดสรรในบล็อกหน่วยความจำที่อยู่ติดกัน แต่ละบล็อกมีขนาดสำหรับหนึ่งองค์ประกอบ:

#include <stdio.h>
int main() { float arr[10];
printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}

ผลลัพธ์คือ:

 The size of the float array with 10 element is 40

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

การจัดสรรหน่วยความจำแบบไดนามิกใน C

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

ตอนนี้คุณสามารถสร้างและทำลายอาร์เรย์ขององค์ประกอบแบบไดนามิกขณะรันไทม์ได้โดยไม่มีปัญหาใดๆ โดยสรุป การจัดการหน่วยความจำอัตโนมัติใช้สแต็ก และการจัดสรรหน่วยความจำแบบไดนามิก C ใช้ฮีป

ที่ ไลบรารีมีฟังก์ชันที่รับผิดชอบในการจัดการหน่วยความจำแบบไดนามิก

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

เรามาหารือเกี่ยวกับฟังก์ชันข้างต้นกับแอปพลิเคชันกันดีกว่า

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

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

ไวยากรณ์ของฟังก์ชัน malloc():

ptr = (cast_type *) malloc (byte_size);

ที่นี่

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

ตัวอย่างของ malloc():

Example: ptr = (int *) malloc (50)

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

ลองพิจารณาอีกตัวอย่างหนึ่ง:

#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

ฟังก์ชัน C malloc()

  1. สังเกตว่า ขนาดของ(*ptr) ถูกใช้แทน ขนาดของ(int) เพื่อให้โค้ดมีความแข็งแกร่งมากขึ้นเมื่อคำประกาศ *ptr ถูกแปลงชนิดข้อมูลเป็นชนิดข้อมูลอื่นในภายหลัง
  2. การจัดสรรอาจล้มเหลวหากหน่วยความจำไม่เพียงพอ ในกรณีนี้จะส่งกลับตัวชี้ NULL ดังนั้นคุณควรรวมโค้ดเพื่อตรวจสอบตัวชี้ NULL
  3. โปรดจำไว้ว่าหน่วยความจำที่จัดสรรนั้นต่อเนื่องกันและสามารถถือเป็นอาร์เรย์ได้ เราสามารถใช้เลขคณิตตัวชี้เพื่อเข้าถึงองค์ประกอบของอาร์เรย์แทนที่จะใช้เครื่องหมายวงเล็บ [ ] เราแนะนำให้ใช้ + เพื่ออ้างถึงองค์ประกอบของอาร์เรย์ เนื่องจากการใช้การเพิ่มค่า ++ หรือ += จะเปลี่ยนที่อยู่ที่เก็บโดย ตัวชี้.

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

ฟังก์ชั่น free() ใน C

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

ฟรี() ฟังก์ชันนี้ถูกเรียกใช้เพื่อปลดปล่อย/จัดสรรหน่วยความจำใน C โดยการปล่อยหน่วยความจำในโปรแกรมของคุณ คุณจะมีหน่วยความจำเพิ่มเติมพร้อมให้ใช้งานในภายหลัง

ตัวอย่างเช่น:

#include <stdio.h>
int main() {
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL){
  *(ptr + 2) = 50;
  printf("Value of the 2nd integer is %d",*(ptr + 2));
}
free(ptr);
}

Output:

 Value of the 2nd integer is 50

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

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

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

ไวยากรณ์ของฟังก์ชัน calloc()

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

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

ตัวอย่างของ calloc():

โปรแกรมด้านล่างนี้คำนวณผลรวมของลำดับเลขคณิต

#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;
    }

ผลลัพธ์:

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

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

ต่อไปนี้เป็นความแตกต่างที่สำคัญระหว่าง malloc() กับ calloc() ใน C:

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

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

ฟังก์ชัน realloc() ใน C

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

realloc() ยังสามารถใช้เพื่อลดขนาดของหน่วยความจำที่จัดสรรไว้ก่อนหน้านี้ได้

ไวยากรณ์ของฟังก์ชัน realloc():

ptr = realloc (ptr,newsize);

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

ตัวอย่างของการจัดสรรใหม่ ():

#include <stdio.h>
int main () {
   char *ptr;
   ptr = (char *) malloc(10);
   strcpy(ptr, "Programming");
   printf(" %s,  Address = %u\n", ptr, ptr);

   ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size
   strcat(ptr, " In 'C'");
   printf(" %s,  Address = %u\n", ptr, ptr);
   free(ptr);
   return 0;
}

เมื่อใดก็ตามที่ realloc() ใน C ส่งผลให้การดำเนินการไม่สำเร็จ ก็จะส่งคืนตัวชี้ค่าว่าง และข้อมูลก่อนหน้านี้ก็จะได้รับการปลดปล่อยด้วยเช่นกัน

อาร์เรย์แบบไดนามิกใน C

อาร์เรย์แบบไดนามิกในภาษา C ช่วยให้จำนวนองค์ประกอบเพิ่มขึ้นตามต้องการ อาร์เรย์แบบไดนามิกใน C ใช้กันอย่างแพร่หลายในอัลกอริทึมของวิทยาการคอมพิวเตอร์

ในโปรแกรมต่อไปนี้ เราได้สร้างและปรับขนาดอาร์เรย์ไดนามิกใน C

#include <stdio.h>
    int main() {
        int * arr_dynamic = NULL;
        int elements = 2, i;
        arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks
        for (i = 0; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        elements = 4;
        arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements
        printf("After realloc\n");
        for (i = 2; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        free(arr_dynamic);
    }

ผลลัพธ์ของโปรแกรม C Dynamic Array ที่หน้าจอ:

 
arr_dynamic[0]=0
arr_dynamic[1]=1
After realloc
arr_dynamic[0]=0
arr_dynamic[1]=1
arr_dynamic[2]=2
arr_dynamic[3]=3

สรุป

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