Menu
Coddy logo textTech

מה זה Segmentation Fault?

Segmentation fault, או בקיצור segfault, היא קריסה שקורית כשתוכנית מנסה לקרוא או לכתוב זיכרון שאסור לה לגשת אליו, למשל כתובת 0 דרך מצביע null. מערכת ההפעלה עוצרת את התוכנית עם האות SIGSEGV.

מאת Kevin Spektor, מייסד שותף ו-CTO

עודכן ב-24 בספטמבר 2026

תוכנית C מדפיסה את השורה הראשונה שלה ואז נעצרת עם הודעה שאף אחד לא כתב:

#include <stdio.h>

int main(void) {
    int *score = NULL;

    printf("About to read the score\n");
    printf("Score: %d\n", *score);
    return 0;
}
About to read the score
bash: line 1: 64030 Segmentation fault: 11  ./seg

הפלט הזה הוא מ-bash ב-macOS, כאשר 64030 הוא מזהה התהליך ו-11 הוא מספר האות. ב-Linux אותה קריסה נראית בדרך כלל כך: Segmentation fault (core dumped). בכל מקרה, התוכנית אף פעם לא הדפיסה את הציון. score מחזיק את הכתובת 0, ו-*score מבקש מהמעבד לקרוא את הזיכרון בכתובת הזו, ואף תוכנית לא רשאית לעשות את זה.

איך נוצר segmentation fault

כל תוכנית רצה במרחב כתובות וירטואלי משלה, טווח עצום של כתובות שמערכת ההפעלה ממלאת חלק אחר חלק. היא ממפה לטווח הזה את הקוד של התוכנית, את המשתנים הגלובליים שלה, את המחסנית (stack) ואת הערימה (heap), בבלוקים שנקראים דפים (4 KB ברוב מערכות Linux על x86, ו-16 KB במחשבי Mac עם Apple silicon). רוב הכתובות נשארות לא ממופות, וכתובת 0 תמיד ביניהן, כדי שבאגים של מצביע null ייתפסו.

  1. התוכנית מבצעת פקודה שקוראת או כותבת לכתובת. כאן זו הקריאה של *score, כתובת 0.
  2. יחידת ניהול הזיכרון של המעבד מחפשת את הכתובת בטבלת הדפים. הדף לא ממופה, או שהתוכנית מנסה לכתוב לדף שמיועד לקריאה בלבד.
  3. המעבד עוצר את הפקודה ומעביר את השליטה לקרנל עם page fault.
  4. הקרנל בודק אם הגישה יכולה להיות חוקית, למשל מחסנית שצריכה לגדול. היא לא, ולכן הקרנל שולח לתהליך את אות 11, SIGSEGV.
  5. פעולת ברירת המחדל של SIGSEGV היא לסיים את התהליך, וכשהמערכת מאפשרת, לשמור core dump. אז ה-shell מדפיס את ההודעה וקובע את קוד היציאה ל-139, שהוא 128 ועוד מספר האות.

כלומר, segmentation fault לא מדווח על ידי הקומפיילר, והוא גם לא חריגה שהשפה מעלה. זו החומרה והקרנל שמגנים על הזיכרון, וזה הופך אותו לשגיאת זמן ריצה מהסוג הפתאומי ביותר. Windows מטפל באותו אירוע כ-access violation, עם קוד החריגה 0xC0000005.

סיבות נפוצות ל-segmentation fault

C ו-C++ מאפשרות לתוכנית לחשב כל כתובת ולהשתמש בה, ולכן כל הסיבות מסתכמות בשימוש בכתובת לא תקינה.

  • גישה דרך מצביע null. int *p = NULL; *p = 5; פונקציה שמחזירה NULL כשהיא נכשלת, כמו malloc או fopen, מובילה לכאן כשלא בודקים את התוצאה שלה.
  • אינדקס רחוק מעבר לסוף של מערך. C לא בודקת גבולות, ולכן arr[1000000] הוא פשוט כתובת שנמצאת מיליון איברים הלאה.
  • שימוש בזיכרון אחרי free. המצביע עדיין מחזיק את הכתובת הישנה, אבל הזיכרון כבר לא שייך לכם.
  • מצביע לא מאותחל. int *p; *p = 5; כותב דרך ערך הזבל ש-p מחזיק במקרה.
  • גלישת מחסנית (stack overflow). פונקציה רקורסיבית בלי תנאי עצירה ממשיכה להוסיף מסגרות למחסנית עד שהיא חורגת מסוף המחסנית. הדוגמה למטה קרסה ב-macOS עם Segmentation fault: 11 וקוד יציאה 139.
  • כתיבה למחרוזת ליטרלית. char *name = "coddy"; name[0] = 'C'; מנסה לשנות זיכרון לקריאה בלבד. ב-Linux זה segfault. ב-macOS אותה תוכנית נעצרה עם Bus error: 10, אות קרוב.
#include <stdio.h>

int depth(int n) {
    return depth(n + 1) + 1;   /* never stops calling itself */
}

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

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

#include <stdio.h>

int main(void) {
    int scores[3] = {72, 88, 95};
    int total = 0;

    for (int i = 0; i <= 3; i++) {   /* <= reads scores[3] */
        total += scores[i];
    }
    printf("Total: %d\n", total);
    return 0;
}

כשהיא קומפלה עם Clang על Mac, היא הדפיסה Total: 256. הסכום הנכון הוא 255. scores[3] היה 4 הבתים הבאים במחסנית, שהיו שייכים לתוכנית, ולכן לא קרתה תקלה וערך הזבל נוסף בשקט. מערכת ההפעלה עוצרת רק גישות לזיכרון שהתוכנית לא מחזיקה בכלל.

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

Best: 95
No scores, nothing to read

