让我们继续讨论和文件有关的话题,再来看EOF。但这次我们要讨论的是完全不同的bug类型。它通常只出现在本地化软件版本里。
下面的代码片段是选自Computational Network Toolkit。错误被PVS-Studio诊断为:V739 EOF不应该和char类型比较。‘c’应该是int类型。
解释
让我们来看EOF是怎样声明的:
如你所见,EOF就是一个值为‘-1’的int类型。Fgetc()返回一个int类型的值。也就是说,它可以返回0到255的一个数,或者是-1(EOF)。
使用扩展字符集(Extended ASCII Codes)的用户在程序中某一个字母处理不当的时候可能会出错。
比如说,在Windows1251编码表中,俄文的最后一个字母是0xFF ,而在程序中就会被编译为文件终止符。
正确代码
建议
在这里并没有什么特殊的建议,但既然我们谈到了EOF,我想展示一些人没有注意到的比较有趣的错误。
只要记住,如果一个函数的返回值是int类型,就不要急于把它转化为char类型。停下来检查看有没有错。顺便提一句,我们在第二个技巧“大于0并不意味着是1”那里讨论memcmp()函数的时候用到了相似的代码。(关于MySQL漏洞那一块)。
下面的代码来自TortoiseGIT项目。该错误被PVS-Studio诊断为:V665 也许,在此处,‘#pragma warning(default: X)'用得不对。应该用'#pragma warning(push/pop)'
解释
程序员经常认为,早期让警告实现的"pragma warning(disable: X)"指令在使用"pragma warning(default : X)"后就能继续工作。但并非如此。'pragma warning(default : X)'这条指令是把‘X’警告设置为DEFAULT状态,这两个不是同一件事。
假设一个文件在编译时用了-wall,那么就会出现C4061警告。如果你增加了"#pragma warning(default : 4061)" 指令,那么这条警告就不会出现,因为它已经被默认关掉了。
正确代码
建议
返回警告的之前状态的正确方法是使用"#pragma warning(push[ ,n ])" 和"#pragma warning(pop)"这两条指令。可以看Visual C++11关于这些指令描述的文档:: Pragma Directives. Warnings.
库开发者应该要特别注意V665警告。忽视定制警告会在库使用者这边引起巨大的灾难。
关于这个话题的,值得一看的好文章:So, You Want to Suppress This Warning in Visual C++
下面的代码来自OpenSSL库。错误被PVS-Studio诊断为:V666 检查‘strncmp’的第三个参数。它可能和字符串的长度不一致,字符串长度取决于第二个参数。
解释
很难停止使用魔数。而且没有理由不使用类似0, 1, -1, 10这些常数。更难的是,给这些常数命名,而且,它们会使阅读代码变得更复杂。
然而,减少使用魔数的个数是有用的。比如说,避免使用用来定义字符串长度的魔数就很有用。
让我们来看一下上面给出的代码。代码看上去很像是复制粘贴的。程序员复制了下面这一行:
之后,用"BITLIST"代替"HEX" ,但是程序员忘了把3改为7. 结果就是,字符串不是和"BITLIST"做比较,而是仅仅比较了"BIT"。这个错误似乎不太严重,但终究是个错误。
用复制粘贴来写代码真的很糟糕。更严重的是,字符串长度由魔数来定义。一直以来,我们遇到这样的错误,就是字符串长度和确定的数字不一致的错误,都是因为拼写错误或者程序员的疏忽。所以,这是一个典型的错误,我们需要做点什么来避免它。让我们来看看应该如何避免。
正确代码
乍一看,只要把strncmp()换成strcmp()就好了。那样魔数就消失了。
不好——我们改变了代码运作的逻辑。strncmp()的作用是检查一个字符串是否是以‘HEX’开始的,而strcmp()是检查两个字符串是否相等的。它们做的是不同的检查。
要解决这个问题最简单的方法就是改变常数:
这个代码是正确的,但是魔数7还在那里,这就有点不好了。这也是为什么我要建议另一种方法。
建议
如果我们能够正确估计字符串的长度,这样的错误就能够避免。最简单的方法就是用strlen()函数。
在下面例子中,如果你忘记更改字符串,你能更快地发现它们的不匹配。
但是这个建议有两个缺点:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)