مؤشر فارغ

في مجال الحوسبة ، يُشير المؤشر الفارغ ( أو المرجع الفارغ ) إلى قيمة تُفيد بأن المؤشر أو المرجع لا يُشير إلى كائن . تستخدم البرامج عادةً المؤشرات الفارغة لتمثيل حالات مثل نهاية قائمة غير معروفة الطول أو فشل تنفيذ إجراء ما؛ ويمكن تشبيه هذا الاستخدام للمؤشرات الفارغة بالأنواع القابلة للتصفير وقيمة Nothing في نوع الخيار .

لا ينبغي الخلط بين المؤشر الفارغ والمؤشر غير المُهيأ : فالمؤشر الفارغ يضمن عدم تساويه مع أي مؤشر يشير إلى كائن. مع ذلك، لا توفر معظم لغات البرمجة هذا الضمان للمؤشرات غير المُهيأة. فقد يكون مساويًا لمؤشرات أخرى صالحة، أو قد يكون مساويًا لمؤشرات فارغة. وقد يحدث كلا الأمرين في أوقات مختلفة، أو قد تكون المقارنة سلوكًا غير مُحدد . كذلك، في اللغات التي تدعم هذه الميزة، يعتمد الاستخدام الصحيح على خبرة كل مطور وأدوات التدقيق اللغوي . حتى عند استخدامها بشكل صحيح، تظل المؤشرات الفارغة غير مكتملة دلاليًا ، لأنها لا تُتيح إمكانية التعبير عن الفرق بين قيم "غير قابلة للتطبيق" و"غير معروفة حاليًا" و"لم تُحدد بعد". [ 1 ]

في الأنظمة ذات البنية الموسومة ، يمكن استبدال المؤشر الذي قد يكون فارغًا باتحاد موسوم يفرض معالجة صريحة للحالة الاستثنائية؛ في الواقع، يمكن اعتبار المؤشر الذي قد يكون فارغًا بمثابة مؤشر موسوم بعلامة محسوبة.

لأن المؤشر الفارغ لا يشير إلى كائن ذي معنى، فإن محاولة الوصول إلى البيانات المخزنة في موقع الذاكرة (غير الصالح) هذا قد تتسبب في حدوث خطأ أثناء التشغيل أو تعطل البرنامج فورًا. يُعرف هذا بخطأ المؤشر الفارغ ، أو استثناء المؤشر الفارغ . وهو أحد أكثر أنواع نقاط الضعف البرمجية شيوعًا، [ 2 ] وقد وصفه توني هوار ، الذي قدم هذا المفهوم، بأنه "خطأ يكلف مليارات الدولارات". [ 3 ]

ج

في لغة C ، يُضمن تساوي مؤشرين فارغين من أي نوع. [ 4 ] يُوفر ماكرو المعالج المسبق NULL، المُعرّف كثابت مؤشر فارغ مُعرّف من قِبل التنفيذ ، [ 5 ] والذي يُمكن التعبير عنه في C99 بشكل قابل للنقل باستخدام ، حيث تُحوّل القيمة الصحيحة إلى النوع (انظر مؤشر إلى نوع void ). [ 6 ] منذ C23 ، يُمثّل المؤشر الفارغ باستخدام ، وهو من النوع (الذي قُدّم لأول مرة في C++11)، مما يُوفّر مؤشرًا فارغًا آمنًا من حيث النوع. [ 7 ]<stdlib.h>#define NULL ((void*)0)0void*nullptrnullptr_t

لا ينص معيار لغة C على أن المؤشر الفارغ هو نفسه المؤشر إلى عنوان الذاكرة  0، مع أن هذا قد يكون صحيحًا في الواقع العملي. يُعدّ الوصول إلى مؤشر فارغ سلوكًا غير مُعرّف في لغة C، [ 8 ] ويُسمح للتنفيذ المتوافق بافتراض أن أي مؤشر يتم الوصول إليه ليس فارغًا.

