C# – מהי תקלת StackOverflow ואיך ניתן להימנע ממנה

StackOverflow Exception
StackOverflow Exception

תקלת StackOverflow היא תקלה שנגרמת כתוצאה מריבוי קריאות לפונקציות בתוכנית שלנו, מה שיגרום לתוכנית שלנו לקרוס בזמן ריצה.
אך כיצד נוכל להימנע מתקלה זו?

לעיתים, כאשר אנו משתמשים ברקורסיה למשל, אנו עלולים להיתקל במצב שבו הקריאה לפונקציה חוזרת על עצמה בלולאה עד אשר התוכנית קורסת.
מדובר בתקלה בזמן ריצה שיש להימנע ממנה משום שלא נצפתה מראש ע"י ה-compiler, ומציפה לנו את ה-Stack.

קחו לדוגמא את קטע הקוד הבא:

 
int myNum = 1;
add1(myNum);

static void add1(int num)
{
    num++;
    add1(num);
}

*שימו לב כי בהחלט לא מומלץ להריץ Code Snippet זה, המחשב שלכם עלול להיתקע.

יצרנו את המשתנה myNum אשר מכיל את הערך 1, ועליו הפעלנו את הפונקציה add1.
פונקציה זו מעלה ב-1 את הערך של הפרמטר אשר מקבלת הפונקציה, וקוראת לעצמה שוב באופן רקורסיבי.
משום שאין לנו שום תנאי יציאה מלולאה זו –
הלולאה חוזרת על עצמה שוב ושוב עד אשר התוכנית קורסת:

StackOverflow Exception
StackOverflow Exception

כלומר שיכולנו להוסיף תנאי שיעצור אותו בהתאם לצרכים שלנו למשל.
נניח והיינו צריכים שהפעולה תחזור על עצמה עד אשר המשתנה יגיע ל-10,
יכולנו להשתמש ב-ref paramter למשל שיעדכן את המשתנה בכל שינוי ויעצור כשיגיע ל-10:

 
int myNum = 1;
add1(ref myNum);
Console.WriteLine(myNum);

static void add1(ref int myNum)
{
    if (myNum == 10)
    {
        return;
    }
    myNum++;
    add1(ref myNum);
}

מחלקת StackOverflowException

החריגה שנזרקת כאשר אנו נתקלים במצב זה נקראת StackOverflowException.
חריגה זו אשר לא ניתן לרשת ממנה יורשת מ-SystemException.
אולם הבעיה היא שלא נוכל "לתפוס" את החריגה באמצעות תחביר trycatch.
החריגה StackOverflowException תמיד תיזרק לפני שתיתפס על ידי ה-trycatch שלנו.
אולם מה שכן נוכל לעשות, זה ליצור חריגה משלנו.
אך עדיין, נצטרך לתת "גג" מוגדר למספר הפעמים שהלולאה יכולה לחזור על עצמה.
כלומר שנגדיר מקסימום למספר הפעמים שהלולאה תחזור על עצמה על מנת שלא תיזרק חריגת StackOverflowException.
ראשית ניצור את החריגה שלנו כך שההודעה שתוצג תכלול את המספר הסופי שהגענו אליו כאשר הגענו לתקרה שהגדרנו:

 
public class MyException : ApplicationException
{
    public override string Message { get; }

    public MyException(int num)
    {
        Message = $"operation stopped at num = {num}";
    }
}

ואת שאר התוכנית נכתוב כך שבכל פעם יתבצע חיבור של 5 למספר הנוכחי:

 
public class Program
{
    static int num = 0;
    static int topOfStack;
    const int stackSize = 1000000;
    const int spaceRequired = 18 * 1024;

    unsafe static void Main(string[] args)
    {
        int var;
        topOfStack = (int)&var;
        add5(ref num);
    }

    unsafe static void add5(ref int num)
    {
        int remaining;
        remaining = stackSize - (topOfStack - (int)&remaining);
        if (remaining > spaceRequired)
        {
            Console.WriteLine(new MyException(num).Message);
            return;
        }
        num += 5;
        add5(ref num);
    }
}

וכעת, כשנריץ את התוכנית נוכל לקבל את הפלט הבא:

StackOverflow Exception
StackOverflow Exception

לסיכום ניתן לומר שעלינו לכתוב את הקוד שלנו בצורה כזו שלא תשאיר פתח למקרה שתיזרק תקלת StackOverflowException .
לקריאה מורחבת על חריגת StackOverflowException באתר של מייקרוסופט יש ללחוץ כאן
לקריאה על Debuging של תקלת StackOverflow בדוטנט באתר של מייקרוסופט יש ללחוץ כאן

רוצים לשתף את המדריך?

אהבתכם את המדריך? פתר לכם תקלה? הזמינו את כותב המדריך לכוס קפה

גולשים יקרים, רוב התכנים המוצגים באתר נכתבים בהתנדבות מלאה מתוך כוונה להנגיש מידע עבורכם. אם נתקלתם במדריך חינמי שפתר לכם תקלה או לימד אתכם משהו חדש שלא ידעתם, וברצונכם לתגמל את כותב המדריך או סתם להזמין אותו לכוס קפה, הינכם יותר ממוזמנים לתרום.

כתיבת תגובה

הזמינו אותי לכוס קפה
buy me coffee

אהבתכם את המדריך? פתר לכם תקלה? הזמינו את כותב המדריך לכוס קפה

גולשים יקרים, רוב התכנים המוצגים באתר נכתבים בהתנדבות מלאה מתוך כוונה להנגיש מידע עבורכם. אם נתקלתם במדריך חינמי שפתר לכם תקלה או לימד אתכם משהו חדש שלא ידעתם, וברצונכם לתגמל את כותב המדריך או סתם להזמין אותו לכוס קפה, הינכם יותר ממוזמנים לתרום.