اشاره‌گرهای معلق

از ویکی‌پدیا، دانشنامهٔ آزاد
پرش به: ناوبری، جستجو

اشاره‌گر های معلق در زبانهای برنامه نویسسی اشاره گرهایی که به یک شی مشخص اشاره نمی‌کنند. شرایط مختلفی به وجود میاید که سلامت کد را به خطر میندازد.

اشارهگر معلق زمانی به وجود میاید که یک شی حذف یا ازادسازی میشود، بدون اینکه مقدار یک اشارهگر تغییر داده شده باشد. در نتیجه ان اشارهگر به مکانی اشاره میکند که دیگر وجود ندارد. حتی ممکن است سیستم مکان قبلی حافظه که ازاد شده بود را به فرایند دیگری داده باشد، در این صورت ان اشارهگر به دادهای متفاوت اشاره میکند. بیشتر زمانی این اتفاق میفتدکه برنامه سعی میکند دادههایی روی حافظه بنویسد. در این صورت ممکن است برنامه اطلاعاتی را تغییر میدهد و باعث بروز باگهایی در خود ان برنامه یا برنامه های دیگر شود که تشخیص ان کار ساده ای نیست.

اشارهگر های معلق زمانی به وجود میایند که یک اشارهگر قبل از مقداردهی اولیه یک متغییر به وجود میاید. ممکن است در یک زبان برنامه نویسی این اتفاق میفتد در این صورت احتمال خطایابی و رفع ان بیشتر میشود.

دلایل بروز اشارهگرهای معلق[ویرایش]

در بسیاری از زبان های برنامه نویسی، مانند C، وقتی یک شی از محدوده خارج میشود یا صریحاً دستور خالی شدن دریافت میکند، اشارهگرهایی که به ان شی اشاره میکنند تغییر نمی‌کنند.

به عنوان مثال به کد زیر توجه کنید:

{
   char *dp = NULL;
   /* ... */
   {
       char c;
       dp = &c;
   } /* c falls out of scope */
     /* dp is now a dangling pointer */
}

در صورتی که سیستم عامل بتواند اشارهگرهایی که به پیوندهای پوچ میرسند، شناسایی کند، میتواند انها را برابر 0 یا NULL قرار دهد. یک راه دیگر این است که به طور مثال اجازه داده نشود dp دوباره استفاده شود.

یک راه دیگر بوجود امدن اشارهگر معلق، استفاده همزمان تابع malloc و در ادامه free است. در واقع بعد از اینکه یک اشارهگر با malloc مقداردهی میشود توسط free خالی میشود و اشارهگر استفاده شده معلق میشود. به کد زیر توجه کنید:

#include <stdlib.h>
 
void func()
{
    char *dp = malloc(A_CONST);
    /* ... */
    free(dp);         /* dp now becomes a dangling pointer */
    dp = NULL;        /* dp is no longer dangling */
    /* ... */
}

یک اشتباه رایج استفاده از ادرس متغییرهای محلی در یک تابع است که با برگرداندن یک اشارهگر به ان قسمت، ان اشارهگر را معلق میکند. چرا که اشارهگر استفاده شده به جایی از حافظه اشاره میکند که پس از اتمام اجرای تابع خالی شده و دیگر تضمینی بر حفظ مقادیر وجود ندارد.

int *func(void)
{
    int num = 1234;
    /* ... */
    return &num;
}

تلاش برای خواندن مقدار برگشت داده شده از تابع، پس از اندکی بعد از فراخوانی ان تابع ممکن است نتیجه 1234 را برگرداند اما در ادامه برنامه احتمالاً ان حافظه برای استفاده دیگری اختصاص خواهد یافت و مقدار مورد نظر ما، پاک خواهد شد.

دلایل به وجود امدن اشارهگرهای وحشی[ویرایش]

اشارهگرهای وحشی زمانی به وجود میایند که اشارهگرها قبل از اولین استفاده، مقداردهی نشده باشند. در واقع زمانی که یک اشارهگر مقداردهی اولیه نمی‌شود، ان اشارهگر حتماً وحشی است. به مثال زیر توجه کنید:

int f(int i)
{
    char *dp;    /* dp is a wild pointer */
    static char *scp;  /* scp is not a wild pointer:
                        * static variables are initialized to 0
                        * at start and retain their values from
                        * the last call afterwards.
                        * Using this feature may be considered bad
                        * style if not commented */
}