عمليًا، قد يؤدي فك مرجعية مؤشر فارغ إلى محاولة قراءة أو كتابة من ذاكرة غير مُخصصة، مما يُسبب خطأ تجزئة أو انتهاكًا للوصول إلى الذاكرة. قد يظهر هذا على شكل تعطل البرنامج، أو يتحول إلى استثناء برمجي يمكن للبرنامج التقاطه. مع ذلك، توجد ظروف معينة لا ينطبق عليها هذا. على سبيل المثال، في الوضع الحقيقي x86 ، يكون العنوان قابلاً للقراءة والكتابة عادةً، وفك مرجعية مؤشر إلى هذا العنوان إجراء صحيح تمامًا ولكنه غير مرغوب فيه عادةً، وقد يؤدي إلى سلوك غير مُحدد ولكنه لا يُسبب تعطلًا في التطبيق؛ إذا تم تمثيل مؤشر فارغ كمؤشر إلى هذا العنوان، فإن فك مرجعيته سيؤدي إلى هذا السلوك. توجد حالات يكون فيها فك مرجعية مؤشر إلى العنوان صفر مقصودًا ومُحددًا جيدًا. على سبيل المثال، قد يقوم كود BIOS المكتوب بلغة C لأجهزة x86 ذات 16 بت في الوضع الحقيقي بكتابة جدول وصف المقاطعات (IDT) عند العنوان الفيزيائي 0 للجهاز عن طريق فك مرجعية مؤشر له نفس قيمة مؤشر فارغ للكتابة. من الممكن أيضًا أن يقوم المترجم بتحسين الكود وإزالة فك مرجعية المؤشر الفارغ، مما يتجنب خطأ تجزئة الذاكرة ولكنه يتسبب في سلوكيات أخرى غير مرغوب فيها. [ 9 ]0000:0000 

لغة سي++

في لغة C++ ، على الرغم من أن NULLالماكرو ورث من لغة C، فقد كان يُفضّل تقليديًا استخدام القيمة العددية الصحيحة للصفر لتمثيل ثابت المؤشر الفارغ. [ 10 ] ومع ذلك، قدّمت C++11 ثابت المؤشر الفارغ الصريح nullptrونوعه nullptr_tلاستخدامه بدلاً من ذلك، مما يوفر مؤشرًا فارغًا آمنًا من حيث النوع. وقد أُضيف nullptrهذا النوع لاحقًا إلى لغة C في الإصدار C23.nullptr_t

لغات أخرى

تستخدم لغات البرمجة رموزًا مختلفة للمؤشر الفارغ . في جافا وسي شارب ، يُستخدم الرمز nullكقيمة حرفية لأنواع المراجع. في باسكال وسويفت ، يُسمى المؤشر الفارغ `null` . في إيفل ، يُسمى مرجعًا. في رست ، يُشار إلى غياب القيمة بـ `null` ، بينما يُشار إلى المؤشر الفارغ الحقيقي بـ ` null` .nilvoidNonestd::ptr::null()

إلغاء المرجع الصفري

