اشاره‌گر هیچ‌مقدار

اشاره‌گر هیچ‌مقدار یا ارجاع هیچ‌مقدار در رایانش، یک دارای یک مقدار ذخیره شده‌است برای اینکه نشان دهد اشاره‌گر یا ارجاع به یک شی معتبر اشاره نمی‌کند. برنامه‌ها مرتباً از اشاره‌گرهای هیچمقدار استفاده می‌کنند تا شرایطی مانند پایان یک لیست با طول نامعلوم، یا شکست در اجرای برخی فعالیت‌ها را نشان دهند؛ این استفاده از اشاره‌گرهای هیچمقدار می‌تواند با انواع هیچمقدارپذیر و مقدار هیچ در یک نوع اختیار، مقایسه شود.

یک اشاره‌گر هیچ‌مقدار نباید با اشاره‌گر تعریف نشده اشتباه گرفته شود: تضمین می‌شود مقایسه‌ی اشاره‌گر هیچ‌مقدار با هر اشاره‌گری که به یک شی معتبر اشاره می‌کند، نابرابر خواهد بود. اما با توجه به زبان و پیاده‌سازی، یک اشاره‌گر تعریف نشده ممکن است قادر به چنین تضمینی نباشد. ممکن است با بقیه‌ی اشاره‌گر های معتبر، بصورت برابر مقایسه شود، یا با اشاره‌گرهای هیچمقدار؛ و یا ممکن است در زمان های مختلف، هرکدام از این دو را انجام دهد.

برای مثال، در زبان جاوا، میتوان دو مفهوم null و empty را به شکل زیر مقایسه کرد:

  • تعریف: لغت null ، اصطلاحی است که نشان می‌دهد یک شئ به هیچ چیز اشاره نمی‌کند درحالی که empty مشخص می‌کند که شئ به یک رشته مشخصی به طول صفر اشاره می‌کند
  • ساختار نحوی: دو دستور String s1 = null و String s1 ، نشان دهنده این هستند که s1 به هیچ چیز اشاره نمی‌کند. اما دستور "" = String s2 بیانگر رشته‌ای خالی است.
  • طول رشته: نمایش طول رشته s1 ، استثنای اشاره‌گر هیچ‌مقدار (Null Pointer Exception) را درپی خواهد داشت. درحالیکه نمایش طول رشته s2 ، عدد صفر را خروجی می‌دهد.

Cویرایش

در زبان c، تضمین می‌شود که دو اشاره‌گر هیچ‌مقدار از هر نوعی برابر مقایسه شوند.[۱]

ماکروی NULL پردازشگر، بعنوان یک اشاره‌گر هیچ‌مقدار ثابت در هنگام پیاده‌سازی تعریف می‌شود،[۲] که در C99 می‌تواند بعنوان یک مقدار صحیح 0 تبدیل شده به نوع *void (اشاره‌گر به void) بیان شود.[۳] استاندارد c بیان نمی‌کند که اشاره‌گر هیچ‌مقدار با اشاره‌گر به آدرس حافظه‌ 0 برابر است، اگرچه در عمل این موضوع درست است.بازخوانی (dereference) یک اشاره‌گر هیچ‌مقدار در زبان c یک رفتار تعریف نشده است،و یک پیاده‌سازی تأیید کننده اجازه دارد تا نتیجه بگیرد که هر اشاره‌گری که مورد بازخوانی قرار گرفته، هیچمقدار نیست.

در عمل، بازخوانی یک اشاره‌گر هیچ‌مقدار ممکن است باعث یک تلاش خواندن یا نوشتن در حافظه شود که نگاشته نشده، و باعث یک خطای قطعه‌بندی یا نقض دسترسی حافظه بشود. این موضوع ممکن است خود را بعنوان یک تصادم برنامه نشان دهد، یا تبدیل به یک خطای نرم‌افزاری شود که می‌تواند توسط کد برنامه خنثی شود. اما شرایط خاصی وجود دارند که مورد این نیست، برای مثال، در حالت واقعی x86، آدرس 0000:0000 قابل خواندن و همچنین معمولاً قابل نوشتن هم هست و بازخوانی یک اشاره‌گر به آن آدرس کاملاً معتبر است، اما معمولاً یک فعالیت ناخواسته به‌شمار می‌رود که ممکن است باعث رفتار تعریف نشده اما غیرمخرب در برنامه شود. مواقعی وجود دارند که بازخوانی اشاره‌گر به آدرس صفر عمداً انجام شده و خوش تعریف است؛ برای مثال، کد BIOS نوشته شده در c برای دستگاه‌های طرز واقعی x86 شانزده بیتی، ممکن است IDT را در آدرس فیزیکی 0 از ماشین با بازخوانی یک اشاره‌گر هیچ‌مقدار برای نوشتن، بنویسد. همچنین ممکن است که کامپایلر بازخوانی اشاره‌گر هیچ‌مقدار را به جهت دوری از خطای قطعه‌بندی بهینه‌سازی کند، اما باعث رفتار ناخواسته‌ دیگری بشود.

