سلوك غير محدد
يُظهر برنامج الحاسوب سلوكًا غير مُحدد ( UB ) عندما يحتوي على تعليمات برمجية، أو يُنفذ تعليمات برمجية لا تُحدد مواصفات لغة البرمجة الخاصة به أي متطلبات مُحددة. [ 1 ] يختلف هذا عن السلوك غير المُحدد ، الذي لا تُحدد مواصفات اللغة نتيجته، والسلوك المُحدد بالتنفيذ الذي يعتمد على توثيق مُكون آخر من مكونات النظام الأساسي (مثل واجهة التطبيق الثنائية أو توثيق المُترجم ).
في مجتمع برمجة لغة C ، يُشار إلى السلوك غير المحدد أحيانًا بشكل فكاهي باسم " الشياطين الأنفية "، وذلك نسبةً إلى منشور على موقع comp.std.c شرح السلوك غير المحدد بأنه يسمح للمترجم بفعل أي شيء يختاره، حتى "إخراج الشياطين من أنفك". [ 2 ]
ملخص
تسمح بعض لغات البرمجة للبرنامج بالعمل بشكل مختلف، أو حتى بامتلاك مسار تحكم مختلف عن الكود المصدري، طالما أنه يُظهر نفس الآثار الجانبية المرئية للمستخدم ، شريطة ألا يحدث سلوك غير مُعرَّف أثناء تنفيذ البرنامج . يُشير مصطلح "السلوك غير المُعرَّف" إلى مجموعة من الشروط التي يجب ألا يستوفيها البرنامج.
في الإصدارات الأولى من لغة C ، تمثلت الميزة الأساسية للسلوك غير المحدد في إنتاج مترجمات عالية الأداء لمجموعة واسعة من الأجهزة : إذ كان بالإمكان ربط بنية معينة بميزة خاصة بالجهاز، ولم يكن على المترجم توليد شيفرة إضافية لوقت التشغيل لتكييف الآثار الجانبية مع دلالات اللغة. وكان يُكتب كود البرنامج المصدري بمعرفة مسبقة بالمترجم المحدد والأنظمة الأساسية التي سيدعمها.
مع ذلك، أدى التوحيد التدريجي للمنصات إلى تقليل هذه الميزة، لا سيما في الإصدارات الأحدث من لغة C. الآن، تمثل حالات السلوك غير المحدد عادةً أخطاءً واضحة في الكود، مثل فهرسة مصفوفة خارج حدودها. بحكم التعريف، يمكن لوقت التشغيل أن يفترض أن السلوك غير المحدد لا يحدث أبدًا؛ لذلك، لا يلزم التحقق من بعض الشروط غير الصالحة. بالنسبة للمترجم ، يعني هذا أيضًا أن العديد من تحويلات البرنامج تصبح صالحة، أو يتم تبسيط براهين صحتها؛ وهذا يسمح بأنواع مختلفة من التحسينات التي تعتمد صحتها على افتراض أن حالة البرنامج لا تستوفي أيًا من هذه الشروط. يمكن للمترجم أيضًا إزالة عمليات التحقق الصريحة التي ربما كانت موجودة في الكود المصدري، دون إخطار المبرمج؛ على سبيل المثال، اكتشاف السلوك غير المحدد عن طريق اختبار ما إذا كان قد حدث ليس مضمونًا، بحكم التعريف. هذا يجعل من الصعب أو المستحيل برمجة خيار أمان قابل للنقل (الحلول غير القابلة للنقل ممكنة لبعض البنى).
عادةً ما يُقيّم تطوير المترجمات الحالية أداءها ويقارنه بمعايير مصممة حول التحسينات الدقيقة، حتى على المنصات الأكثر استخدامًا في سوق أجهزة الكمبيوتر المكتبية والمحمولة للأغراض العامة (مثل amd64). ولذلك، يوفر السلوك غير المحدد مجالًا واسعًا لتحسين أداء المترجم، حيث يُسمح بربط شيفرة المصدر لعبارة معينة بأي شيء أثناء التشغيل.
في لغتي C و C++، يُسمح للمُصرّف بإصدار تشخيص أثناء الترجمة في هذه الحالات، ولكنه ليس مُلزمًا بذلك: سيُعتبر التنفيذ صحيحًا بغض النظر عما يفعله في مثل هذه الحالات، على غرار مصطلحات "عدم الاكتراث" في المنطق الرقمي. تقع مسؤولية كتابة كود لا يستدعي سلوكًا غير مُعرّف على عاتق المُبرمج، على الرغم من أن تطبيقات المُصرّف مُصرح لها بإصدار تشخيصات عند حدوث ذلك. تحتوي المُصرّفات الحديثة على علامات تُفعّل هذه التشخيصات، على سبيل المثال، -fsanitize=undefinedتفعيل "مُعقّم السلوك غير المُعرّف" ( UBSan ) في gcc 4.9 [ 3 ] وفي clang . مع ذلك، هذه العلامة ليست افتراضية، وتفعيلها هو خيار الشخص الذي يُنشئ الكود.
في بعض الحالات، قد توجد قيود محددة على السلوك غير المحدد. على سبيل المثال، قد تترك مواصفات مجموعة تعليمات وحدة المعالجة المركزية سلوك بعض أشكال التعليمات غير محدد، ولكن إذا كانت وحدة المعالجة المركزية تدعم حماية الذاكرة ، فمن المحتمل أن تتضمن المواصفات قاعدة عامة تنص على أنه لا يجوز لأي تعليمة يمكن للمستخدم الوصول إليها أن تتسبب في ثغرة أمنية في نظام التشغيل ؛ وبالتالي، يُسمح لوحدة المعالجة المركزية الفعلية بإتلاف سجلات المستخدم استجابةً لمثل هذه التعليمة، ولكن لا يُسمح لها، على سبيل المثال، بالتحول إلى وضع المشرف .
يمكن لمنصة التشغيل أيضًا أن توفر بعض القيود أو الضمانات على السلوك غير المحدد، إذا وثّقت سلسلة الأدوات أو بيئة التشغيل صراحةً أن بنيات محددة موجودة في شفرة المصدر مرتبطة بآليات محددة جيدًا ومتاحة في بيئة التشغيل. على سبيل المثال، قد يوثّق مترجم سلوكًا معينًا لبعض العمليات غير المحددة في مواصفات اللغة، بينما قد لا تفعل ذلك مترجمات أو مُجمّعات أخرى لنفس اللغة. يُنتج المُجمّع شفرة قابلة للتنفيذ لواجهة تطبيق ثنائية (ABI) محددة ، سادًا الفجوة الدلالية بطرق تعتمد على إصدار المُجمّع: يمكن أن توفر وثائق هذا الإصدار من المُجمّع ومواصفات واجهة التطبيق الثنائية قيودًا على السلوك غير المحدد. الاعتماد على تفاصيل التنفيذ هذه يجعل البرنامج غير قابل للنقل ، ولكن قد لا تكون قابلية النقل مصدر قلق إذا لم يكن من المفترض استخدام البرنامج خارج بيئة تشغيل محددة.
يمكن أن يؤدي السلوك غير المحدد إلى تعطل البرنامج أو حتى إلى حالات فشل يصعب اكتشافها وتجعل البرنامج يبدو وكأنه يعمل بشكل طبيعي، مثل فقدان البيانات بصمت وإنتاج نتائج غير صحيحة.
برنامج خاطئ
في تصميم لغات البرمجة ، يُعتبر البرنامج خاطئًا إذا لم تكن دلالاته محددة بدقة، ولكن لا يُلزم تطبيق اللغة بالإشارة إلى وجود خطأ سواءً أثناء الترجمة أو التنفيذ. على سبيل المثال، في لغة آدا :
- إضافةً إلى الأخطاء المحدودة، تُعرّف قواعد اللغة أنواعًا معينة من الأخطاء على أنها تؤدي إلى تنفيذ خاطئ. ومثل الأخطاء المحدودة، لا يلزم أن يكتشف التطبيق هذه الأخطاء قبل وقت التشغيل أو أثناءه. وعلى عكس الأخطاء المحدودة، لا يوجد حدٌّ مُحدد في اللغة للأثر المُحتمل للتنفيذ الخاطئ؛ فالأثر عمومًا غير قابل للتنبؤ. [ 4 ]
إن تعريف حالة ما على أنها "خاطئة" يعني أن تطبيق اللغة لا يحتاج إلى إجراء فحص مكلف محتمل ( على سبيل المثال، أن متغيرًا عامًا يشير إلى نفس الكائن كمعامل روتيني فرعي) ولكنه قد يعتمد مع ذلك على صحة الشرط في تحديد دلالات البرنامج.
فوائد
يُتيح توثيق عملية ما على أنها سلوك غير مُعرَّف للمُترجمات افتراض أن هذه العملية لن تحدث أبدًا في برنامج مُطابق. وهذا يُعطي المُترجم معلوماتٍ أكثر عن الكود، وهذه المعلومات بدورها تُتيح فرصًا أكبر لتحسين الأداء.
مثال على ذلك في لغة C:
int foo ( unsigned char x ) { int value = 2147483600 ; // بافتراض أن القيمة عدد صحيح 32 بت وحرف 8 بت value += x ; if ( value < 2147483600 ) { bar (); } return value ; }لا يمكن أن تكون قيمة xسالبة، وبما أن تجاوز سعة الأعداد الصحيحة الموقعة سلوك غير مُعرَّف في لغة C، يمكن للمُصرِّف أن يفترض أن value < 2147483600ستكون دائمًا خاطئة. وبالتالي، يمكن للمُصرِّف تجاهل ifالعبارة، بما في ذلك استدعاء الدالة ، لأن تعبير الاختبار في ليس له أي آثار جانبية ولن يتحقق شرطه أبدًا. لذلك، فإن الكود مُكافئ دلاليًا لما يلي:barif
int foo ( unsigned char x ) { int value = 2147483600 ; value += x ; return value ; }لو كان المترجم مجبراً على افتراض أن تجاوز عدد صحيح موقع له سلوك التفاف ، فإن التحويل أعلاه لن يكون قانونياً.
يصعب على البشر ملاحظة هذه التحسينات عندما يصبح الكود أكثر تعقيدًا وتُطبّق تحسينات أخرى، مثل تضمين الدوال . على سبيل المثال، قد تستدعي دالة أخرى الدالة المذكورة أعلاه:
void run_tasks ( unsigned char * ptrx ) { int z ; z = foo ( * ptrx ); while ( * ptrx > 60 ) { run_one_task ( ptrx , z ); } }للمُصرّف حرية تحسين الكود وإزالة whileحلقة التكرار هنا بتطبيق تحليل نطاق القيم : بفحص قيمة foo()`d`، يعلم أن القيمة الأولية التي يشير إليها `d` ptrxلا يمكن أن تتجاوز 47 (لأن أي قيمة أكبر ستؤدي إلى سلوك غير مُعرّف في `d` foo())؛ لذلك، سيكون الفحص الأولي لـ `d` *ptrx > 60خاطئًا دائمًا في برنامج مُطابق. علاوة على ذلك، بما أن النتيجة zلا تُستخدم الآن وليس foo()لها أي آثار جانبية، يمكن للمُصرّف تحسين الكود لتصبح دالة فارغة تُعيد القيمة فورًا. قد يكون run_tasks()اختفاء حلقة التكرار مفاجئًا بشكل خاص إذا تم تعريف `d` في ملف كائن مُصرّف بشكل منفصل .whilefoo()
من الفوائد الأخرى للسماح بأن يكون تجاوز سعة الأعداد الصحيحة الموقعة غير مُعرَّف، إمكانية تخزين قيمة المتغير ومعالجتها في سجل معالج أكبر من حجم المتغير في الشيفرة المصدرية. على سبيل المثال، إذا كان نوع المتغير، كما هو مُحدد في الشيفرة المصدرية، أضيق من عرض السجل الأصلي (كما هو الحال intفي جهاز 64 بت ، وهو سيناريو شائع)، فيمكن للمُصرِّف استخدام عدد صحيح موقع 64 بت للمتغير في شيفرة الآلة التي يُنتجها، دون تغيير السلوك المُعرَّف للشيفرة. أما إذا كان البرنامج يعتمد على سلوك تجاوز سعة عدد صحيح 32 بت، فسيتعين على المُصرِّف إضافة منطق إضافي عند الترجمة لجهاز 64 بت، لأن سلوك تجاوز السعة لمعظم تعليمات الآلة يعتمد على عرض السجل. [ 5 ]
كما يسمح السلوك غير المحدد بإجراء المزيد من عمليات التحقق في وقت الترجمة بواسطة كل من المترجمات وتحليل البرنامج الثابت .
المخاطر
تتضمن معايير لغتي C وC++ عدة أشكال من السلوك غير المحدد، مما يوفر مرونة أكبر في تطبيقات المترجمات وعمليات التحقق أثناء الترجمة، على حساب احتمالية حدوث سلوك غير محدد أثناء التشغيل. فعلى وجه الخصوص، يحتوي معيار ISO للغة C على ملحق يسرد المصادر الشائعة للسلوك غير المحدد. [ 6 ] علاوة على ذلك، لا يُشترط على المترجمات تشخيص التعليمات البرمجية التي تعتمد على السلوك غير المحدد. لذا، من الشائع أن يعتمد المبرمجون، حتى ذوي الخبرة منهم، على السلوك غير المحدد إما عن طريق الخطأ، أو ببساطة لعدم إلمامهم بقواعد اللغة التي قد تمتد لمئات الصفحات. قد يؤدي هذا إلى ظهور أخطاء عند استخدام مترجم مختلف أو إعدادات مختلفة. يمكن أن يساعد الاختبار أو الفحص العشوائي مع تمكين عمليات التحقق الديناميكية من السلوك غير المحدد، مثل أدوات Clang ، في اكتشاف السلوك غير المحدد الذي لم يتم تشخيصه بواسطة المترجم أو أدوات التحليل الثابتة. [ 7 ]
قد يؤدي السلوك غير المحدد إلى ثغرات أمنية في البرمجيات. على سبيل المثال، تُعزى ثغرات تجاوز سعة المخزن المؤقت وغيرها من الثغرات الأمنية في متصفحات الويب الرئيسية إلى هذا السلوك. عندما قام مطورو GCC بتغيير مُصرّفهم في عام 2008 بحيث أغفل بعض عمليات التحقق من تجاوز السعة التي تعتمد على هذا السلوك، أصدر مركز الاستجابة لحوادث أمن الحاسوب (CERT) تحذيرًا بشأن الإصدارات الأحدث من المُصرّف. [ 8 ] أشارت مجلة Linux Weekly News إلى أن السلوك نفسه لوحظ في PathScale C و Microsoft Visual C++ 2005 والعديد من المُصرّفات الأخرى؛ [ 9 ] وقد عُدّل التحذير لاحقًا ليشمل تحذيرات من مُصرّفات مختلفة. [ 10 ]
أمثلة
لغة C ولغة C++
يمكن تصنيف الأشكال الرئيسية للسلوك غير المحدد في لغة C على نطاق واسع على النحو التالي: [ 11 ] انتهاكات سلامة الذاكرة المكانية، وانتهاكات سلامة الذاكرة الزمنية، وتجاوز عدد صحيح ، وانتهاكات التسمية الصارمة، وانتهاكات المحاذاة، والتعديلات غير المتسلسلة، وسباقات البيانات، والحلقات التي لا تقوم بعمليات الإدخال/الإخراج ولا تنتهي.
في لغة C، يؤدي استخدام أي متغير تلقائي قبل تهيئته إلى سلوك غير محدد، وكذلك الحال بالنسبة للقسمة على صفر ، أو تجاوز سعة عدد صحيح مُوَقَّع، أو فهرسة مصفوفة خارج حدودها المحددة (انظر تجاوز سعة المخزن المؤقت )، أو الوصول إلى مؤشر فارغ . بشكل عام، أي حالة من حالات السلوك غير المحدد تترك آلة التنفيذ المجردة في حالة غير معروفة، وتتسبب في أن يكون سلوك البرنامج بأكمله غير محدد.
نظراً لأن القيم النصية عادة ما يتم تخزينها في ذاكرة للقراءة فقط، فإن محاولة تعديل إحداها تتسبب في سلوك غير محدد: [ 12 ]
char * p = "wikipedia" ; // دالة صحيحة في لغة C، مهملة في C++98/C++03، وغير صحيحة في C++11 p [ 0 ] = 'W' ; // سلوك غير مُعرَّفالقسمة الصحيحة على صفر تؤدي إلى سلوك غير محدد: [ 13 ]
int x = 1 ; return x / 0 ; // سلوك غير مُعرَّفقد تؤدي بعض عمليات المؤشر إلى سلوك غير محدد: [ 14 ]
int arr [ 4 ] = { 0 , 1 , 2 , 3 }; int * p = arr + 5 ; // سلوك غير مُعرَّف عند فهرسة عنصر خارج النطاق p = nullptr ; int a = * p ; // سلوك غير مُعرَّف عند محاولة الوصول إلى مؤشر فارغفي لغتي C و C++، تُعرَّف المقارنة العلائقية للمؤشرات إلى الكائنات (للمقارنة بين أصغر من أو أكبر من) تعريفًا دقيقًا فقط إذا كانت المؤشرات تشير إلى أعضاء نفس الكائن، أو عناصر من نفس المصفوفة . [ 15 ] مثال:
int main ( void ) { int a = 0 ; int b = 0 ; return & a < & b ; // سلوك غير محدد في لغة C، وسلوك غير مُحدد في لغة C++ }يؤدي الوصول إلى نهاية دالة تُرجع قيمة (بخلاف main()) بدون عبارة إرجاع إلى سلوك غير محدد إذا تم استخدام قيمة استدعاء الدالة من قبل المستدعي: [ 16 ]
int f () {}int x = f (); // سلوك غير مُعرَّفيؤدي تعديل كائن ما بين نقطتين متتاليتين في التسلسل إلى سلوك غير محدد. [ 17 ] طرأت تغييرات كبيرة على أسباب السلوك غير المحدد فيما يتعلق بنقاط التسلسل بدءًا من C++11. [ 18 ] يمكن للمترجمات الحديثة إصدار تحذيرات عند مصادفتها تعديلات غير متسلسلة متعددة على نفس الكائن. [ 19 ] [ 20 ] سيؤدي المثال التالي إلى سلوك غير محدد في كل من لغتي C وC++.
int f ( int i ) { // سلوك غير محدد: تعديلان غير متسلسلين على i return i ++ + i ++ ; }عند تعديل كائن بين نقطتين متتاليتين في التسلسل، فإن قراءة قيمة الكائن لأي غرض آخر غير تحديد القيمة المراد تخزينها يُعد سلوكًا غير محدد. [ 21 ]
a [ i ] = i ++ ; // سلوك غير مُعرَّف printf ( "%d %d \n " , ++ n , pow ( 2 , n )); // سلوك غير مُعرَّف أيضًافي لغتي C/C++، يؤدي إزاحة قيمة بتية بعدد بتات إما سالب أو أكبر من أو يساوي العدد الإجمالي للبتات في هذه القيمة إلى سلوك غير محدد. وأفضل طريقة (بغض النظر عن مُصنِّع المُصرِّف) هي إبقاء عدد البتات المراد إزاحتها (المعامل الأيمن لمعاملات الإزاحة البتية<< ) ضمن النطاق: [ 0 , sizeof value * CHAR_BIT - 1 ] (حيث يُمثِّل المعامل الأيسر).>>value
int num = -1 ; unsigned int val = 1 << num ; // إزاحة بمقدار عدد سالب - سلوك غير مُعرَّفnum = 32 ; // أو أي عدد أكبر من 31 val = 1 << num ; // يتم تعريف القيمة الحرفية '1' كعدد صحيح 32 بت - في هذه الحالة، يعتبر الإزاحة بأكثر من 31 بت سلوكًا غير محددnum = 64 ; // أو أي عدد أكبر من 63 unsigned long long val2 = 1ULL << num ; // القيمة الحرفية '1ULL' مُعرّفة كعدد صحيح 64 بت - في هذه الحالة، يُعتبر الإزاحة بأكثر من 63 بت سلوكًا غير مُعرّفسي شارب
في لغة C# ، يمكن استدعاء السلوك غير المحدد في unsafeالسياق.
باستخدام النظام ؛unsafe { int * p = ( int * ) 0x12345678 ; Console . WriteLine ( * p ); // قراءة عنوان ذاكرة عشوائي }يؤدي استخدام الذاكرة بعد تحريرها من المكدس أيضًا إلى سلوك غير محدد.
باستخدام النظام ؛unsafe int * GetPointer () { int x = 100 ; return & x ; }int * p = GetPointer (); Console.WriteLine ( * p ) ; // يحصل على مؤشر إلى ذاكرة مكدس لم تعد صالحةجافا
في لغة جافا ، يعد التوافق الأصلي وتضارب البيانات من أبرز الحالات التي يحدث فيها سلوك غير محدد.
يمكن أن يؤدي تضارب البيانات التالي إلى سلوك غير محدد عن طريق انتهاك نموذج ذاكرة جافا.
int x = 0 ; boolean ready = false ;Thread t1 = new Thread (() -> { x = 33 ; ready = true ; });Thread t2 = new Thread (() -> { if ( ready ) { System . out . println ( x ); // قد يطبع 33 أو 0 } });t1.start ( ) ; t2.start ( ) ;قد تنشأ مشكلة الذاكرة غير المعرّفة في استدعاءات واجهة Java الأصلية، مما قد يؤدي إلى سلوك غير معرّف. من لغة C:
#include <jni.h>JNIEXPORT jint JNICALL Java_Crash_boom ( JNIEnv * env , jclass cls ) { int * p = NULL ; return * p ; // إلغاء مرجعية مؤشر فارغ }على لغة جافا:
package org.wikipedia.examples ;public class Crash { static { System.loadLibrary ( "crash " ) ; }private static native int boom ();public static void main ( String [] args ) { System . out . println ( boom ()); } }الصدأ
على الرغم من أنه من المتوقع عمومًا عدم حدوث سلوك غير محدد في لغة Rust الآمنة ، إلا أن التعليمات البرمجية غير السليمة وغير الآمنة لا تزال قادرة على تعريض السلوك غير المحدد للتعليمات البرمجية الآمنة فيما يُعرف بثغرات السلامة . [ 22 ]
على سبيل المثال، تستخدم العديد من أنواع البيانات في لغة Rust ثوابت تسمح بإجراء تحسينات مفيدة. تُعد المراجع مثالًا على ذلك ، فمع أنها تمتلك نفس تمثيل المؤشرات الخام ، إلا أنها لا يمكن أن تكون أبدًا فارغة ( null ) أو غير محاذية أو تشير إلى وجهات غير صالحة. وبالتالي، فإن كسر أي من هذه الثوابت غير مُعرَّف بغض النظر عن كيفية استخدام المرجع الناتج.
استخدم std :: mem ؛/// يُنشئ مرجعًا فارغًا. pub const fn null_ref < T : ? Sized > () -> & T { unsafe { mem :: zeroed () } }يكون الاستدعاء null_refدائمًا غير صحيح بسبب الثوابت التي تفرضها جميع أنواع المراجع، على الرغم من أن الدالة نفسها ليست unsafe fnعنصرًا ويمكن استدعاؤها من التعليمات البرمجية الآمنة.
علاوة على ذلك، فإن إلغاء مرجعية أي مؤشر فارغ غير محدد، على الرغم من أن العديد من أنظمة المضيف لا تزال مصممة للتعامل مع مثل هذه الحالات في خطأ تجزئة الذاكرة :
استخدم std :: ptr ;fn main () { let p : * const i32 = ptr :: null ();// تنبيه: `p` قيمة فارغة ولا يمكن الوصول إليها. unsafe { * p }; }مراجع
- ↑ "ما يجب أن يعرفه كل مبرمج لغة C عن السلوك غير المحدد #1/3" . 13 مايو 2011. تم الاطلاع عليه بتاريخ 23 فبراير 2025 .
- ↑ "شياطين الأنف" . ملف المصطلحات . تم الاطلاع عليه بتاريخ 12 يونيو 2014 .
- ↑ معقم سلوك غير محدد من دول مجلس التعاون الخليجي – ubsan
- ↑ "1.1.5 تصنيف الأخطاء" . دليل مرجعي لـ Ada . ISO/IEC 8652:1995(E).
- ↑ "نبذة مختصرة عن المترجمات التي تستغل تجاوز الإشارة" .
- ↑ ISO/IEC 9899:2011 §J.2.
- ↑ جون ريغير (19 أكتوبر 2017). "سلوك غير محدد في عام 2017، مؤتمر cppcon 2017" . يوتيوب .
- ↑ "ملاحظة الثغرة الأمنية VU#162289 — يقوم gcc بتجاهل بعض عمليات التحقق من الالتفاف بصمت" . قاعدة بيانات ملاحظات الثغرات الأمنية . CERT. 4 أبريل 2008. مؤرشفة من الأصل في 9 أبريل 2008.
- ↑ جوناثان كوربيت (16 أبريل 2008). "GCC وتجاوزات المؤشر" . أخبار لينكس الأسبوعية .
- ↑ "ملاحظة الثغرة الأمنية VU#162289 — قد تتجاهل مترجمات لغة C بعض عمليات التحقق من الالتفاف بصمت" . قاعدة بيانات ملاحظات الثغرات الأمنية . CERT. 8 أكتوبر 2008 [4 أبريل 2008].
- ↑ باسكال كوك وجون ريغير (4 يوليو 2017). "السلوك غير المحدد في عام 2017، مدونة مضمنة في الأوساط الأكاديمية" .
- ↑ ISO / IEC (2003). ISO/IEC 14882:2003(E): لغات البرمجة – C++ §2.13.4 السلاسل النصية الحرفية [lex.string] الفقرة 2
- ↑ ISO / IEC (2003). ISO/IEC 14882:2003(E): لغات البرمجة – C++ §5.6 عوامل الضرب [expr.mul] الفقرة 4
- ↑ ISO / IEC (2003). ISO/IEC 14882:2003(E): لغات البرمجة - C++ §5.7 عوامل الجمع [expr.add] الفقرة 5
- ↑ ISO / IEC (2003). ISO/IEC 14882:2003(E): لغات البرمجة – C++ §5.9 عوامل المقارنة [expr.rel] الفقرة 2
- ↑ ISO / IEC (2007). ISO/IEC 9899:2007(E): لغات البرمجة – C §6.9 التعريفات الخارجية الفقرة 1
- ↑ ANSI X3.159-1989 لغة البرمجة C ، الحاشية 26
- ↑ "ترتيب التقييم - cppreference.com" . en.cppreference.com . تم الاطلاع عليه بتاريخ 9 أغسطس 2016 .
- ↑ "خيارات التحذير (باستخدام مجموعة مترجمات جنو (GCC))" . GCC، مجموعة مترجمات جنو - مشروع جنو - مؤسسة البرمجيات الحرة (FSF) . تم الاطلاع عليه بتاريخ 9 يوليو 2021 .
- ↑ "علامات التشخيص في Clang" . وثائق Clang 13. تم الاطلاع بتاريخ 2021-07-09 .
- ↑ ISO / IEC (1999). ISO/IEC 9899:1999(E): لغات البرمجة - لغة C §6.5 التعبيرات الفقرة 2
- ↑ "السلوك غير محدد" . مرجع Rust . تم الاطلاع عليه بتاريخ 28-11-2022 .
للمزيد من القراءة
- بيتر فان دير ليندن ، خبير برمجة C. رقم ISBN 0-13-177429-8
- فريق يو بي كناريز (أبريل 2015)، جون ريغير (جامعة يوتا، الولايات المتحدة الأمريكية)
- السلوك غير المحدد في عام 2017 (يوليو 2017) باسكال كوك (TrustInSoft، فرنسا) وجون ريغير (جامعة يوتا، الولايات المتحدة الأمريكية)
روابط خارجية
- نسخة مصححة من معيار C99 . انظر القسم 6.10.6 للاطلاع على #pragma
- تنفيذ لغة البرمجة
- لغة البرمجة سي
- لغة سي++