لأن المؤشر الفارغ لا يشير إلى كائن ذي معنى، فإن محاولة الوصول إلى البيانات المخزنة في ذلك الموقع من الذاكرة (أي الوصول إلى البيانات الموجودة في ذلك الموقع) عادةً (ولكن ليس دائمًا) تتسبب في حدوث خطأ أثناء التشغيل أو تعطل البرنامج فورًا. وتُصنّف مؤسسة MITRE خطأ المؤشر الفارغ كواحد من أكثر نقاط الضعف البرمجية شيوعًا. [ 11 ]

  • في لغة C ، يُعدّ الوصول إلى مؤشر فارغ سلوكًا غير مُعرّف . [ 8 ] تتسبب العديد من التطبيقات في توقف البرنامج بسبب انتهاك الوصول ، لأن تمثيل المؤشر الفارغ يُختار ليكون عنوانًا لا يُخصصه النظام أبدًا لتخزين الكائنات. مع ذلك، هذا السلوك ليس عامًا، كما أنه غير مضمون، إذ يُسمح للمترجمات بتحسين البرامج بافتراض خلوها من السلوك غير المُعرّف. ينطبق هذا السلوك نفسه على لغة C++ ، حيث لا يوجد استثناء للمؤشر الفارغ فيها. على منصات مثل أنظمة يونكس وويندوز مع مُترجم Visual Studio ، يتسبب انتهاك الوصول SIGSEGVفي إصدار إشارة C/C++. على الرغم من أن الوصول إلى مؤشر فارغ ليس استثناءً يُمكن التقاطه في كتل C++ try، catchإلا أنه من الممكن "التقاط" هذا الانتهاك باستخدام الأقواس ( std::) signal()في C/C++ لتحديد معالج يتم استدعاؤه عند إصدار هذه الإشارة.
    • تتضمن بعض مكتبات C++ الخارجية، مثل مكتبات POCO C++NullPointerException ، فئة. على عكس Java، حيث java.lang.NullPointerExceptionيمتد java.lang.RuntimeException، فإن Poco::NullPointerExceptionيمتد بدلاً من ذلك Poco::LogicException. [ 12 ]
  • في Cyclone ، سيؤدي فشل فحص المؤشر الفارغ إلى ظهور خطأ Null_Exception.
  • في لغة D ، كما هو الحال في لغة C++، يؤدي إلغاء مرجع المؤشر الفارغ إلى خطأ في تجزئة الذاكرة.
  • في دلفي والعديد من تطبيقات باسكال الأخرى، يُمثل الثابت nilمؤشرًا فارغًا إلى العنوان الأول في الذاكرة، والذي يُستخدم أيضًا لتهيئة المتغيرات المُدارة. يؤدي فك مرجعيته إلى ظهور استثناء نظام تشغيل خارجي، والذي يُربط بمثيل EAccessViolationاستثناء باسكال إذا System.SysUtilsكانت الوحدة مرتبطة في usesالعبارة.
  • في لغة جافا ، يؤدي الوصول إلى مرجع فارغ ( null​​) إلى حدوث استثناء NullPointerException(NPE)، والذي يمكن التقاطه بواسطة رمز معالجة الأخطاء، ولكن الممارسة المفضلة هي ضمان عدم حدوث مثل هذه الاستثناءات أبدًا.
  • في بيئة .NET ولغة C#null ، يؤدي الوصول إلى مرجع فارغ (null reference ) System.NullReferenceExceptionإلى ظهور استثناء. على الرغم من أن معالجة هذه الاستثناءات تُعتبر عمومًا ممارسة سيئة، إلا أنه يمكن للبرنامج معالجتها.
  • في لغة Objective-C ، يمكن إرسال الرسائل إلى nilكائن (وهو مؤشر فارغ) دون التسبب في مقاطعة البرنامج؛ يتم تجاهل الرسالة ببساطة، وتكون القيمة المرجعة (إن وجدت) إما فارغة nilأو غير 0فارغة، وذلك حسب النوع. [ 13 ]
  • في لغة Rust ، يؤدي إلغاء مرجعية مؤشر فارغ ( std::ptr::null()​​) في unsafeكتلة إلى سلوك غير محدد، مما يؤدي عادة إلى خطأ في تجزئة الذاكرة أو تلف الذاكرة.
  • قبل إدخال بروتوكول منع الوصول في وضع المشرف (SMAP)، كان من الممكن استغلال ثغرة الوصول إلى مؤشر فارغ عن طريق ربط الصفحة صفر بمساحة عناوين المهاجم ، مما يؤدي إلى توجيه المؤشر الفارغ إلى تلك المنطقة. وقد يؤدي هذا في بعض الحالات إلى تنفيذ التعليمات البرمجية . [ 14 ]

التخفيف

مع أنه قد توجد لغات برمجة لا تقبل القيم الفارغة، إلا أن معظمها يسمح بوجودها، لذا توجد تقنيات لتجنب أو تسهيل تصحيح أخطاء الوصول إلى المؤشرات الفارغة. [ 15 ] يقترح بوند وآخرون تعديل آلة جافا الافتراضية (JVM) لتتبع انتشار القيم الفارغة. [ 15 ]

توجد ثلاثة مستويات للتعامل مع المراجع الفارغة، مرتبة حسب فعاليتها:

  1. اللغات التي لا تحتوي على قيمة فارغة؛
  2. اللغات التي يمكنها تحليل التعليمات البرمجية بشكل ثابت لتجنب إمكانية الوصول إلى قيمة فارغة في وقت التشغيل؛
  3. إذا كان من الممكن حدوث إلغاء مرجعي فارغ في وقت التشغيل، فإليك الأدوات التي تساعد في تصحيح الأخطاء.

Pure functional languages are an example of level 1 since no direct access is provided to pointers and all code and data is immutable. User code running in interpreted or virtual-machine languages generally does not suffer the problem of null pointer dereferencing.

Where a language does provide or utilise pointers which could become void, it is possible to avoid runtime null dereferences by providing compilation-time checking via static analysis or other techniques, with syntactic assistance from language features such as those seen in the Eiffel programming language with Void safety[16] to avoid null dereferences, D,[17] and Rust.[18] Nullable type systems have also been widely adopted in mainstream programming languages: Kotlin, Swift, and TypeScript include them from the start, while C# and Scala have been retrofitted with nullable type systems.[19]

In some languages, analysis can be performed using external tools, but these are weak compared to direct language support with compiler checks since they are limited by the language definition itself.

The last resort of level 3 is when a null reference occurs at runtime; debugging aids can help.

History

In 2009, Tony Hoare stated[3][20][21] that he invented the null reference in 1965 as part of the ALGOL W language. In that 2009 reference Hoare describes his invention as a "billion-dollar mistake":

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

See also

