优质电子艺术图书馆

我们的注意力吸引到了GitHub上的Electronic Arts存储库。 它很小,在二十三个项目中,我们仅对几个C ++库感兴趣:EASTL,EAStdC,EABase,EAThread,EATest,EAMain和EAAssert。 项目也很小(大约10个文件),因此我们仅在“最大”的20个文件中发现了错误:D但是我们也发现了有趣的错误! 在撰写本文时,我和我的同事们还积极讨论了EA的游戏及其策略:D

图片1


引言


电子艺界(EA)是一家发行视频游戏的美国公司。 在GitHub网站上,她有一个小型存储库和几个C ++项目。 这些是C ++库:EASTL,EAStdC,EABase,EAThread,EATest,EAMain和EAAssert。 它们很小,我们仅在其中最大的EAStdC (20个文件)中使用PVS-Studio分析仪发现了有趣的错误。 有了这么多的卷, 很难谈论整个代码的质量。 只需对5条警告进行评分,然后自行决定。

警告1


V524 “ >>”功能的主体完全等同于“ <<”功能的主体,这很奇怪。 第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; // <= else return -1; switch(*p) { case 'b': case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': case 'g': case 'G': case 'e': case 'E': case 'f': case 'F': case 'a': case 'A': case 'p': case 'c': case 'C': case 's': case 'S': case 'n': { // Finalize the current span. spans[spanIndex].mpEnd = p + 1; spans[spanIndex].mFormat[nFormatLength] = 0; // <= spans[spanIndex].mFormatChar = *p; if(++spanIndex == kSpanCapacity) break; .... } 

spans [spanIndex] .mFormat数组16个元素组成,即 最后一个有效项目的索引为15 。 现在,将编写OVprintfCore函数代码 ,以便如果nFormatLength索引具有最大可能的索引-15 ,则增量最多为16 。 此外,在switch语句中,可能会超出数组的边界。

此代码段已复制到另外2个位置:

  • V557阵列可能超限。 “ nFormatLength”索引的值可以达到16。EASprintfOrdered.cpp 614
  • V557阵列可能超限。 索引“ nFormatLength”的值可以达到16。EASprintfOrdered.cpp 977

警告3


V560条件表达式的一部分始终为true :(结果> = 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; } .... } 

条件result> = 0始终为true,因为result变量在循环中的任何地方都不会更改。 该代码看起来非常可疑,很可能该代码中有错误。

此代码段已复制到另外2个位置:

  • V560条件表达式的一部分始终为true :(结果> = 0)。 EASprintfOrdered.cpp 852
  • V560条件表达式的一部分始终为true :(结果> = 0)。 EASprintfOrdered.cpp 1215

警告4


V1009检查阵列初始化。 仅第一个元素被显式初始化。 其余元素用零初始化。 EASprintfOrdered.cpp 151

 static int OVprintfCore(....) { .... int spanArgOrder[kArgCapacity] = { -1 }; .... } 

这可能不是一个错误,但是应该警告开发人员,只有spanArgOrder数组的第一个元素初始化为-1 ,而其他所有元素都将具有0值。

此代码段已复制到另外2个位置:

  • V1009检查阵列初始化。 仅第一个元素被显式初始化。 其余元素用零初始化。 EASprintfOrdered.cpp 518
  • V1009检查阵列初始化。 仅第一个元素被显式初始化。 其余元素用零初始化。 EASprintfOrdered.cpp 881

警告5


V728过度检查可以简化。 '((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。 电子艺术几乎完美的图书馆

Source: https://habr.com/ru/post/zh-CN461679/


All Articles