لماذا لا تثير اللغات أخطاء عند تجاوز الأعداد الصحيحة بشكل افتراضي؟

StackOverflow https://stackoverflow.com/questions/103654

سؤال

في العديد من لغات البرمجة الحديثة (بما في ذلك C++، وJava، وC#)، تسمح اللغة بذلك تجاوز عدد صحيح أن يحدث في وقت التشغيل دون إثارة أي نوع من حالات الخطأ.

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

//Returns the sum of the values in the specified list.
private static int sumList(List<int> list)
{
    int sum = 0;
    foreach (int listItem in list)
    {
        sum += listItem;
    }
    return sum;
}

إذا تم استدعاء هذه الطريقة على النحو التالي:

List<int> list = new List<int>();
list.Add(2000000000);
list.Add(2000000000);
int sum = sumList(list);

سيحدث تجاوز في sumList() الطريقة (لأن int النوع في C# هو عدد صحيح موقّع 32 بت، ومجموع القيم في القائمة يتجاوز قيمة الحد الأقصى لعدد صحيح موقّع 32 بت).سيكون لمتغير المجموع قيمة -294967296 (وليس قيمة 4000000000)؛هذا على الأرجح ليس ما قصده المطور (الافتراضي) لطريقة sumList.

من الواضح أن هناك تقنيات مختلفة يمكن للمطورين استخدامها لتجنب احتمال تجاوز عدد صحيح، مثل استخدام نوع مثل Java BigInteger, ، أو ال checked الكلمة الرئيسية و /checked تبديل المترجم في C #.

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

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

هل هناك أسباب أخرى لقرار تصميم اللغة هذا أيضًا، بخلاف اعتبارات الأداء؟

هل كانت مفيدة؟

المحلول

في C#، كان الأمر يتعلق بالأداء.على وجه التحديد، قياس الأداء خارج الصندوق.

عندما كانت لغة C# جديدة، كانت Microsoft تأمل أن يتحول إليها الكثير من مطوري لغة C++.لقد عرفوا أن العديد من الأشخاص الذين يستخدمون لغة C++ يعتقدون أن لغة C++ سريعة، وخاصة أسرع من اللغات التي "تهدر" الوقت في إدارة الذاكرة التلقائية وما شابه ذلك.

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

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

أعتقد أنه من الواضح أن 99% من الوقت نريد/نتحقق منه.إنها تسوية مؤسفة.

نصائح أخرى

أعتقد أن الأداء هو سبب وجيه.إذا كنت تأخذ في الاعتبار كل تعليمات في برنامج نموذجي تزيد عددًا صحيحًا، وإذا بدلاً من العملية البسيطة لإضافة 1، كان عليه التحقق في كل مرة مما إذا كانت إضافة 1 ستتجاوز النوع، فإن التكلفة في الدورات الإضافية ستكون شديدة جدًا.

أنت تعمل على افتراض أن تجاوز العدد الصحيح هو دائمًا سلوك غير مرغوب فيه.

في بعض الأحيان يكون تجاوز عدد صحيح هو السلوك المرغوب فيه.أحد الأمثلة التي رأيتها هو تمثيل قيمة العنوان المطلقة كرقم نقطة ثابتة.بالنظر إلى int غير الموقع، فإن 0 هو 0 أو 360 درجة والحد الأقصى للعدد الصحيح غير الموقع 32 بت (0xffffffff) هو أكبر قيمة أقل بقليل من 360 درجة.

int main()
{
    uint32_t shipsHeadingInDegrees= 0;

    // Rotate by a bunch of degrees
    shipsHeadingInDegrees += 0x80000000; // 180 degrees
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees, overflows 
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees

    // Ships heading now will be 180 degrees
    cout << "Ships Heading Is" << (double(shipsHeadingInDegrees) / double(0xffffffff)) * 360.0 << std::endl;

}

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

لا يفرض C/C++ أبدًا سلوك الفخ.حتى التقسيم الواضح على 0 هو سلوك غير محدد في لغة C++، وليس نوعًا محددًا من المصائد.

لغة C ليس لديها أي مفهوم للملاءمة، إلا إذا قمت بحساب الإشارات.

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

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

حتى لو قامت لغة C++ بفحص الأعداد الصحيحة، فإن 99% من المبرمجين في الأيام الأولى كانوا سيتوقفون عن العمل إذا تم إيقاف تشغيلهم لتعزيز الأداء...

لأن التحقق من الفائض يستغرق وقتا.كل عملية رياضية بدائية، والتي تترجم عادة إلى تعليمة تجميع واحدة، يجب أن تتضمن فحصًا للتجاوز، مما يؤدي إلى تعليمات تجميع متعددة، مما قد يؤدي إلى برنامج أبطأ عدة مرات.

ومن المرجح أن يكون الأداء 99٪.على نظام التشغيل x86، سيتعين عليك التحقق من علامة التجاوز في كل عملية مما قد يمثل نجاحًا كبيرًا في الأداء.

ستغطي نسبة 1٪ الأخرى تلك الحالات التي يقوم فيها الأشخاص بتلاعبات صغيرة رائعة أو يكونون "غير دقيقين" في خلط العمليات الموقعة وغير الموقعة ويريدون دلالات الفائض.

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

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

(مرجع الحمض: http://en.wikipedia.org/wiki/ACID)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top