Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Studies

מחסנית הקריאות

קריאות LIFO

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

#include <stdio.h>
void f2(int x) {
    printf("f2 started: arg = %d\n", x);
    printf("f2 finished: arg = %d\n", x);
}
void f1(char c) {
    printf("f1 started: arg = %c\n", c);
    f2(42);
    printf("f1 finished: arg = %c\n", c);
}
int main() {
    printf("main started\n");
    f1('a');
    printf("main finished\n");
}

ננתח את סדר הקריאות בתוכנית:

  1. לאחר הקריאה לפונקציית ה-main, אנחנו קופצים ל-f1, כאשר main עוד לא סיימה.
  2. f1 קוראת מיד ל-f2, כאשר main ו-f1 עוד לא הסתיימו.
  3. f2 מתחילה ביצוע, קוראת ל-printf ואז מסיימת בעצמה - main ו-f1 עוד לא הסתיימו.
  4. לאחר החזרה מ-f2, הפונקציה f1 קוראת ל-printf ואז מסיימת בעצמה - main עדיין לא הסתיימה.
  5. לאחר החזרה מ-f1 פונקציית ה-main קוראת ל-printf ואז מסתיימת בעצמה.

כלומר, סדר הקריאה הוא main->f1->f2, וסדר הסיום הוא f2->f1->main - בדיוק הפוך, או LIFO!

מחסנית הקריאות

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

כיצד זה עובד?

  1. כאשר פונקציה נקראת, התוכנית דוחפת (push) על מחסנית הקריאות שבזכרון אובייקט עם כל המידע הרלוונטי לביצוע הפונקציה - ארגומנטים, משתנים לוקאליים וכו׳.

  2. אובייקט זה נקרא stack frame.

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

  4. בסיום הפונקציה הנקראת, התוכנית מוציאה (pop) ממחסנית הקריאות את ה-stack frame האחרון, בו השתמשה בפונקציה הנקראת. פעולה זו נהוג לקרוא קיפול (fold) של המחסנית. במידה והיה ערך חזרה מהפונקציה, גם הוא נקרא מתוך מחסנית הקריאות לפני שהיא מקופלת.

  5. הפונקציה הקוראת ממשיכה לבצע מאחרי הנקודה בה התבצעה הקריאה.

Note

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

Note

בהמשך נפגוש איזור נוסף בזכרון שאינו על המחסנית בשם הערימה (heap).

דוגמא

נצייר את מחסנית הקריאות של התוכנית הבאה, המחשבת ומדפיסה שטח של אליפסה:

#include <stdio.h>
#define PI 3.14
double calculate_ellipse_area(double r1, double r2) {
    return PI * r1 * r2;
}
void show_ellipse_area(double r1, double r2) {
    printf("area: %.2lf", calculate_ellipse_area(r1, r2));
}
int main() {
    double r1, r2;
    scanf("%lf%lf", &r1, &r2);
    show_area(r);
}

בתחילת התוכנית התוכנית מחסנית הקריאות נראת כך:

כלומר, המחסנית מכילה רק את המידע הנדרש עבור main. לאחר הקריאה ל-show_ellipse_area נקבל את התמונה הבאה:

דחפנו על המחסנית עותקים של המשתנים r1 ו-r2. נדגיש - המשתנים r1, r2 בפונקציה main אינם באותו מקום בזכרון כמו r1, r2 של show_ellipse_area, או של calculate_ellipse_area. על אף שיש להם את אותו שם ואתו ערך, הם לא אותו משתנה וכתיבה לאחד מהם לא תשנה את האחר.

בצורה דומה לחלוטין דחפנו עותקים נוספים על המשתנים.

Note

לא ציירנו על המחסנית את הקריאה ל-printf אבל היא גם נמצאת על מחסנית הקריאות.