מספר קסם (תכנות)

בתכנות מחשבים, מספר קסם (magic number) הוא בדרך כלל קבוע מספרי שרירותי או שרירותי לכאורה, כלומר שלא ניתן להבין מערכו באופן מיידי מהם משמעותו והשימוש שהתוכנה עושה בו. נוהג קלוקל של מתכנתים הוא לכתוב קוד מקור ולשלב בו ישירות קבועים מספריים ללא כל הסבר או פירוש. קטע הפסאודו קוד הבא מדגים את הליקוי שבמספרי קסם מהסוג הזה: for i from 1 to 52 j := i + randomInt(53 - i) - 1 a.swapEntries(i, j) למתכנת הקורא את הקוד לעיל לא קל להבין את תכליתו ואת משמעותו של המספר 52. הרבה יותר קל לבחון את הקוד בצורתו כדלהלן - constant int deckOfCardsSize := 52 for i from 1 to deckOfCardsSize j := i + randomInt(deckOfCardsSize + 1 - i) - 1 a.swapEntries(i, j) ולהבין מיד שמדובר בפעולת ערבוב של חפיסת קלפי משחק, והמספר 52 מייצג את מספר הקלפים בחפיסה. מעבר ליתרון שבקוד הקריא יותר, מאפשר השימוש בקבוע גם שינוי של הערך במקום אחד (אם בעתיד נרצה לערבב מספר שונה של קלפים) והימנעות מבאגים הנובעים משינוי הערך במקום אחד והשארת הערך הישן במקום אחר. כמו כן מאפשר הדבר גילוי קל יותר של שגיאות הקלדה, משום שהקלדה שגויה של שם המשתנה תגרום לשגיאת מהדר, בעוד שהקלדת מספר לא נכון תגרום להתנהגות לא צפויה בזמן ריצת התוכנית. אל מול יתרונות אלה עומד החסרון שבשורות קוד ארוכות יותר (במיוחד כאשר מעורבים בחישוב מספר קבועים בעלי שמות ארוכים) ובקושי שבניפוי שגיאות, משום שאחדים מהמנפים אינם מציגים את ערכיהם של קבועים. במספר מקרים יוצאים מן הכלל אין לגנות שימוש במספרים באופן ישיר בקוד. לדוגמה, אין כל צורך להחליף בקבוע את המספר 0 המאתחל מונה לולאה. גם השימוש בקבוע בעל שם משמעותי אינו מושלם, משום שהמספר בסופו של דבר אינו טיפוס נתונים של ממש וניתן לעשות עליו פעולות שאינן הגיוניות. לכן עדיף להשתמש בטיפוס מיוחד הנקרא ספירה (enum, enumeration), המאפשר למתכנת למספר ערכים לוגיים שונים מבלי להפוך אותם בפועל למספרים לכל דבר. אמנם ברוב השפות (למעט JAVA) מוחלפים מאחורי הקלעים טיפוסים אלה במספר פשוט, אולם טעויות כגון חישוב אריתמטי והשוואה עם משתנה שהוא במהותו לוגי, נמנעות. סוג נפוץ אחר של מספרי קסם הם המזהים המופיעים בכותרת (header) של קבצים בינאריים ושל הודעות בפרוטוקולי תקשורת. מטרתם לאפשר לתוכנה הקוראת את הקובץ או את ההודעה לבדוק באופן מיידי, על ידי השוואה פשוטה, האם המידע הבינארי הוא מהסוג שהיא מצפה לו. תוכנות קוראות מורכבות יותר יכולות גם לקרוא את המזהה ולברור מבין תוכנות אחרות, את זו המיועדת לטפל בסוג המידע המתאים למספר הקסם שקראה. דוגמה למספר קסם כזה הוא הקבוע 0xCAFEBABE (בבסיס הקסדצימלי) שמהווה את ארבעה הבתים הראשונים של כל קובץ מהודר בשפת תכנות JAVA. בבואו להריץ מחלקה נתונה, יכול ה-JVM לקרוא את ארבעת הבתים הראשונים ובמקרה שהם שונים ממספר הקסם, לדחות את המחלקה בהודעת שגיאה, מבלי לנסות לקרוא ממנה עוד. באופן דומה, כל הודעה בפרוטוקול התקשורת SMB, מתחילה בקבוע 0xff534d42, כאשר שלושת הבתים האחרונים הם ערכי האותיות SMB. בעוד ששימוש כגון זה במספר קסם אינו רע כשלעצמו, עדיין ראוי שקוד המקור של התוכנה הקוראת את המידע הבינארי, כאשר היא משווה את מה שקראה עם מספר הקסם, תעשה זאת באמצעות קבוע בעל שם משמעותי ולא מספר סתום. מספרי קסם המשמשים לניפוי שגיאות הם ערכים מיוחדים, הנכתבים לכתובות בזיכרון מחשב בעת הקצאת זיכרון דינמית או שחרורו. הרעיון מאחורי כתיבה זו, הוא לאפשר למתכנת להבחין מיד מתי התוכנה משתמשת בערכי "זבל", כלומר בערכים שלא אותחלו, על ידי כך שהוא רואה בזיכרון המחשב (באמצעות מנפה השגיאות או זריקת זיכרון (core dump)) שהיא טוענת ומשתמשת בערך שהוא מכיר כמספר קסם. לדוגמה, פונקציית ()HeapAlloc של מיקרוסופט, כאשר היא מופעלת במצב ניפוי שגיאות, מקנה את הערך ה"קסום" 0xABABABAB לכל הבתים על הערימה שלא הוקצו, אך נמצאים בסמוך לשטחים מוקצים. באופן זה קל לגלות גלישת חוצץ אל בתים אלה. בשימוש במספר קסם לצורך ניפוי שגיאות גלומה הנחה הסתברותית, שאותו מספר לא יוצב בדרך מקרה בזיכרון על ידי התוכנית כחלק מפועלה התקין. במקרה בלתי סביר זה ייתכן שמנפה השגיאות יודיע על באג שלא קיים באמת, או שיכשל בגילוי באג קיים. מספר קסם מתוך "קובץ הז'רגון", מונחון נודע לעגת האקרים