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

การตรวจจับหน่วยความจำรั่ว

พื้นฐาน memcheck

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

หน่วยความจำรั่วคืออะไร

หน่วยความจำรั่ว เกิดขึ้นเมื่อคุณจัดสรรหน่วยความจำบน HEAP ด้วย malloc, calloc หรือ realloc แล้วสูญเสียพอยน์เตอร์ทั้งหมดที่ชี้ไปยังหน่วยความจำนั้นก่อนเรียก free

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

หน่วยความจำรั่วแบบพื้นฐาน

โปรแกรมนี้ทำให้หน่วยความจำรั่วหนึ่งบล็อก ขนาด 40 ไบต์

พอยน์เตอร์ p เป็นตัวแปรภายใน เมื่อ main คืนค่า p จะหายไป แต่บล็อกบน HEAP ที่เคยชี้อยู่จะไม่ถูกคืนหน่วยความจำ

#include <stdlib.h>

int main(void) {
    int *p = malloc(10 * sizeof(int));
    p[0] = 1;
    return 0; /* never free(p) */
}

การเรียกใช้ memcheck

memcheck เป็นเครื่องมือเริ่มต้น ดังนั้นคุณจึงไม่จำเป็นต้องระบุชื่อเครื่องมือ

เพิ่ม --leak-check=full เพื่อรับเส้นทางการเรียกฟังก์ชันสำหรับการจัดสรรแต่ละรายการที่รั่ว

gcc -g -O0 -o leak leak.c
valgrind --leak-check=full ./leak

สรุป LEAK

เมื่อสิ้นสุดการทำงาน Valgrind จะแสดง LEAK SUMMARY สำหรับโปรแกรมข้างต้น ผลลัพธ์จะมีลักษณะประมาณนี้:

definitely lost: 40 bytes in 1 blocks

เส้นทางการเรียกจะชี้ไปยังตำแหน่งที่เรียก malloc ได้อย่างแม่นยำ ทำให้คุณทราบว่าการจัดสรรใดไม่ได้คืนหน่วยความจำ

หมวดหมู่ของหน่วยความจำรั่วทั้งสี่

memcheck จัดประเภทหน่วยความจำที่สูญหายออกเป็นสี่กลุ่ม:

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

สูญหายแน่นอนเทียบกับยังเข้าถึงได้

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

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

หน่วยความจำรั่วทางอ้อม

เมื่อคุณสูญเสียส่วนหัวของโครงสร้างแบบเชื่อมโยง โหนดทุกตัวจะไม่สามารถเข้าถึงได้

ส่วนหัวจะแสดงเป็น สูญหายแน่นอน และส่วนที่เหลือจะแสดงเป็น สูญหายทางอ้อม การแก้สาเหตุหลัก ซึ่งก็คือส่วนหัวที่สูญหาย จะกู้คืนหน่วยความจำทั้งหมดได้

struct Node { int v; struct Node *next; };

struct Node *make(void) {
    struct Node *h = malloc(sizeof *h);
    h->next = malloc(sizeof *h);
    h->next->next = NULL;
    return h;
}
/* if the caller drops the returned head, both nodes leak */

เวอร์ชันที่แก้ไขแล้ว

คืนหน่วยความจำทุกส่วนที่จัดสรร เวอร์ชันนี้ไม่มีหน่วยความจำรั่ว

เมื่อทำงานภายใต้ Valgrind จะแสดงรายงานว่า All heap blocks were freed -- no leaks are possible

#include <stdlib.h>

int main(void) {
    int *p = malloc(10 * sizeof(int));
    p[0] = 1;
    free(p);
    return 0;
}

การนับการจัดสรร

แม้จะไม่มีหน่วยความจำรั่ว HEAP SUMMARY ก็ยังบอกได้ว่าโปรแกรมของคุณทำงานกับหน่วยความจำมากเพียงใด:

total heap usage: 1 allocs, 1 frees, 40 bytes allocated

เมื่อจำนวนการจัดสรรและการคืนหน่วยความจำเท่ากัน แสดงว่าการทำงานสะอาด ไม่มีความผิดปกติ หากจำนวนไม่ตรงกัน นั่นคือเบาะแสแรกว่ามีบางสิ่งหลุดรอดไป

การแสดงบล็อกที่ยังเข้าถึงได้

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

--show-leak-kinds=all

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

valgrind --leak-check=full --show-leak-kinds=all ./leak

หน่วยความจำรั่วบนเส้นทางข้อผิดพลาด

หน่วยความจำรั่วที่ตรวจจับได้ยากที่สุดมักซ่อนอยู่บนเส้นทางที่คืนค่าก่อนกำหนด ในกรณีนี้ หากการจัดสรรครั้งที่สองล้มเหลว บล็อกแรกจะรั่ว

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

char *a = malloc(100);
char *b = malloc(100);
if (!b) {
    /* bug: a is leaked here */
    return -1;
}
/* fix: free(a); before returning */

ตรวจสอบความเข้าใจ

เลือกหมวดหมู่ของหน่วยความจำรั่วที่ต้องได้รับการแก้ไขอย่างเร่งด่วนที่สุด

สรุปทบทวน

ตอนนี้คุณสามารถตรวจจับหน่วยความจำรั่วด้วย memcheck ได้แล้ว:

  • เรียกใช้ valgrind --leak-check=full ./prog กับไบนารีที่คอมไพล์ด้วย -g
  • อ่าน LEAK SUMMARY และ HEAP SUMMARY
  • ให้ความสำคัญกับ สูญหายแน่นอน ก่อน แล้วติดตามหน่วยความจำรั่วทางอ้อมกลับไปยังสาเหตุหลัก
  • ใช้ --show-leak-kinds=all เพื่อตรวจสอบบล็อกที่ยังเข้าถึงได้

ถัดไป: การเข้าถึงหน่วยความจำที่ไม่ถูกต้อง

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

บทเรียน “การตรวจจับหน่วยความจำรั่ว” ฟรีหรือไม่

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

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

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

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

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

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

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

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

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

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

  1. เหตุใดจึงใช้ Valgrind
  2. การตรวจจับหน่วยความจำรั่ว
  3. การเข้าถึงที่ไม่ถูกต้อง
  4. การอ่านรายงาน
← กลับไปที่ C Academy