מצביע האפס
במחשוב, מצביע האפס (באנגלית: Null pointer) הוא ערך שמור המציין שמצביע או הפניה אינם מתייחסים לאובייקט חוקי. תוכניות משתמשות באופן שגרתי במצביע אפס כדי לייצג תנאים מסוימים כגון סוף רשימה באורך לא ידוע או לייצוג כישלון בפעולה כלשהי האמורה להציב ערך למצביע. ניתן להשוות את מצביע האפס לערך Nothing המופיע במספר שפות תכנות.
אין לבלבל בין מצביע אפס לבין מצביע שאינו מאותחל: עבור מצביע אפס, מובטח שהוא שונה מכל מצביע שמצביע על אובייקט חוקי או מקום תקף בזיכרון. לעומת זאת, מקרים אלו אפשריים בהחלט בחלק משפות התכנות עבור מצביע שאינו מאותחל, ושימוש בו עשוי לגרום להתנהגות בלתי צפויה.
בשפת C
ב- C, מובטח ששני מצביעי אפס מכל סוג יהיו שווים.[1] המאקרו NULL
מוגדר כקבוע,[2] וב- C99 ניתן לבטאו כ (0(void *))
- כלומר המרה של הערך 0
לסוג void*
(מצביע לסוג void ).[3] תקן C אינו קובע שמצביע האפס אמור להיות זהה לכתובת המצביע לכתובת 0, אם כי זה עשוי להיות המקרה בפועל. התייחסות למצביע אפס היא התנהגות לא מוגדרת ב-C.[4]
בפועל, התייחסות למצביע null עלולה לגרום לניסיון קריאה או כתיבה מזיכרון שאינו ממופה, ולעורר שגיאת גישה לזיכרון, דבר העשוי לגרום לקריסת תוכנית, או להפוך לחריגת תוכנה הניתנת לטיפול על ידי קוד התוכנית. עם זאת, ישנן נסיבות מסוימות שבהן זה לא המקרה. לדוגמה, ב- x86, הכתובת 0000:0000
ניתנת לעיתים לקריאה או לכתיבה, והפניית מצביע לכתובת זו היא פעולה חוקית לחלוטין אך בדרך כלל לא רצויה שעלולה להוביל להתנהגות לא מוגדרת אך לא קורסת באפליקציה. ישנם מקרים שבהם הפניית המצביע לכתובת אפס היא מכוונת ומוגדרת היטב; לדוגמה, קוד BIOS שנכתב ב-C עבור התקני x86 של 16 סיביות עשוי לכתוב את טבלת הפסיקות בכתובת פיזית 0 של המכונה על ידי הפניה של מצביע null לכתיבה. אפשרות אחרת למימוש מהדר היא לבצע אופטימיזציה המניחה שאין בקוד התייחסות למצביע null, התנהגות העשויה לגרום להתנהגות לא צפויה במידה והקוד דווקא מכיל בטעות התייחסויות כאלו.
++C
ב-++C, בעוד NULL
עבר בירושה מ-C, המספר השלם של אפס הועדף באופן מסורתי כדי לייצג קבוע מצביע אפס.[5] עם זאת, C++11 הציג את קבוע המצביע null המפורש nullptr
לשימוש במקום זאת.
שפות אחרות
בסביבות שפת תכנות מסוימות (לפחות מימוש קנייני אחד של Lisp, למשל),[דרוש מקור] הערך המשמש כמצביע null (נקרא nil
ב- Lisp ) עשוי להיות למעשה מצביע לגוש של נתונים פנימיים שימושיים ליישום (אך לא ניתן להגיע אליו במפורש מתוכניות משתמש), ובכך לאפשר שימוש באותו אוגר. כקבוע שימושי וכדרך מהירה לגישה למידע "פנימי" של היישום (הידוע כווקטור nil
) .
שפות תכנות שונות משתמשות בטרמינולוגיה שונה עבור מצביע האפס . ב-Python, למשל, הערך קרוי None
. בפסקל ובסוויפט, מצביע אפס נקרא nil
. באייפל, void
.
התייחסות למצביע האפס
היות שמצביע האפס אינו מצביע על אובייקט בעל משמעות, ניסיון להתייחס למצביע ריק (כלומר, לגשת לנתונים המאוחסנים במיקום הזיכרון אליו מורה המצביע) בדרך כלל (אך לא תמיד) גורם לשגיאת זמן ריצה או קריסת תוכנית מיידית.
- ב-C, התייחסות למצביע אפס היא התנהגות לא מוגדרת.[4] יישומים רבים גורמים לקוד כזה לגרום לעצירת התוכנית, מכיוון שייצוג המצביע null נבחר להיות כתובת שלעולם לא מוקצית על ידי המערכת לאחסון אובייקטים. עם זאת, התנהגות זו אינה אוניברסלית וגם אינה מובטחת, שכן מהדרים רשאים לבצע אופטימיזציה של תוכניות בהנחה שהם נקיים מהתנהגות לא מוגדרת.
- ב-Delphi ובמימושים אחרים של Pascal, הקבוע nil מייצג מצביע לכתובת הראשונה בזיכרון המשמשת גם לאתחול משתנים. התייחסות אליו תגרום לחריגת מערכת הפעלה.
- ב-Java, התייחסות ל- null מפעילה NullPointerException (NPE), אשר ניתנת לטיפול על ידי קוד טיפול בשגיאות, אך הנוהג המועדף הוא לנסות להבטיח שחריגים כאלה לעולם לא יתרחשו.
- ב-.NET, גישה ל- null גורמת ל-NullReferenceException. טיפול בה אינו מומלץ אך אפשרי.
- ב-Objective-C, הודעות עשויות להישלח לאובייקט
nil
(שהוא מצביע null) מבלי לגרום להפרעה לתוכנית. התכנית מתעלמת מההודעה, וערך ההחזרה (אם ישנו) הואnil
או0
, תלוי בסוג.[6] - לפני ההשקה של SMAP, ניתן היה לנצל באג של הפניית מצביע null על ידי מיפוי pagezero לתוך מרחב הכתובות של התוקף, ומכאן לגרום למצביע null להצביע לאזור זה, מה שעלול להוביל לביצוע קוד זדוני במקרים מסוימים.[7]
טיפול בבאגים
ישנן טכניקות כדי להקל על ניפוי באגים הנגרמים מהתייחסות למצביע אפס.[8][9] הוצע[8] לשנות את JVM כדי לעקוב אחר התפשטות של ערך אפס. הרעיון של מערכת Casper[9] הוא להשתמש בטרנספורמציה של קוד מקור על מנת לעקוב אחר התפשטות זו, מבלי לשנות את ה-JVM. במקרים מסוימים, ניתן ליצור אוטומטית תיקון כדי לתקן חריגות של מצביע האפס.[10]
שפות פונקציונליות טהורות וקוד משתמש המופעל על ידי מפרש או על מכונות וירטואליות אינם סובלים מהבעיה של הפניית מצביע אפס, שכן לא ניתנת גישה ישירה למצביעים.
כאשר שפה אכן מספקת אפשרות לשימוש במצביעים, ייתכן שניתן יהיה להפחית או להימנע מהפניות מצביע אפס בזמן ריצה על ידי בדיקה בזמן הידור, באמצעות ניתוח סטטי או טכניקות אחרות. ניתן לראות גישה זו בגרסאות מודרניות של שפת התכנות אייפל, [11] D,[12] ו- Rust . [13]
ניתן לבצע ניתוח דומה באמצעות כלים חיצוניים.
היסטוריה
בשנת 2009, סר טוני הואר הצהיר[14] שהוא המציא את מצביע האפס בשנת 1965 כחלק משפת ALGOL W. בהתייחסות זו משנת 2009 מתאר הואר את המצאתו כ"טעות של מיליארדי דולרים":
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
ראו גם
הערות שוליים
- ^ ISO/IEC 9899, clause 6.3.2.3, paragraph 4.
- ^ ISO/IEC 9899, clause 7.17, paragraph 3: NULL... which expands to an implementation-defined null pointer constant...
- ^ ISO/IEC 9899, clause 6.3.2.3, paragraph 3.
- ^ 4.0 4.1 ISO/IEC 9899, clause 6.5.3.2, paragraph 4, esp. footnote 87.
- ^ Stroustrup, Bjarne (במרץ 2001). "Chapter 5:
Theconst
qualifier (§5.4) prevents accidental redefinition ofNULL
and ensures thatNULL
can be used where a constant is required.". The C++ Programming Language (14th printing of 3rd ed.). United States and Canada: Addison–Wesley. p. 88. ISBN 0-201-88954-4.{{cite book}}
: (עזרה) - ^ The Objective-C 2.0 Programming Language, section "Sending Messages to nil".
- ^ OS X exploitable kernel NULL pointer dereference in AppleGraphicsDeviceControl
- ^ 8.0 8.1 Bond, Michael D.; Nethercote, Nicholas; Kent, Stephen W.; Guyer, Samuel Z.; McKinley, Kathryn S. (2007). "Tracking bad apples". Proceedings of the 22nd annual ACM SIGPLAN conference on Object oriented programming systems and applications - OOPSLA '07. p. 405. doi:10.1145/1297027.1297057. ISBN 9781595937865.
- ^ 9.0 9.1 Cornu, Benoit; Barr, Earl T.; Seinturier, Lionel; Monperrus, Martin (2016). "Casper: Automatic tracking of null dereferences to inception with causality traces". Journal of Systems and Software. 122: 52–62. arXiv:1502.02004. doi:10.1016/j.jss.2016.08.062. ISSN 0164-1212.
- ^ Durieux, Thomas; Cornu, Benoit; Seinturier, Lionel; Monperrus, Martin (2017). "Dynamic patch generation for null pointer exceptions using metaprogramming" (PDF). 2017 IEEE 24th International Conference on Software Analysis, Evolution and Reengineering (SANER). IEEE: 349–358. arXiv:1812.00409. doi:10.1109/SANER.2017.7884635. ISBN 978-1-5090-5501-2.
- ^ "Void-safety: Background, definition, and tools". נבדק ב-2021-11-24.
- ^ Bartosz Milewski. "SafeD – D Programming Language". נבדק ב-17 ביולי 2014.
{{cite web}}
: (עזרה) - ^ "Fearless Security: Memory Safety". ארכיון מ-8 בנובמבר 2020. נבדק ב-4 בנובמבר 2020.
{{cite web}}
: (עזרה) - ^ Tony Hoare (2009-08-25). "Null References: The Billion Dollar Mistake". InfoQ.com.
34103857מצביע האפס