تم لفت انتباهنا إلى مستودع الفنون الإلكترونية على جيثب. إنه صغير جدًا ومن بين 23 مشروعًا ، كنا مهتمين فقط بعدد قليل من مكتبات C ++: EASTL و EAStdC و EABase و EAThread و EATest و EAMain و EAAssert. كانت المشروعات أيضًا صغيرة جدًا (حوالي 10 ملفات) ، لذلك وجدنا أخطاء فقط في "أكبر" من 20 ملفًا: D لكن وجدناها أيضًا مثيرة للاهتمام! أثناء كتابة المقال ، ناقشنا أنا وزملائي أيضًا ألعاب EA واستراتيجيتها: D
مقدمة
Electronic Arts (EA) هي شركة أمريكية تقوم بتوزيع ألعاب الفيديو. على موقع GitHub ، لديها
مستودع صغير والعديد من مشاريع C ++. هذه مكتبات C ++: EASTL و EAStdC و EABase و EAThread و EATest و EAMain و EAAssert. إنها صغيرة جدًا وقد وجدنا أخطاء مثيرة للاهتمام باستخدام محلل
PVS-Studio في "الأكبر" منها فقط -
EAStdC (20 ملفًا). مع مثل هذه المجلدات ،
من الصعب التحدث عن جودة الكود ككل. فقط معدل 5 تحذيرات وتقرر لنفسك.
تحذير 1
V524 من الغريب أن يكون جسم الدالة ">>" مكافئًا تمامًا لجسم وظيفة "<<". EAFixedPoint.h 287
template <class T, int upShiftInt, int downShiftInt, int upMulInt, int downDivInt> struct FPTemplate { .... FPTemplate operator<<(int numBits) const { return value << numBits; } FPTemplate operator>>(int numBits) const { return value << numBits; } FPTemplate& operator<<=(int numBits) { value <<= numBits; return *this;} FPTemplate& operator>>=(int numBits) { value >>= numBits; return *this;} .... }
عند التحميل الزائد لمشغلي النوبات ، قام المبرمج بعمل خطأ مطبعي في أحدهم ، مما أربك المشغلين << و >>. على الأرجح ، هذه هي نتيجة نسخ ولصق البرمجة.
تحذير 2
V557 تجاوز الصفيف هو ممكن. يمكن أن تصل قيمة مؤشر 'nFormatLength' إلى 16. EASprintfOrdered.cpp 246
static const int kSpanFormatCapacity = 16; struct Span8 { .... char mFormat[kSpanFormatCapacity]; .... }; static int OVprintfCore(....) { .... EA_ASSERT(nFormatLength < kSpanFormatCapacity); if(nFormatLength < kSpanFormatCapacity) spans[spanIndex].mFormat[nFormatLength++] = *p;
تتكون
الصفوف الممتدة [spanIndex] .mFormat من
16 عنصرًا ، أي يحتوي العنصر الأخير صالح على فهرس
15 . الآن
يتم كتابة
رمز الدالة
OVprintfCore بحيث إذا كان مؤشر
nFormatLength يحتوي على أقصى مؤشر ممكن -
15 ،
فستحدث زيادة تصل إلى
16 . كذلك في
بيان التبديل ، من الممكن تجاوز حدود المصفوفة.
تم نسخ جزء الشفرة هذا إلى مكانين آخرين:
- V557 تجاوز الصفيف هو ممكن. يمكن أن تصل قيمة مؤشر 'nFormatLength' إلى 16. EASprintfOrdered.cpp 614
- V557 تجاوز الصفيف هو ممكن. يمكن أن تصل قيمة مؤشر "nFormatLength" إلى 16. EASprintfOrdered.cpp 977
تحذير 3
V560 جزء من التعبير الشرطي صحيح دائمًا: (النتيجة> = 0). EASprintfOrdered.cpp 489
static int OVprintfCore(....) { .... for(result = 1; (result >= 0) && (p < pEnd); ++p) { if(pWriteFunction8(p, 1, pWriteFunctionContext8, kWFSIntermediate) < 0) return -1; nWriteCountSum += result; } .... }
نتيجة الشرط
> = 0 صحيحة دائمًا ، لأن متغير
النتيجة لا يتغير في أي مكان في الحلقة. يبدو الرمز مشبوهًا للغاية وعلى الأرجح يوجد خطأ في هذا الرمز.
تم نسخ جزء الشفرة هذا إلى مكانين آخرين:
- V560 جزء من التعبير الشرطي صحيح دائمًا: (النتيجة> = 0). EASprintfOrdered.cpp 852
- V560 جزء من التعبير الشرطي صحيح دائمًا: (النتيجة> = 0). EASprintfOrdered.cpp 1215
تحذير 4
V1009 تحقق من تهيئة الصفيف. تتم تهيئة العنصر الأول فقط بشكل صريح. تتم تهيئة العناصر الباقية مع الأصفار. EASprintfOrdered.cpp 151
static int OVprintfCore(....) { .... int spanArgOrder[kArgCapacity] = { -1 }; .... }
قد لا يكون هذا خطأ ، ولكن يجب تحذير المطورين من أن العنصر الأول فقط من صفيف
spanArgOrder يتم تهيئته بقيمة
-1 ، وسيكون لكل الآخرين قيمة 0.
تم نسخ جزء الشفرة هذا إلى مكانين آخرين:
- V1009 تحقق من تهيئة الصفيف. تتم تهيئة العنصر الأول فقط بشكل صريح. تتم تهيئة العناصر الباقية مع الأصفار. EASprintfOrdered.cpp 518
- V1009 تحقق من تهيئة الصفيف. تتم تهيئة العنصر الأول فقط بشكل صريح. تتم تهيئة العناصر الباقية مع الأصفار. EASprintfOrdered.cpp 881
تحذير 5
V728 يمكن تبسيط عملية التحقق المفرطة. The '(A &&! B) || تعبير (! A && B) 'يعادل التعبير' bool (A)! = Bool (B) '. int128.h 1242
inline void int128_t::Modulus(....) const { .... bool bDividendNegative = false; bool bDivisorNegative = false; .... if( (bDividendNegative && !bDivisorNegative) || (!bDividendNegative && bDivisorNegative)) { quotient.Negate(); } .... }
تمت تهيئة جزء الشفرة هذا للراحة ، لكن في الأصل يعد شرطًا طويلًا للغاية يصعب قراءته. شيء آخر هو إذا قمنا بتبسيط التعبير الشرطي ، كما ينصح المحلل:
if( bDividendNegative != bDivisorNegative) { quotient.Negate(); }
لقد أصبح الكود أقصر كثيرًا ، الأمر الذي سهل إلى حد كبير فهم منطق التعبير الشرطي.
استنتاج
كما لاحظت ، يتم تكرار معظم التحذيرات المشبوهة في ثلاثة أماكن ، وداخل نفس الملف. رمز الازدواجية هو ممارسة التنمية سيئة للغاية ، كما يعقد دعم رمز. وإذا حدث خطأ في هذا الكود ، فإن استقرار البرنامج يتناقص بشكل حاد بسبب انتشار الأخطاء في جميع أنحاء الكود.
دعونا نأمل أن يتم نشر شيء آخر مثير للاهتمام وسنعود إلى هذا المستودع مرة أخرى :). في غضون ذلك ، أقترح أولئك الذين يرغبون في تنزيل
PVS-Studio ومحاولة التحقق من مشاريعهم.

إذا كنت ترغب في مشاركة هذه المقالة مع جمهور يتحدث الإنجليزية ، فالرجاء استخدام الرابط الخاص بالترجمة: Svyatoslav Razmyslov.
الكمال تقريبا المكتبات عن طريق الفنون الإلكترونية .