در مثال زیر، سه عدد اشاره‌گر (pointer) تعریف شده است. ptr1, ptr2, ptr3 .

  • اشاره‌گر ptr1 برابر است با آدرس متغیر عددی num بنابراین، شامل آدرس معتبری از حافظه است.
  • اشاره‌گر ptr2 به جایی اشاره نمی‌کند.(اشاره‌گر Null در اکثر زبان ها)
  • اشاره‌گر ptr3 برابر صفر است.(اشاره‌گر Null در زبان C)
# include < stdio.h >

int main(void){ 

	int num = 10;

	int *ptr1 = &num;

	int *ptr2;

	int *ptr3=0;

	return 0;

}

++Cویرایش

در ++C با وجود اینکه ماکروی NULL از C ارثبری شده است، مقدار صحیح واقعی برای صفر به‌طور سنتی ترجیح داده شده‌است که نمایش دهنده‌ی یک اشاره‌گر هیچ‌مقدار ثابت باشد.[۴] اگرچه 11++C یک nullptr ثابت صریح برای استفاده‌ی جایگزین، معرفی کرده‌است.

JAVAویرایش

از اشاره‌گر Null ، در پیاده‌سازی داده‌ساختارهایی مانند لیست پیوندی (Linked List) و درخت و همچنین در الگوی سینگلتون (Singleton Pattern) استفاده می‌شود. الگوی سینگلتون، تضمین می‌کند که تنها یک نمونه از کلاس موردنظر، ایجاد گردد. نمونه ای از پیاده‌سازی این الگو را برای درک بهتر نحوه استفاده از اشاره‌گر Null در اینجا می‌‌توانید ببینید.

Pythonویرایش

کلمه کلیدی None در پایتون، برای تعریف متغیر یا شئ Null به کار می‌رود. None، یک شئ است و یک نوع‌داده از کلاس NoneType. ما می‌توانیم هر متغیری را None کنیم اما نمی‌توانیم شئ دیگری از NoneType را ایجاد کنیم. در نتیجه، همه متغیرهایی که None هستند، به یک شئ مشخص و یکسان اشاره می‌کنند.

نکاتی که قابل ذکر هستند:

  • مقادیر None و False یکسان نیستند.
  • مقادیر None و صفر یکسان نیستند.
  • مقایسه برابر بودن None با هرچیزی غیر از خودش، خروجی نادرست (False) دارد.

دیگر زبان‌هاویرایش

در برخی از محیط های زبان برنامه‌نویسی، مقدار استفاده شده در اشاره‌گر هیچ‌مقدار (که در Lisp به نام nil شناخته می‌شود) ممکن است درواقع اشاره‌گری به یک بلوک داخلی داده که برای پیاده‌سازی مفید است (اما به‌طور صریح از طریق برنامه‌های کاربر قابل دسترسی نیست)، بنابراین به همان ثبات اجازه داده می‌شود که بعنوان یک ثابت مفید و یک راه سریع برای دسترسی به پیاده‌سازی های داخلی مورد استفاده قرار بگیرد. این راهکار بعنوان بردار nil شناخته می‌شود.

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

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

بازخوانی هیچمقدارویرایش

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

در c بازخوانی یک اشاره‌گر هیچ‌مقدار، یک رفتار تعریف نشده حساب می‌شود[4]. پیاده‌سازی های زیادی باعث می‌شوند که اینگونه کد ها به متوقف شدن برنامه با نقض دسترسی ختم شوند، چون نمایش اشاره‌گر هیچ‌مقدار، انتخاب شده تا آدرسی باشد که هیچ‌گاه توسط سیستم برای ذخیره‌ی اشیاء تخصیص داده نمی‌شود. اما این رفتار، همگانی نیست.

در جاوا، دسترسی به یک ارجاع هیچمقدار باعث فعال شدن یک استثنای اشاره‌گر هیچ‌مقدار(NullPointerException) یا NPE می‌شود که می‌تواند توسط کد مدیریت خطا خنثی شود، اما راهکار ترجیح داده شده بر این است که اطمینان حاصل شود چنین استثناهایی هرگز رخ نمی‌دهند.

