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

หลีกเลี่ยงสแตกโอเวอร์โฟลว์

จำกัดขอบเขตการเรียกซ้ำ

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

สแตกล้นคืออะไร

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

หากการเรียกซ้ำลึกเกินไป สแตกจะเต็มและโปรแกรมหยุดทำงานด้วย สแตกล้น

ไม่มีกรณีฐาน

สาเหตุที่พบบ่อยที่สุดคือกรณีฐานที่ไม่มีวันไปถึง ทำให้การเรียกทำซ้ำไม่สิ้นสุดและสแตกล้น

อย่าเรียกใช้ฟังก์ชันลักษณะนี้ ให้ศึกษาเหตุผลที่ทำให้ฟังก์ชันล้มเหลว

int broken(int n) {
    /* no base case: never stops */
    return broken(n + 1);
}

อาร์กิวเมนต์ไม่ลดลง

แม้จะมีกรณีฐาน แต่อาร์กิวเมนต์ก็ต้องเคลื่อนเข้าใกล้กรณีนั้น ในที่นี้ n เพิ่มขึ้น จึงไม่มีวันไปถึง 0

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

int oops(int n) {
    if (n == 0) return 0;
    return oops(n + 1); /* wrong direction */
}

เวอร์ชันที่ถูกต้อง

การแก้ทิศทางทำให้ฟังก์ชันสิ้นสุดได้ ตอนนี้ n ลดลงเข้าหากรณีฐาน 0

#include <stdio.h>

int good(int n) {
    if (n == 0) return 0;
    return n + good(n - 1);
}

int main(void) {
    printf("%d\n", good(10));
    return 0;
}

ขีดจำกัดความลึกมีอยู่จริง

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

สำหรับความลึกมหาศาล ควรเลือกใช้การวนซ้ำ

เปลี่ยนการเรียกซ้ำลึกเป็นลูป

หากความลึกของการเรียกซ้ำเพิ่มขึ้นตามขนาดข้อมูลนำเข้า ให้เปลี่ยนไปใช้ลูป วิธีนี้หลีกเลี่ยงการซ้อนเฟรมหลายพันเฟรม

ลูปด้านล่างหาผลรวมตั้งแต่ 1 ถึง n ขนาดใหญ่ได้อย่างปลอดภัยโดยใช้หน่วยความจำคงที่

#include <stdio.h>

int main(void) {
    long total = 0;
    for (int i = 1; i <= 1000000; i++)
        total += i;
    printf("%ld\n", total);
    return 0;
}

ลดความลึกด้วยการแบ่งแยกแล้วพิชิต

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

long range_sum(int lo, int hi) {
    if (lo == hi) return lo;
    int mid = (lo + hi) / 2;
    return range_sum(lo, mid) + range_sum(mid + 1, hi);
}

ระวังอาร์เรย์เฉพาะที่ขนาดใหญ่

ตัวแปรเฉพาะที่ขนาดใหญ่ทำให้แต่ละเฟรมมีน้ำหนักมาก ดังนั้นสแตกจึงเต็มเร็วขึ้น

หลีกเลี่ยงการประกาศอาร์เรย์ขนาดใหญ่ภายในฟังก์ชันเรียกซ้ำ ให้ส่งพอยน์เตอร์หรือใช้ฮีปแทน

void heavy(int n) {
    int buffer[10000]; /* big frame each call */
    if (n == 0) return;
    heavy(n - 1);
}

ใช้ตัวสะสม

การส่งผลรวมที่กำลังคำนวณอยู่เป็นตัวสะสมช่วยให้แต่ละเฟรมมีขนาดเล็ก และทำให้การเรียกซ้ำมีลักษณะเป็นการเรียกท้ายฟังก์ชัน

จากนั้นคอมไพเลอร์บางตัวอาจนำเฟรมเดียวกลับมาใช้ซ้ำได้

#include <stdio.h>

long sum_acc(int n, long acc) {
    if (n == 0) return acc;
    return sum_acc(n - 1, acc + n);
}

int main(void) {
    printf("%ld\n", sum_acc(100, 0));
    return 0;
}

รายการตรวจสอบเพื่อความปลอดภัย

ก่อนวางใจใช้ฟังก์ชันเรียกซ้ำ ให้ตรวจสอบว่า:

1. มีกรณีฐานหรือไม่?
2. การเรียกทุกครั้งเคลื่อนเข้าใกล้กรณีฐานหรือไม่?
3. ความลึกอาจมากจนเกินไปเมื่อข้อมูลนำเข้ามีขนาดใหญ่หรือไม่?

หากความลึกอาจเพิ่มขึ้นอย่างรวดเร็ว ให้ใช้ลูปแทน

ทดสอบด้วยข้อมูลนำเข้าขนาดเล็ก

ทดสอบการเรียกซ้ำด้วยข้อมูลนำเข้าขนาดเล็กมากก่อนเสมอ โดยเลือกกรณีที่ตรวจสอบด้วยมือได้

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

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

ค้นหาวิธีแก้ที่ปลอดภัยที่สุด

สรุปทบทวน

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

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

บทเรียน “หลีกเลี่ยงสแตกโอเวอร์โฟลว์” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “หลีกเลี่ยงสแตกโอเวอร์โฟลว์”

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

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

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

บทเรียน “หลีกเลี่ยงสแตกโอเวอร์โฟลว์” ใช้เวลานานแค่ไหน

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

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

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

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

  1. การเรียกซ้ำทำงานอย่างไร
  2. ปัญหาการเรียกซ้ำคลาสสิก
  3. การเรียกซ้ำเทียบกับการวนซ้ำ
  4. หลีกเลี่ยงสแตกโอเวอร์โฟลว์
← กลับไปที่ C Academy