下面的代码选自Unreal Engine 4项目。这段代码被PVS-Studio诊断为不够高效,因为:V803 性能下降。在‘itr’是迭代器的例子中,使用前自增比用后自增高效。把iterator++换成++iterator。
解释
如果你没有看到这一节的标题,我想你应该很难注意到这段代码的问题。乍一看,这段代码没什么问题,但是还不够完美。是的,我在讨论后自增——“Iter++”。相对于后自增迭代器,你更应该用前自增的形式,比如用‘++Iter’代替‘ Iter++’。为什么我们要这么做呢,这么做的现实意义在哪?这里面是有玄机的。
高效代码
建议
前自增和后自增的区别大伙儿都知道。我希望大家也同样知道这两个结构内部的区别(这个区别表现了我们运算的规则)。如果你曾经重载过这个运算,你肯定有注意到。如果没有——我会做一个简单的解释。(了解的人可以直接跳过这一段,直接跳到代码后面的。)
前自增运算会改变对象的状态,然后给自己返回改变过的值。不需要临时对象。前自增运算大概是这样的:
后自增同样也改变对象的状态,但是它返回的是对象之前的状态。它能做到返回之前状态是用到了临时对象。后自增重载代码应该是这样的:
通过上面的代码,你可以看到有一个加法运算用到了临时变量。在实际应用中,这有多重要呢?
现在的编译器都很智能,能自己做优化,而且如果临时变量没有用的话,它们就不会去创建它。这就是为什么发布版本很难找出'it++' 和 '++it'的区别。
但是在调试的时候,又不一样了。在这个例子中,这两种表现的区别还是很重要的。
比如说,在这篇文章中,就用了几个例子测试代码在调试版本中用前自增和后自增所用的时间。我们可以看到用后自增时间差不多要长四倍。
有些人会说,“然后呢?他们在发布版本中是一样的!”,在他们这么说也对也不对。一般来说,在做单元测试的时候,我们会花比较多的时间在调试版本上去调试代码。虽然我们要花很长的时间在软件的调试版本上,但并不意味着我们愿意浪费时间呀。
总的来说,我觉得我们已经解决这个问题了。——我们应该用前自增代替后自增。是的,你应该。这样在调试的时候你花的时间就比较少了啊。而且,如果迭代器非常‘大量’,效果就更可观了。
参考文献(建议阅读):
Is it reasonable to use the prefix increment operator ++it instead of postfix operator it++ for iterators?
Pre vs. post increment operator - benchmark
下面的代码选自Energy Checker SDK.代码中包含的错误被PVS-Studio诊断为:V576 不正确的格式。建议检查'wprintf'函数的第二个参数。需要指向wchar_t字符串的指针标志。
解释
注意:第一个错误是用_T来标识一个一个宽字符的字符串格式。应该用前缀L才对。然而,这个错误不是太严重,也不是我们的兴趣所在。如果我们不用宽字符格式,代码只是不能编译而已,_T也不会扩展的。
如果你想用wprintf()来打印一个char*的字符串,在格式化字符串里你应该用"%S" 。
很多Linux程序员不知道陷阱在哪。事情是这样的,微软在wsprintf这类函数的实现上有些不同。如果我们在Visual C++中用wsprintf函数,我们就应该用“%s”来打印宽字符串,同时,在打印char*字符串是用“%S”。所以,这个例子有点诡异。跨平台的应用程序经常会落入这个陷阱里。
正确代码
我这里给出的代码只是一个修正这个问题的方案,不是最完美的。但我仍然希望展示我们修改此类错误的首要观点。
建议
在这里,我没有什么特别的建议。我只是告诉你一些在使用类似wprintf()的函数时要注意的。
从Visual Studio2015开始,就有针对“建议写可移植性代码”的方案。为兼容ISO C (C99),你应该指出预处理器,宏_CRT_STDIO_ISO_WIDE_SPECIFIERS。
这个例子中,下面的代码是正确的:
分析器知道_CRT_STDIO_ISO_WIDE_SPECIFIERS,而且在做分析的时候也把它考虑进去了。
顺便说一句,如果你打开了兼容ISO C模式(宏_CRT_STDIO_ISO_WIDE_SPECIFIERS已经声明),你可以用之间的版本,也就是用"%Ts"来标识格式。
总的来说,关于宽字符的标识非常错综复杂,已经不是短短的一篇文章能讲完的。为更深刻了解这个话题,我建议你阅读一些关于这个话题的文章:
Bug 1121290 - distinguish specifier s and ls in the printf family of functions
MBCS to Unicode conversion in swprintf
Visual Studio swprintf is making all my %s formatters want wchar_t * instead of char *
下面的代码来自游戏'Wolf'.代码中包含的错误被PVS-Studio诊断为:V511在'sizeof (src)'这个表达式中,sizeof()返回的是指针的大小,而不是数组的大小。
解释
有时候,程序员会忘记在C/C++中,你不可以把一个数组值传递给一个函数。因为传数组给一个函数,数组类型自动转换为指针类型。方括号里里的数字没什么意思,它们只是用来告诉程序员,多大的数组被传进去了。事实上,你可以传任意大小的数组。比如,下面的代码也能编译成功:
类似的,sizeof(src)不是计算数组的大小,而是指针的大小。然后结果就是,memcpy()只是复制了一部分数组而已。也就是,4或者8字节,这都取决于指针的大小(不算奇怪的结构)。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课