سوراخ های امنیتی مرتبط[ویرایش]

مانند سرریز بافر، اشارهگرهای معلق به راحتی به سوراخ های امنیتی تبدیل میشوند. برای مثال اگر یک اشارهگر بخواهد یک تابع virtual function را فراخوانی کند، در صورتی که اشارهگر دستکاری شده باشد، یک ادرس دیگر میتواند فراخوانی شود که ممکن است دستور اجرای یک فرایند خرابکارانه باشد.

روشهای جلوگیری[ویرایش]

در زبان برنامه نویسی C/C++ راحتترین روش قابل استفاده، استفاده از یک تابع جایگزین برای free یا استفاده از تابع delete در زمان مناسب است. به هر حال با این روش، تنها برای یک اشارهگرهایی اثربخش است که مستقیماً توسط توابع تعریف شده خالی شوند، اما سایر اشارهگرها را از حال معلق خارج نمی‌سازد.

#include <assert.h>
#include <stdlib.h>
 
/* Alternative version for 'free()' */
void safefree(void **pp)
{
    /* in debug mode, abort if pp is NULL */
    assert(pp);
    if (pp != NULL) {               /* safety check */
        free(*pp);                  /* deallocate chunk, note that free(NULL) is valid */
        *pp = NULL;                 /* reset original pointer */
    }
}
 
int f(int i)
{
    char *p = NULL, *p2;
    p = (char *)malloc(1000);    /* get a chunk */
    p2 = p;              /* copy the pointer */
    /* use the chunk here */
    safefree((void **)&p);       /* safety freeing; does not affect p2 variable */
    safefree((void **)&p);       /* this second call won't fail */
    char c = *p2;       /* p2 is still a dangling pointer, so this is undefined behavior. */
    return i + c;
}

راه حل دیگر این است که قبل از استفاده از malloc() اشارهگر بررسی شود و در صورت معلق بودن 0 مقداردهی شود.

    safefree(&p);        /* i'm not sure if chunk has been released */
    p = malloc(1000);    /* allocate now */

این راهکارها میتواند توسط یک دستور #define با ایجاد چند ماکرو مفید، خلاصه شود. به هر شکل، این برنامه نویس است که باید تصمیم بگیرد از توابع امن استفاده کند. در صورت عدم استفاده از این توابع مشکلات اشارهگرهای معلق باز هم برمیگردد.

یک روش دیگر که قابل استفاده درراهکارهایی که بشتر ساختاریافته هستند استفاده از اشارهگر هوشمنند است. اشارهگر هوشمند، به این شکل عمل میکند که تعداد اشارهگرها به یک شی را میشمارد و زمانی که نیاز به ازاد سازی حافظه شد، با ازاد سازی ان شی، تمام اشارهگرهای ان را هم به NULL تغییر میدهد.

روش دیگر استفاده از تکنیک Garbage Collection استف. در این حالت این سیستم عالم است که حساب اشارهگرهای برنامه اجرا شده را در اختیار میگیرد و در صورتی که شی پیدا شود که اشارهگری به ان اشاره نمی‌کند، ان شی را از حافظه پاک میکند. این روش به کلی احتمال بروز اشارهگرهای معلق را از بین میبرد.

در زبانهای برنامه نویسی سطح بالا، مانند JAVA، امکان بروز اشارهگر معلق وجود ندارد، چرا که هیچ روش مستقیمی برای خالی کردن حافظه در این زبان تعبیه نشده و تمام ازادسازی حافظه توسط Garbage Collector انجام میشود.

روش شناسایی[ویرایش]

یک روش این است که تمام اشارهگرها را تا قبل از مقدار دهی برابر 0 یا null pointer قرار دهیم، همین روش باید برای اشارهگرهایی که مقادیر مرتبط با ان ازادسازی شده اند استفاده شود. اشارهگهر null خطری برای استفاده ندارد و در هیچ سیستم کامپیوتری استفاده از ان منجر به از دستگاری داده ها و خرابکاری نمی‌شود. در بیشتر کامپایلرها با فراخوانی یا خواندن مقدار null pointer برنامه متوقف میشود و ادامه نمی‌یابد.

منابع[ویرایش]