0Pricing
C Academy · บทเรียน

รายการหน่วยความจำว่างและการนำกลับมาใช้

ติดตามและนำบล็อกกลับมาใช้ใหม่

รายการหน่วยความจำว่างและการนำกลับมาใช้ เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

ก้าวข้ามตัวจัดสรรแบบ Bump

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

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

ส่วนหัวบล็อกพร้อมลิงก์

เราขยายส่วนหัวด้วยพอยน์เตอร์ next และแฟล็ก free เมื่อรวมกันแล้ว ส่วนเหล่านี้จะเปลี่ยนพูลของเราให้เป็นรายการบล็อกที่ไล่ดูได้

ส่วนข้อมูลจะอยู่ต่อจากส่วนหัวทันทีในหน่วยความจำ

typedef struct block {
    size_t size;          /* payload bytes */
    int free;             /* 1 if reusable */
    struct block *next;   /* next block in pool */
} block_t;

เริ่มต้นด้วยบล็อกว่างขนาดใหญ่หนึ่งบล็อก

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

ส่วนหัวของรายการคือบล็อกเริ่มต้นนี้ ซึ่งครอบคลุมพื้นที่ทั้งหมดของอารีนา

static unsigned char pool[4096];
static block_t *head;

void heap_init(void) {
    head = (block_t *)pool;
    head->size = sizeof(pool) - sizeof(block_t);
    head->free = 1;
    head->next = NULL;
}

การค้นหาแบบ First-Fit

กลยุทธ์การนำกลับมาใช้ใหม่ที่ง่ายที่สุดคือแบบ first-fit โดยไล่ดูรายการแล้วคืนบล็อกว่างบล็อกแรกที่มีขนาดใหญ่พอ วิธีนี้รวดเร็วและมักเก็บบล็อกขนาดเล็กไว้ใกล้ต้นรายการ

ทางเลือกอื่นคือ best-fit ซึ่งเลือกบล็อกที่เล็กที่สุดที่พอใช้ และ worst-fit โดยแลกความเร็วกับลักษณะการเกิดพื้นที่แตกกระจาย

block_t *first_fit(size_t size) {
    for (block_t *b = head; b; b = b->next)
        if (b->free && b->size >= size)
            return b;
    return NULL;
}

การจัดสรรจากบล็อกว่าง

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

พอยน์เตอร์ที่คืนมาคือ block + 1 ซึ่งซ่อนส่วนหัวจากผู้เรียกใช้

void *my_alloc(size_t size) {
    block_t *b = first_fit(size);
    if (!b) return NULL;
    b->free = 0;
    return (void *)(b + 1);
}

การคืนบล็อก

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

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

void my_free(void *p) {
    if (!p) return;
    block_t *b = (block_t *)p - 1;
    b->free = 1;
}

การรวมบล็อกว่างที่อยู่ติดกัน

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

วิธีนี้ช่วยลดพื้นที่แตกกระจายภายนอก ทำให้คำขอขนาดใหญ่ในอนาคตยังได้รับการจัดสรรได้

void coalesce(block_t *b) {
    if (b->next && b->next->free) {
        b->size += sizeof(block_t) + b->next->size;
        b->next = b->next->next;
    }
}

ตัวอย่างรายการพื้นที่ว่างที่เรียกใช้ได้

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

#include <stdio.h>
#include <stddef.h>

typedef struct block { size_t size; int free; struct block *next; } block_t;
static unsigned char pool[1024];
static block_t *head;

void heap_init(void){ head=(block_t*)pool; head->size=sizeof(pool)-sizeof(block_t); head->free=1; head->next=NULL; }
block_t *first_fit(size_t s){ for(block_t *b=head;b;b=b->next) if(b->free&&b->size>=s) return b; return NULL; }
void *my_alloc(size_t s){ block_t *b=first_fit(s); if(!b) return NULL; b->free=0; return (void*)(b+1); }
void my_free(void *p){ if(!p) return; ((block_t*)p-1)->free=1; }

int main(void){
    heap_init();
    int *a = my_alloc(sizeof(int));
    *a = 7;
    printf("a=%d free=%d\n", *a, head->free);
    my_free(a);
    printf("after free: free=%d\n", head->free);
    return 0;
}

ต้นทุนของการค้นหา

รายการพื้นที่ว่างแบบเชื่อมโยงรายการเดียวทำให้การจัดสรรมีความซับซ้อน O(n) ตามจำนวนบล็อก เมื่อมีการจัดสรรจำนวนมาก วิธีนี้จะช้าลง

ตัวจัดสรรจริงใช้รายการพื้นที่ว่างแยกตามกลุ่มขนาด หรือใช้ต้นไม้ เพื่อให้การค้นหาใกล้เคียง O(1) หลักการนำกลับมาใช้ใหม่ยังคงเหมือนเดิม

/* Segregated lists: one bucket per size class */
static block_t *bins[NUM_SIZE_CLASSES];
/* lookup goes straight to the right bucket */

การคืนพื้นที่ซ้ำและข้อมูลเสียหาย

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

นี่คือเหตุผลที่ข้อผิดพลาดเกี่ยวกับหน่วยความจำในภาษา C อันตรายมาก เพราะข้อมูลกำกับของตัวจัดสรรเองอยู่ติดกับข้อมูลของคุณ

ประกอบการนำกลับมาใช้ใหม่

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

การปรับปรุงที่เหลือคือการแยกบล็อกที่ใหญ่เกินไปและการจัดแนว ซึ่งเป็นหัวข้อของบทเรียนสุดท้าย

ตรวจสอบอย่างรวดเร็ว

ลองคิดดูว่าอะไรช่วยป้องกันไม่ให้รายการพื้นที่ว่างเกิดการแตกกระจายมากเกินไป

สรุปทบทวน

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

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

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

บทเรียน “รายการหน่วยความจำว่างและการนำกลับมาใช้” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “รายการหน่วยความจำว่างและการนำกลับมาใช้” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “รายการหน่วยความจำว่างและการนำกลับมาใช้”

ติดตามและนำบล็อกกลับมาใช้ใหม่ คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “รายการหน่วยความจำว่างและการนำกลับมาใช้” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม

ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. malloc ทำงานอย่างไร
  2. ตัวจัดสรรแบบเพิ่มตำแหน่งอย่างง่าย
  3. รายการหน่วยความจำว่างและการนำกลับมาใช้
  4. การจัดแนวและการแบ่ง
← กลับไปที่ C Academy