اصطياد تجاوز unsigned int: تحقيق C++ من الصفر إلى الاحتراف

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

فخ للغافلين: لماذا unsigned int هي قنبلة موقوتة؟

\n\n

كل مطور C++ عاجلاً أم آجلاً يواجه موقفًا تتصرف فيه الأرقام مثل الأرانب الشريرة - تقفز بلا نهاية من الموجب إلى الصفر. الحديث يدور عن تجاوز unsigned int. على عكس نظيرها المُشار إليه (signed int)، فإن تجاوز unsigned في C++ مُحدد بدقة بالمعيار - فهو ليس سلوكًا غير محدد (UB). الرقم ببساطة يلتف حول modulo 2N. لكن هذا لا يجعل الخطأ أقل فتكًا.

\n\n

تخيل: أنت تحسب رصيد حساب، أو عدد العناصر في مصفوفة، أو وقت تشغيل مؤقت. تجاوز واحد عشوائي - وينهار منطق التطبيق. السؤال من العنوان "How do I detect unsigned integer overflow?" جمع آلاف المشاهدات على Stack Overflow ليس من فراغ. دعنا نحلل ثلاث طرق لاكتشاف التجاوز: من الساذج (لكنه يعمل) إلى الاحترافي.

\n\n

لماذا لا تعمل الفحوصات القياسية؟

\n\n

أول ما يخطر ببال المبتدئ:

\n\n
unsigned int a = UINT_MAX;\nunsigned int b = 1;\nif (a + b < a) // فحص ساذج\n    std::cout << "تجاوز!";
\n\n

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

\n\n

الطريقة رقم 1: الحساب قبل العملية (Pre-check)

\n\n

الطريقة الأكثر موثوقية هي التحقق من إمكانية التجاوز قبل تنفيذ العملية. لجمع رقمين غير مُشار إليهما، الشرط بسيط:

\n\n

إذا كان a + b قد يتجاوز، فإن a > (UINT_MAX - b).

\n\n
#include <climits>\n#include <iostream>\n\nbool will_add_overflow(unsigned int a, unsigned int b) {\n    return a > UINT_MAX - b;\n}\n\nint main() {\n    unsigned int x = 4000000000;\n    unsigned int y = 500000000;\n    \n    if (will_add_overflow(x, y)) {\n        std::cerr << "خطأ: تجاوز أثناء الجمع!\\n";\n    } else {\n        std::cout << "النتيجة: " << x + y << "\\n";\n    }\n    return 0;\n}
\n\n

الضرب - أكثر تعقيدًا

\n

بالنسبة للضرب، الفحص أكثر دقة: a > UINT_MAX / b (ولا تنسَ القسمة على صفر).

\n\n
bool will_mult_overflow(unsigned int a, unsigned int b) {\n    if (b == 0) return false; // الضرب في صفر - آمن\n    return a > UINT_MAX / b;\n}
\n\n

الإيجابيات: توافق عبر المنصات، تحكم كامل، لا تبعيات خفية.
\nالسلبيات: تحتاج لكتابة دوال منفصلة لكل عملية. يصبح الكود ضخمًا.

\n\n

الطريقة رقم 2: الفحص بعد العملية (Post-check)

\n\n

معيار C++ يضمن أنه بالنسبة للأنواع unsigned، نتيجة العملية هي (a + b) mod 2^N. لذلك يمكننا تنفيذ العملية ثم التحقق مما إذا كانت القيمة قد "التفت".

\n\n
#include <iostream>\n#include <limits>\n\ntemplate<typename T>\nbool add_overflow(T a, T b, T& result) {\n    result = a + b;\n    return result < a; // الحيلة الأساسية\n}\n\nint main() {\n    unsigned int res;\n    if (add_overflow(4000000000u, 500000000u, res)) {\n        std::cout << "تم تسجيل التجاوز!\\n";\n    } else {\n        std::cout << "موافق: " << res << "\\n";\n    }\n    return 0;\n}
\n\n

هام: هذه الحيلة تعمل فقط إذا كان a و b من نفس النوع غير المُشار إليه. بالنسبة لـ uint32_t - ممتاز. بالنسبة لمزيج من unsigned int و int - فشل.

\n\n

فخ الطرح

\n

بالنسبة للطرح، الشرط معاكس: result > a.

\n\n
template<typename T>\                

المدونات

توصيات الكتب