GitHub上的Electronic Arts存储库最近吸引了我们的注意力。 它很小,在那里可用的23个项目中,只有几个C ++库似乎很有趣:EASTL,EAStdC,EABase,EAThread,EATest,EAMain和EAAssert。 项目本身也很小(每个约10个文件),因此仅在“最大”的20个文件项目中发现了错误:D但是我们确实找到了它们,而且它们看起来确实很有趣! 在撰写本文时,我们还对EA游戏和公司政策进行了热烈的讨论:D
引言
电子艺界(EA)是一家美国视频游戏公司。 它在GitHub上有一个小型
存储库 ,还有一些C ++项目,即C ++库:EASTL,EAStdC,EABase,EAThread,EATest,EAMain和EAAssert。 它们很小,
PVS-Studio分析仪仅在“最大”项目
EAStdC (20个文件)中设法找到了所有错误。 使用这样的大小,您
将无法可靠地判断整体代码质量,因此,请查看以下五个警告并
自行决定。
警告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;
spans [spanIndex] .mFormat数组包含
16个元素,因此最后一个有效元素的索引是
15 。 以其当前形式,如果
OVprintfCore函数具有尽可能高的索引(即
15) ,则
最终将
nFormatLength的索引
增加到 16 。 之后,在
switch语句中将发生数组越界错误。
此片段又被复制了两次:
- 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。 该代码看起来并不正确,其中肯定有一些错误。
此片段又被复制了两次:
- 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。
此片段又被复制了两次:
- 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(); }
现在的代码要短得多,这使条件的逻辑更容易掌握。
结论
您可能已经注意到,大多数警告都有另外两个重复项,所有这些重复项都在同一文件中找到。 代码重复是非常糟糕的反模式,因为它使程序维护变得非常复杂。 当错误蔓延到此类代码中时,程序的稳定性会急剧下降,因为它们会散布在整个代码中。
希望EA将上传其他一些有趣的项目,我们将再次访问它们的资源库:)。 同时,欢迎下载
PVS-Studio并在您自己的项目中尝试。