מה המשמעות של "core dumped"

Core dump הוא קובץ שמחזיק עותק של הזיכרון של התוכנית ברגע שהיא קרסה. דיבאגר יכול לפתוח אותו אחר כך ולהראות בדיוק איפה התוכנית הייתה ומה המשתנים שלה החזיקו: gdb ./app core. בהרבה הפצות Linux, systemd-coredump אוסף את הקבצים האלה, ו-coredumpctl list מציג אותם. כש-core dumps כבויים, למשל עם ulimit -c 0, ההודעה היא רק Segmentation fault, בלי המילים שבסוגריים.

איך מוצאים את השורה שקרסה

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

דיבאגר. קמפלו עם מידע דיבוג והריצו את התוכנית בתוך הדיבאגר. כשהיא נעצרת, bt (backtrace) מדפיס את שרשרת הקריאות לפונקציות עם שמות קבצים ומספרי שורות. ב-Linux הדיבאגר הוא בדרך כלל gdb, וב-macOS הוא lldb, שבו bt עובד באותה צורה.

gcc -g app.c -o app
gdb ./app
(gdb) run
(gdb) bt

AddressSanitizer. קמפלו עם -fsanitize=address (גם GCC וגם Clang תומכים בזה) והריצו את התוכנית כרגיל. במקום segfault סתמי, הוא מדפיס דוח שמציין את סוג הטעות, כמו heap-use-after-free או stack-buffer-overflow, עם השורה שביצעה את הגישה והשורה שהקצתה את הזיכרון. הוא תופס גם את הקריאה השקטה של איבר אחד מעבר לסוף שראינו למעלה, שבעצמה אף פעם לא קורסת.

Valgrind. ב-Linux, valgrind ./app מריץ תוכנית בלי שינויים ומדווח על כל קריאה או כתיבה לא תקינה, כמו Invalid read of size 4.

Segmentation fault בשפות אחרות

Python, Java ו-JavaScript בודקות כל אינדקס וכל הפניה לפני השימוש בהם, ולכן אותן טעויות הופכות לחריגות עם הודעות ברורות: IndexError או AttributeError ב-Python, ArrayIndexOutOfBoundsException או NullPointerException ב-Java, TypeError ב-JavaScript. אפשר לתפוס אותן עם טיפול בחריגות. ב-segfault אי אפשר לטפל בצורה הזו: זה אות, ובלוק catch של C++ לא רואה אותו.

תוכניות Python עדיין יכולות לקבל segfault כשקוד C נכשל מתחתן. השורה הזו מבקשת מ-ctypes לקרוא את כתובת 0:

import ctypes
ctypes.string_at(0)

בהרצה עם python3 -X faulthandler, Python 3.12 הדפיס Fatal Python error: Segmentation fault, ואחריו את שורות ה-Python שרצו. אותה קריסה קורית כשלהרחבת C או לספרייה נייטיבית יש באג זיכרון. Rust נוקטת גישה אחרת: הקומפיילר שלה דוחה את רוב הקוד שעלול לגשת לזיכרון לא תקין, עוד לפני שהתוכנית נבנית.

לאן ממשיכים מכאן

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

שאלות נפוצות

איך מתקנים segmentation fault?
קודם מוצאים את השורה: קמפלו עם -g והריצו את התוכנית תחת gdb או lldb, ואז הקלידו bt אחרי הקריסה, או קמפלו עם -fsanitize=address כדי לקבל דוח מפורט. אחר כך תקנו את המצביע או האינדקס שהשורה הזו משתמשת בו: בדקו מצביעים מול NULL, השאירו אינדקסים של מערכים מתחת לאורך, הפסיקו להשתמש בזיכרון אחרי free, וודאו שהרקורסיה מסתיימת.
האם segmentation fault זה דליפת זיכרון?
לא, אלה בעיות הפוכות. דליפת זיכרון היא זיכרון שהתוכנית הקצתה ואף פעם לא שחררה, ולכן היא ממשיכה לרוץ ומשתמשת ביותר ויותר זיכרון. Segmentation fault היא גישה לזיכרון שלא שייך לתוכנית, והתוכנית נעצרת מיד. טעויות עם free, כמו שימוש במצביע אחרי ששוחרר, יכולות לגרום ל-segfault, ואילו שכחה של free גורמת לדליפות.
למה זה נקרא segmentation fault?
השם מגיע מחלוקת זיכרון לסגמנטים (segmentation), תכנון ישן שבו הזיכרון של תוכנית חולק לסגמנטים עם גבולות קבועים, ונגיעה בכתובת מחוץ לסגמנט שלכם נחשבה לתקלה (fault). מערכות מודרניות מנהלות את הזיכרון בדפים במקום זאת, אבל השם, וגם שם האות SIGSEGV, נשארו. ב-Windows אותו אירוע נקרא access violation.
האם ב-Python יכול לקרות segmentation fault?
קוד Python רגיל לא גורם לזה, כי Python בודק כל אינדקס וכל הפניה ומעלה חריגה במקום. תוכנית Python עדיין יכולה לקבל segfault בתוך קוד C: מודול הרחבה שנכתב ב-C, ספרייה כמו framework ללמידת מכונה, או ctypes. הרצה של python -X faulthandler script.py מדפיסה את שורות ה-Python שרצו ברגע הקריסה.
מה המשמעות של קוד יציאה 139?
קוד יציאה 139 אומר שהתהליך נהרג על ידי אות 11, שהוא SIGSEGV, כלומר segmentation fault. ה-shell מדווח על מוות מאות כ-128 ועוד מספר האות, ו-128 + 11 = 139. ב-Docker וב-Kubernetes, קונטיינר שיוצא עם 139 הוא קונטיינר שהתהליך הראשי שלו קיבל segfault.
איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל