משתנה לא מאותחל

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

דוגמה בשפת C

עריכה

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

הנה דוגמה פשוטה ב-C:

void count( void )
{
  int k, i;

  for (i = 0; i < 10; i++)
  {
    k = k + 1;
  }

  printf("%d", k);
}

הערך הסופי של k אינו מוגדר. ההנחה שהערך הסופי של k חייב להיות 10 מתבססת על ההנחה שהערך ההתחלתי שלו הוא 0, מה שעשוי להיות נכון ועשוי להיות שגוי. יש לשים לב לכך שבדוגמה המשתנה i מאותחל לאפס (בתוך ה- for). דוגמה נוספת בערך זה עוסקת במבנים. בדוגמה מטה, יש מבנה - struct student - שמכיל כמה משתנים שנותנים מידע על תלמיד. הפונקציה register_student גורמת לדליפת זיכרון משום שהיא לא מצליחה לאתחל את השדות של struct student new_student כמו שצריך. מעיון מעמיק של הקוד ניתן להסיק כי השדות age ו- semester student_number מאותחלים בצורה טובה, אולם אבל האתחול של האיברים first_name ו- last_name שגוי: אם האורך של מערכי התווים first_name ו- last_name הוא פחות מ-16 בתים, במהלך strcpy,[1] לא מצליחים לאתחל את כל 16 הבתים של הזיכרון השמורים לכל אחד מהשדות הללו של המבנה.

struct student {
  unsigned int age;
  unsigned int semester;
  char first_name[16];
  char last_name[16];
  unsigned int student_number;
};

int register_student(struct student *output, int age, char *first_name, char *last_name)
{
  // If any of these pointers are Null, we fail.
  if (!output || !first_name || !last_name)
  {
    printf("Error!\n");
    return -1;
  }

  // We make sure the length of the strings are less than 16 bytes (including the null-byte)
  // in order to avoid overflows
  if (strlen(first_name) > 15 || strlen(last_name) > 15) {
   printf("first_name and last_name cannot be longer than 16 characters!\n");
   return -1;
  }

  // Initializing the members
  struct student new_student;
  new_student.age = age;
  new_student.semester = 1;
  new_student.student_number = get_new_student_number();

  strcpy(new_student.first_name, first_name);
  strcpy(new_student.last_name, last_name);

  //copying the result to output
  memcpy(output, &new_student, sizeof(struct student));
  return 0;
}

בכל מקרה, גם כאשר משתנה מאותחל באופן מרומז לערך ברירת מחדל כמו 0, זה בדרך כלל לא הערך הנכון . אם הערך הוא ערך ברירת מחדל (כמו 0) זה לא בהכרח מעיד על כך שהאתחול בוצע כהלכה. (עם זאת אתחול של מצביעים למצביע האפס הוא חיוני מכיוון שהוא מונע ביצוע פעולות על המצביעים הללו לפני שהם מאותחלים בפועל לערך הנכון שלהם). ב-C, משתנים שלא מוקצים דינמית ולא מאותחלים במפורש בקוד מאותחלים לאפס (או null עבור מצביעים).

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

שימוש בשפות שונות

עריכה

משתנים לא מאותחלים הם בעיה מסוימת בשפות כמו C ,C++ ואסמבלי שיועדו לתכנות מערכות (אנ'), ופותחו כך שביצועים מועדפים על פני בטיחות - על המתכנת הוטל הנטל להיות מודע לבעיות מסוכנות כמו משתנים לא מאותחלים.

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

  • ב-VHDL המשתנים הסטנדרטיים מאותחלים לערך 'U' מיוחד.
  • ב-Java אין משתנים לא מאותחלים. שדות של מחלקות, אובייקטים ואלמנטים במערכים שלא מאותחלים באופן מפורש מאותחלים אוטומטית עם ערך ברירת המחדל לסוג שלהם (false עבור בוליאני, 0 עבור כל הסוגים המספריים, null עבור פוינטרים). משתנים אחרים חייבים להיות מאותחלים לפני שנעשית עליהם או באמצעותם פעולה כלשהי, אחרת תיגרם שגיאת קומפילציה.

לקריאה נוספת

עריכה
  • "CWE-457 Use of Uninitialized Variable".

הערות שוליים

עריכה