מצביע האפס

במחשוב, מצביע האפס (באנגלית: Null pointer) הוא ערך שמור שנועד לציין הצבעה לאובייקט בלתי חוקי. תוכניות מחשב משתמשות באופן שגרתי במצביע אפס כדי לייצג תנאים מסוימים כגון סוף רשימה או כישלון בפעולה כלשהי האמורה להציב ערך למצביע כלשהו. ניתן להשוות את מצביע האפס לערך "Nothing" או "None" המופיע במספר שפות תכנות.

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

== בשפת C ==
ב-C, מובטח ששני מצביעי אפס מכל סוג יהיו שווים.<ref>ISO/IEC 9899, clause 6.3.2.3, paragraph 4.</ref> המאקרו <code>NULL</code> מוגדר כקבוע,<ref>ISO/IEC 9899, clause 7.17, paragraph 3: NULL... which expands to an implementation-defined null pointer constant...</ref> וב-C99 ניתן לבטאו כ-<code>(0(void *))</code> – כלומר המרה של הערך <code>0</code> לסוג <code>void*</code> (מצביע לסוג void).<ref>ISO/IEC 9899, clause 6.3.2.3, paragraph 3.</ref> תקן C אינו קובע שמצביע האפס אמור להיות זהה לכתובת המצביע לכתובת 0, אם כי זה עשוי להיות המקרה בפועל. התייחסות (כלומר, ניסיון לקרוא את תוכן הזיכרון בערך המצביע) למצביע אפס היא התנהגות לא מוגדרת ב-C.<ref name="ub">ISO/IEC 9899, clause 6.5.3.2, paragraph 4, esp. footnote 87.</ref>

בפועל, התייחסות למצביע null עלולה לגרום לניסיון קריאה או כתיבה מזיכרון שאינו ממופה, ולעורר שגיאת גישה לזיכרון, דבר העשוי לגרום לקריסת תוכנית, או להפוך לחריגת תוכנה הניתנת לטיפול על ידי קוד התוכנית. עם זאת, ישנן נסיבות מסוימות שבהן לא תיווצר בהכרח שגיאה. כך לדוגמה, ב- x86, הכתובת <code>0000:0000</code> ניתנת לעיתים לקריאה או לכתיבה, והפניית מצביע לכתובת זו היא פעולה חוקית לחלוטין אך בדרך כלל לא רצויה שעלולה להוביל להתנהגות לא מוגדרת (אך לא קריסה) באפליקציה. ישנם מקרים שבהם הפניית המצביע לכתובת אפס היא מכוונת ומוגדרת היטב; לדוגמה, קוד BIOS שנכתב ב-C עבור התקני x86 של 16 סיביות עשוי לכתוב את טבלת הפסיקות בכתובת פיזית 0 של המכונה על ידי הפניה של מצביע null לכתיבה.
במימוש מהדר ניתן לבצע אופטימיזציה המניחה שאין בקוד התייחסות למצביע null. מימוש זה יייתן ביצועים טובים יותר למהדר בקוד תקין, אך עשוי לגרום להתנהגות בלתי צפויה במידה והקוד מכיל בטעות התייחסויות למצביע האפס.

== ++C ==
ב-++C, בעוד <code>NULL</code> עבר בירושה מ-C, המספר השלם של אפס הועדף באופן מסורתי כדי לייצג קבוע מצביע אפס.<ref></ref> עם זאת, בגרסת C++11 הוסף הקבוע המפורש <code>nullptr</code> לשימוש ייעודי כמצביע האפס.

== שפות אחרות ==
בסביבות שפת תכנות מסוימות (לפחות מימוש קנייני אחד של Lisp, למשל), הערך המשמש כמצביע null (נקרא <code>nil</code> ב-Lisp) עשוי להיות למעשה הכתובת בזיכרון של נתונים פנימיים השימושיים ליישום שאינם נגישים ישירות מתוכניות המשתמש. כך מתאפשרת בדרך מהירה גישה למידע "פנימי" של היישום (הידוע כ"ווקטור <code>nil</code>").

שפות תכנות שונות משתמשות בטרמינולוגיה שונה עבור מצביע האפס. ב-Python, למשל, הערך קרוי <code>None</code>. בפסקל ובסוויפט, מצביע אפס נקרא <code>nil</code>. באייפל, <code>void</code>.