Notes

  1. توسان، إتيان؛ غوالياردو، باولو؛ ليبكين، ليونيد؛ سيكيدا، خوان (2022). "مشاكل القيم الفارغة: آراء المستخدمين". وقائع مؤسسة VLDB . 15 : 2613-2625 . doi : 10.14778/3551793.3551818 . hdl : 20.500.11820/29fe40ac-24be-4b44-a67f-5c191e97092a .
  2. "CWE-476: إلغاء مرجعية مؤشر فارغ" . MITRE .
  3. 1 2 توني هوار (25-08-2009). "المراجع الفارغة: خطأ المليار دولار" . InfoQ.com.
  4. ISO/IEC 9899 ، البند 6.3.2.3، الفقرة 4.
  5. ISO/IEC 9899 ، البند 7.17، الفقرة 3: NULL... والذي يتوسع إلى ثابت مؤشر فارغ محدد من قبل التنفيذ...
  6. ISO/IEC 9899 ، البند 6.3.2.3، الفقرة 3.
  7. "WR14-N3042: تقديم الثابت nullptr" . open-std.org . 2022-07-22. مؤرشف من الأصل في 24 ديسمبر 2022.
  8. 1 2 ISO/IEC 9899 ، البند 6.5.3.2 ، الفقرة 4 ، وخاصة الحاشية 87.
  9. لاتنر، كريس (13 مايو 2011). "ما يجب أن يعرفه كل مبرمج لغة سي عن السلوك غير المحدد #1/3" . blog.llvm.org . مؤرشف من الأصل في 14 يونيو 2023. تم الاطلاع عليه في 14 يونيو 2023 .
  10. ستروستروب، بيارن (مارس 2001). "الفصل 5: يمنع المُؤهِّل (الفقرة 5.4) إعادة تعريف غير مقصودة ويضمن إمكانية استخدامه حيثما يلزم ثابت." لغة برمجة C++ (الطبعة الرابعة عشرة من الطبعة الثالثة ) . الولايات المتحدة وكندا: أديسون-ويسلي. ص 88. ISBNconstNULLNULL   0-201-88954-4.
  11. "CWE-476: إلغاء مرجعية مؤشر فارغ" . MITRE .
  12. "Class Poco::NullPointerException" . docs.pocoproject.org . تم الاطلاع عليه بتاريخ 17 أغسطس 2025 .
  13. "لغة البرمجة Objective-C 2.0" . القسم "إرسال الرسائل إلى nil".
  14. "ثغرة استغلالية في نظام التشغيل OS X تتمثل في الوصول إلى مؤشر فارغ في AppleGraphicsDeviceControl" .
  15. 1 2 بوند، مايكل د.؛ نذركوت، نيكولاس؛ كينت، ستيفن و.؛ غاير، صموئيل ز.؛ ماكينلي، كاثرين س. (2007). "تعقب التفاحات الفاسدة". وقائع المؤتمر السنوي الثاني والعشرين لجمعية ACM SIGPLAN حول أنظمة وتطبيقات البرمجة كائنية التوجه - OOPSLA '07 . ص 405. doi : 10.1145/1297027.1297057 . ISBN  9781595937865. S2CID 2832749 . 
  16. "السلامة في الفراغ: الخلفية والتعريف والأدوات" . تم الاطلاع عليه بتاريخ 24-11-2021 .
  17. بارتوش ميليفسكي. "SafeD – لغة برمجة D" . تم الاطلاع عليه بتاريخ 17 يوليو 2014 .
  18. "أمن بلا خوف: سلامة الذاكرة" . مؤرشف من الأصل في 8 نوفمبر 2020. تم الاطلاع عليه في 4 نوفمبر 2020 .
  19. مادسن، ماغنوس؛ بول، جاكو فان دي (2021). "أنواع العلاقات القابلة للتصفير مع التوحيد المنطقي". وقائع مؤتمر ACM حول لغات البرمجة . 5 : 1-28 . doi : 10.1145/3485487 .
  20. توني هوار (25-08-2009). "عرض تقديمي: "المراجع الفارغة: خطأ المليار دولار"" . InfoQ.com.
  21. كونتييري، ماكسي (18-06-2025). "رائحة الكود 304 - استثناء المؤشر الفارغ" . ماكسيميليانو كونتييري - تصميم البرمجيات . تم الاسترجاع في 18-06-2025 .

مراجع

  • اللجنة الفنية المشتركة ISO/IEC JTC 1، اللجنة الفرعية SC 22، الفريق العامل WG 14 (2007-09-08). المعيار الدولي ISO/IEC 9899 (ملف PDF) (مسودة اللجنة).{{cite book}}: صيانة CS1: أسماء متعددة: قائمة المؤلفين ( رابط ) صيانة CS1: أسماء رقمية: قائمة المؤلفين ( رابط )