c++ - سلوك غير محدد وغير محدد والتنفيذ المحدد




undefined-behavior c++-faq (6)

C ++ n3337 standard § 1.3.10 السلوك المحدد بالتطبيق

السلوك ، لبناء نظام جيد تشكيل والبيانات الصحيحة ، التي تعتمد على تنفيذ كل وثائق التنفيذ

في بعض الأحيان ، لا يفرض C ++ Standard سلوكًا معينًا على بعض التركيبات ولكنه يقول بدلاً من ذلك أنه يجب اختيار سلوك محدد جيد التحديد ووصفه بواسطة تطبيق معين (إصدار المكتبة). لذلك يمكن للمستخدم معرفة كيف سيتصرف البرنامج على الرغم من أن Standard لا يصف ذلك.

معيار C ++ n3337 § 1.3.24 سلوك غير محدد

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

عندما يصادف البرنامج بناء لم يتم تعريفه وفقًا لمعيار C ++ ، يُسمح له بالقيام بكل ما يريد القيام به (ربما إرسال بريد إلكتروني إليّ أو إرسال رسالة إلكترونية إليك أو ربما تجاهل الرمز تمامًا).

معيار C ++ n3337 § 1.3.25 سلوك غير محدد

السلوك ، لبناء نظام جيد التكوين والبيانات الصحيحة ، التي تعتمد على التنفيذ [ملاحظة: التنفيذ غير مطلوب لتوثيق السلوك الذي يحدث. عادة ما يتم تحديد نطاق السلوكيات المحتملة بواسطة هذه المواصفة القياسية الدولية. - ملاحظة النهاية

معيار C ++ لا يفرض سلوكًا معينًا على بعض التركيبات ولكنه يقول بدلاً من ذلك أنه يجب اختيار سلوك محدد جيد التحديد ( لا يمكن وصفه بوتًا ضروريًا ) بواسطة تطبيق معين (إصدار المكتبة). لذلك في الحالة التي لم يتم فيها تقديم وصف ، قد يكون من الصعب على المستخدم أن يعرف بالضبط كيف سيتصرف البرنامج.

ما الفرق بين السلوك غير المحدد وغير المحدد والتنفيذ المحدد في C و C ++؟


التنفيذ المحدد

يرغب التنفيذ ، يجب أن يكون موثقًا جيدًا ، والمعيار يعطي خيارات ولكن من المؤكد أن يتم تجميعها

غير محدد -

نفس ما هو محدد بالتنفيذ ولكن غير موثق

غير محدد-

قد يحدث أي شيء ، اعتني به.


ربما تكون الصياغة السهلة أسهل للفهم من التعريف الصارم للمعايير.

السلوك المحدد بالتنفيذ
تقول اللغة أن لدينا أنواع بيانات. يحدد بائعو البرامج المجمعة الأحجام التي يستخدمونها ، ويقدمون وثائق لما قاموا به.

سلوك غير معروف
أنت تفعل شيئًا خاطئًا. على سبيل المثال ، لديك قيمة كبيرة جدًا في int لا تتناسب مع char . كيف تضع هذه القيمة في char ؟ في الواقع لا توجد وسيلة! أي شيء يمكن أن يحدث ، ولكن الشيء الأكثر منطقية هو أخذ البايت الأول من ذلك int ووضعه في char . من الخطأ فعل ذلك لتعيين البايت الأول ، ولكن هذا ما يحدث تحت غطاء محرك السيارة.

سلوك غير محدد
ما هي وظيفة هذين التنفيذين أولاً؟

void fun(int n, int m);

int fun1()
{
  cout << "fun1";
  return 1;
}
int fun2()
{
  cout << "fun2";
  return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?

اللغة لا تحدد التقييم ، من اليسار إلى اليمين أو اليمين إلى اليسار! لذلك قد يؤدي سلوك غير محدد أو لا يؤدي إلى سلوك غير محدد ، ولكن بالتأكيد لا يجب أن ينتج برنامجك سلوكًا غير محدد.

eSKay أعتقد أن سؤالك يستحق تحرير الإجابة لتوضيح المزيد :)

fun(fun1(), fun2()); ليس السلوك "تعريف التنفيذ"؟ يجب على المترجم أن يختار واحدة أو الدورة الأخرى ، بعد كل شيء؟

والفرق بين التعريف المحدد بالتنفيذ وغير محدد ، هو أنه من المفترض أن يقوم المترجم باختيار سلوك في الحالة الأولى ولكنه لا يلزم في الحالة الثانية. على سبيل المثال ، يجب أن يحتوي أحد التطبيقات على تعريف واحد فقط لـ sizeof(int) . لذلك ، لا يمكن أن نقول أن sizeof(int) هو 4 لبعض جزء من البرنامج و 8 للآخرين. على عكس السلوك غير المحدد ، حيث يستطيع المترجم أن يقول حسناً ، سأقوم بتقييم هذه الوسيطات من اليسار إلى اليمين ويتم تقييم وسيطات الدالة التالية من اليمين إلى اليسار. يمكن أن يحدث في نفس البرنامج ، وهذا هو السبب في أنه يسمى غير محدد . في الواقع ، كان يمكن جعل C ++ أسهل إذا تم تحديد بعض السلوكيات غير المحددة. ألقِ نظرة هنا على إجابة الدكتور ستورستروب عن ذلك :

ويزعم أن الفرق بين ما يمكن إنتاجه والذي يمنح المترجم هذه الحرية ويحتاج إلى "تقييم عادي من اليسار إلى اليمين" يمكن أن يكون كبيراً. أنا غير مقتنع ، ولكن مع وجود عدد لا يحصى من المجمعين "هناك" للاستفادة من الحرية وبعض الناس الذين يدافعون بحماس عن تلك الحرية ، فإن التغيير سيكون صعباً وقد يستغرق عدة عقود للوصول إلى الزوايا البعيدة لعالمتي C و C ++. أشعر بخيبة أمل أنه ليس جميع المجمعين يحذرون من رمز مثل ++ i + i ++. وبالمثل ، فإن ترتيب تقييم الحجج غير محدد.

المنظمة الدولية للأرصاد الجوية (IMO) الكثير من "الأشياء" تبقى غير محددة ، وغير محددة ، ومحددة بالتنفيذ ، وما إلى ذلك. ومع ذلك ، من السهل القول وحتى تقديم أمثلة ، ولكن يصعب إصلاحها. وتجدر الإشارة أيضا إلى أنه ليس من الصعب تجنب معظم المشاكل وإنتاج الشفرات المحمولة.


من الناحية التاريخية ، كان كل من السلوك المعرّف بالتنفيذ والسلوك غير المعرّف يمثلان حالات توقع مؤلفو المعيار من خلالها أن الأشخاص الذين يكتبون تطبيقات ذات جودة سيستخدمون الحكم لتحديد ما هي الضمانات السلوكية ، إن وجدت ، التي قد تكون مفيدة للبرامج في مجال التطبيق المقصود الذي يعمل على الأهداف المقصودة. تختلف متطلبات شفرة الطحن ذات الرقم المتطور تمامًا عن متطلبات شفرة الأنظمة ذات المستوى المنخفض ، ويمنح كلاً من UB و IDB مرونة كتّاب المجمعين لتلبية هذه الاحتياجات المختلفة. لا تفرض أي من الفئتين أن تتصرف التطبيقات بطريقة مفيدة لأي غرض معين ، أو حتى لأي غرض كان. ومع ذلك ، يجب أن تتصرف تطبيقات الجودة التي تدعي أنها مناسبة لغرض معين بطريقة تتلاءم مع هذا الغرض ، سواء كان المعيار يتطلب ذلك أم لا .

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

ولسوء الحظ ، بدأ مؤلفو الترجمة المجمعة منذ منتصف التسعينات في تفسير الافتقار إلى الولايات السلوكية كحكم على أن الضمانات السلوكية لا تساوي التكلفة حتى في مجالات التطبيق حيث تكون حيوية ، وحتى على الأنظمة التي لا تكلف شيئا من الناحية العملية. بدلاً من التعامل مع UB كدعوة لممارسة حكم معقول ، بدأ كتاب المجمع يقومون بمعالجته كعذر لعدم القيام بذلك.

على سبيل المثال ، وبالنظر إلى التعليمات البرمجية التالية:

int scaled_velocity(int v, unsigned char pow)
{
  if (v > 250)
    v = 250;
  if (v < -250)
    v = -250;
  return v << pow;
}

لن يكون من الضروري أن ينفق التنفيذ المتمثل في أي جهد من أي نوع على الإطلاق للتعامل مع تعبير " v << pow " كتحويل ثنائي التكميلي دون النظر إلى ما إذا كانت " v إيجابية أم سلبية.

ومع ذلك ، فإن الفلسفة المفضلة بين بعض مؤلفي برامج التجميع اليوم تشير إلى أنه نظرًا لأن v يمكن أن يكون سالبًا فقط إذا كان البرنامج سيشارك في سلوك غير محدد ، فلا يوجد سبب لجعل البرنامج يقطع النطاق السلبي لـ v . على الرغم من أن التحول السلبي للقيم السلبية كان يتم دعمه على كل مترجم ذي أهمية ، وكمية كبيرة من الشفرات الحالية تعتمد على هذا السلوك ، فإن الفلسفة الحديثة قد تفسر حقيقة أن المعيار يقول أن القيم السلبية المتغيرة يسارًا هي UB مثل مما يعني أن كتاب المجمع يجب أن يشعروا بالحرية في تجاهل ذلك.


السلوك غير المحدد مقابل سلوك غير محدد له وصف قصير له.

ملخصهم النهائي:

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


السلوك غير المحدد هو أحد تلك الجوانب في لغة C و C ++ التي يمكن أن تكون مفاجئة للمبرمجين القادمين من لغات أخرى (تحاول لغات أخرى إخفاءها بشكل أفضل). بشكل أساسي ، من الممكن كتابة برامج C ++ التي لا تتصرف بطريقة يمكن توقعها ، على الرغم من أن العديد من compilers C ++ لن يُبلغ عن أية أخطاء في البرنامج!

دعونا ننظر إلى مثال كلاسيكي:

#include <iostream>

int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

يشير متغير p إلى السلسلة الحرفية "hello!\n" ، ويحاول التخصيصان أدناه تعديل هذه السلسلة الحرفية. ما الذي يفعله هذا البرنامج؟ طبقًا للقسم 2.14.5 من الفقرة 11 من معيار C ++ ، فإنه يستدعي سلوكًا غير محدد :

تأثير محاولة تعديل سلسلة حرفية غير معروف.

أستطيع أن أسمع الناس يصرخون "ولكن انتظر ، يمكنني تجميع هذه المشكلة ولا الحصول على الإخراج yellow " أو "ماذا تقصد غير محددة ، يتم تخزين حرفية سلسلة في الذاكرة للقراءة فقط ، لذلك نتائج المهمة الأولى في تفريغ الأساسية" . هذه هي بالضبط المشكلة مع سلوك غير معروف. في الأساس ، يسمح المعيار لأي شيء أن يحدث عندما تستدعي سلوكًا غير معروف (حتى الشياطين الأنفية). إذا كان هناك سلوك "صحيح" وفقًا للنموذج العقلي للغة ، فهذا النموذج خاطئ ببساطة ؛ معيار C ++ لديه التصويت الوحيد ، الفترة.

تتضمن الأمثلة الأخرى للسلوك غير المعروف الوصول إلى مصفوفة خارج حدودها ، إلغاء الإشارة إلى المؤشر الفارغ ، الوصول إلى الكائنات بعد انتهاء عمرها أو كتابة تعبيرات ذكية مزعومة مثل i++ + ++i .

يذكر القسم 1.9 من معيار C ++ أيضًا الأخوين الأقل خطورة للسلوك غير المحدد والسلوك غير المحدد والسلوك المحدد بالتنفيذ :

تعرف الأوصاف الدلالية في هذه المواصفة القياسية الدولية آلة تجريدية غير معدلة معلمات.

يتم وصف بعض جوانب وعمليات الجهاز المجرد في هذه المواصفة القياسية الدولية على أنها محددة بالتنفيذ (على سبيل المثال ، sizeof(int) ). هذه تشكل معالم آلة مجردة. يجب أن يتضمن كل تنفيذ وثائق تصف خصائصه وسلوكه في هذه النواحي.

تم وصف بعض الجوانب والعمليات الأخرى للجهاز التجريدي في هذه المواصفة القياسية الدولية على أنها غير محددة (على سبيل المثال ، ترتيب تقييم الحجج لوظيفة ما). تحدد هذه المواصفة القياسية الدولية ، حيثما أمكن ، مجموعة من السلوكيات المسموح بها. تحدد هذه الجوانب غير التدميرية للآلة التجريدية.

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

على وجه التحديد ، ينص القسم 1.3.24 على ما يلي:

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

ما الذي يمكنك فعله لتجنب الوقوع في سلوك غير معروف؟ في الأساس ، عليك قراءة كتب C ++ جيدة من قبل المؤلفين الذين يعرفون ما يتحدثون عنه. دروس الإنترنت المسمار. برغي bullschildt.







unspecified-behavior