== התייחסות למצביע האפס ==
היות שמצביע האפס אינו מצביע על אובייקט בעל משמעות, ניסיון להתייחס למצביע ריק (כלומר, לגשת לנתונים המאוחסנים במיקום הזיכרון אליו מורה המצביע) בדרך כלל (אך לא תמיד) גורם לשגיאת זמן ריצה או קריסת תוכנית מיידית.
* ב-C, התייחסות למצביע אפס היא התנהגות לא מוגדרת. יישומים רבים גורמים לקוד כזה לגרום לעצירת התוכנית, מכיוון שייצוג המצביע null נבחר להיות כתובת שלעולם לא מוקצית על ידי המערכת לאחסון אובייקטים. עם זאת, התנהגות זו אינה אוניברסלית וגם אינה מובטחת, שכן מהדרים רשאים לבצע אופטימיזציה של תוכניות בהנחה שהם נקיים מהתנהגות לא מוגדרת.
* ב-Delphi ובמימושים אחרים של Pascal, הקבוע nil מייצג מצביע לכתובת הראשונה בזיכרון המשמשת גם לאתחול משתנים. התייחסות אליו תגרום לחריגת מערכת הפעלה.
* ב-Java, התייחסות ל- null מפעילה NullPointerException (NPE), אשר ניתנת לטיפול על ידי קוד טיפול בשגיאות, אך הנוהג המועדף הוא לנסות להבטיח שחריגים כאלה לעולם לא יתרחשו.
* ב-, גישה ל- null גורמת ל-NullReferenceException. טיפול בה אינו מומלץ אך אפשרי.
* ב-Objective-C, הודעות עשויות להישלח לאובייקט <code>nil</code> (שהוא מצביע null) מבלי לגרום להפרעה לתוכנית. התכנית מתעלמת מההודעה, וערך ההחזרה (אם ישנו) הוא <code>nil</code> או <code>0</code>, תלוי בסוג.<ref>The Objective-C 2.0 Programming Language, [https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocObjectsClasses.html#//apple_ref/doc/uid/TP30001163-CH11-SW7 section "Sending Messages to nil"].</ref>
* לפני ההשקה של SMAP, ניתן היה לנצל באג של הפניית מצביע null על ידי מיפוי pagezero לתוך מרחב הכתובות של התוקף, ומכאן לגרום למצביע null להצביע לאזור זה, מה שעלול להוביל לביצוע קוד זדוני במקרים מסוימים.<ref>[https://bugs.chromium.org/p/project-zero/issues/detail?id=782&can=1&q=null%20pointer&sort=-reported&colspec=ID%20Status%20Restrict%20Reported%20Vendor%20Product%20Finder%20Summary OS X exploitable kernel NULL pointer dereference in AppleGraphicsDeviceControl]</ref>

== טיפול בבאגים ==
ישנן טכניקות כדי להקל על ניפוי באגים הנגרמים מהתייחסות למצביע אפס.<ref name="BondNethercote2007"></ref><ref name="Cornu2016"></ref> הוצע<ref name="BondNethercote2007" /> לשנות את JVM כדי לעקוב אחר התפשטות של ערך אפס. הרעיון של מערכת Casper<ref name="Cornu2016" /> הוא להשתמש בטרנספורמציה של קוד מקור על מנת לעקוב אחר התפשטות זו, מבלי לשנות את ה-JVM. במקרים מסוימים, ניתן ליצור אוטומטית תיקון כדי לתקן חריגות של מצביע האפס.<ref></ref>

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

כאשר שפה אכן מספקת אפשרות לשימוש במצביעים, ייתכן שניתן יהיה להפחית או להימנע מהפניות מצביע אפס בזמן ריצה על ידי בדיקה בזמן הידור, באמצעות ניתוח סטטי או טכניקות אחרות. ניתן לראות גישה זו בגרסאות מודרניות של שפת התכנות אייפל, <ref></ref> D,<ref name="SafeD"></ref> ו-Rust . <ref></ref>

ניתן לבצע ניתוח דומה באמצעות כלים חיצוניים.

== היסטוריה ==
בשנת 2009, סר טוני הואר הצהיר<ref></ref> שהוא המציא את מצביע האפס בשנת 1965 כחלק משפת ALGOL W. בהתייחסות זו משנת 2009 מתאר הואר את המצאתו כ"טעות של מיליארדי דולרים":

==ראו גם==
* תו ה-Null

== הערות שוליים ==

קטגוריה:תכנות