در NET. دسترسی به ارجاع هیچمقدار، یک استثنای ارجاع هیچمقدار (NullReferenceException) را برای پرتاب فعال می‌کند. اگرچه گرفتن این استثناء ها به صورت عمومی یک عمل بد تلقی می‌شود، این نوع استثناء می‌تواند توسط برنامه گرفته و مدیریت شود.

در C شی‌گرا، پیغام ها ممکن است بدون قطع کردن برنامه، به شی nil فرستاده شوند (که یک اشاره‌گر هیچ‌مقدار است)؛ پیغام به سادگی نادیده گرفته می‌شود و مقدار خروجی (در صورت وجود) بر طبق نوع، 0 یا nil خواهد بود.[۵]

قبل از معرفی SMAP، یک باگ بازخوانی اشاره‌گر هیچ‌مقدار با نگاشتن pagezero به فضای آدرس مهاجم، به‌طور کامل قابل استخراج بود، در نتیجه باعث اشاره‌ی اشاره‌گر هیچ‌مقدار به آن ناحیه می‌شد. این موضوع در برخی موارد منجر به اجرای کد می‌شد.[۶]

تکنیک هایی برای ساده‌تر شدن دیباگ کردن بازخوانی اشاره‌گر هیچ‌مقدار وجود دارد.[۷][۸] باند و دیگران[۷] پیشنهاد می‌کنند که JVM برای زیر نظر داشتن انتشار هیچمقدار تنظیم شود. ایده‌ی سیستم کسپر[9] استفاده از تبدیل سورس کد برای دنبال کردن این انتشار بدون تنظیم JVM، می‌باشد.

تاریخچهویرایش

در سال ۲۰۰۹ تونی هور بیان کرد[۹] که او در سال ۱۹۶۵ ارجاع هیچمقدار را بعنوان بخشی از زبان ALGOL W اختراع نموده‌است. در آن ارجاع سال ۲۰۰۹ وی اختراع خود را بعنوان «اشتباه میلیارد دلاری» توصیف کرد:

من این را اشتباه میلیارد دلاری خود می‌خوانم. آن اختراع ارجاع هیچمقدار در سال ۱۹۶۵. در آن زمان مشغول طراحی اولین سیستم نوع جامع برای ارجاع در یک زبان شی‌گرا (ALGOL W) بودم. هدفم این بود که اطمینان حاصل کنم که تمامی استفاده ها از ارجاع‌ها کاملاً امن هستند، با بررسی خودکار کامپایلر. اما نتوانستم در مقابل وسوسه‌ی گذاشتن یک ارجاع هیچمقدار مقاومت کنم، به دلیل اینکه پیاده‌سازی آن بسیار راحت بود. این موضوع به خطاها، آسیب‌پذیری ها و تصادم‌های سیستمی بیشماری منجر شد که احتمالاً باعث بوجود آمدن میلیارد ها دلار خسارت و درد در ۴۰ سال گذشته شده‌است.

پانویسویرایش

  1. ISO/IEC 9899, clause 6.3.2.3, paragraph 4.
  2. ISO/IEC 9899, clause 7.17, paragraph 3: NULL... which expands to an implementation-defined null pointer constant
  3. ISO/IEC 9899, clause 6.3.2.3, paragraph 3.
  4. Stroustrup, Bjarne (March 2001). "Chapter 5
    The const qualifier (§5.4) prevents accidental redefinition of NULL and ensures that NULL 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.
  5. The Objective-C 2.0 Programming Language, section "Sending Messages to nil".
  6. OS X exploitable kernel NULL pointer dereference in AppleGraphicsDeviceControl
  7. ۷٫۰ ۷٫۱ Bond, Michael D.; Nethercote, Nicholas; Kent, Stephen W.; Guyer, Samuel Z.; McKinley, Kathryn S. (2007). "Tracking bad apples": 405. doi:10.1145/1297027.1297057.
  8. 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. doi:10.1016/j.jss.2016.08.062. ISSN 0164-1212.
  9. Tony Hoare (2009-08-25). "Null References: The Billion Dollar Mistake". InfoQ.com.

منابعویرایش

  • Joint Technical Committee ISO/IEC JTC 1, Subcommittee SC 22, Working Group WG 14 (2007-09-08). International Standard ISO/IEC 9899 (PDF) (Committee Draft).