หลีกเลี่ยงสแตกโอเวอร์โฟลว์
จำกัดขอบเขตการเรียกซ้ำ
หลีกเลี่ยงสแตกโอเวอร์โฟลว์ เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การเรียกซ้ำทำงานอย่างไร
- ปัญหาการเรียกซ้ำคลาสสิก
- การเรียกซ้ำเทียบกับการวนซ้ำ
- หลีกเลี่ยงสแตกโอเวอร์โฟลว์