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