常见的由于语法不当引起的编译警告
1 引言
本文主要总结了笔者自己的开发中遇到的不规范的代码写法。这些代码风格可能并不会引起错误,但是会产生隐患或容易引起歧义。
这些语法缺陷可以通过合理配置编译器的编译选项检查出来,对于编译器的警告配置在前面有文章进行了简单介绍。
2 代码缺陷总结
2.1 格式化字符串与后续参数不匹配
函数原型如下
int printf(const char* format, ...);
int sprintf(char* str, const char* format, ...);
2.1.1 size_t
类型
由于 size_t
在不同平台上会有不同的类型定义,合适的做法是使用 z
对 size_t
进行格式化。
变量宽度标识符 z
需要编译器支持C++11或C99特性。
示例代码如下:
size_t n = sizeof(char);
printf("the size of char is %zd\n", n); // decimal
printf("the size of char is %zx\n", n); // hex
2.1.2 int64_t
及其相关类型
int64_t
是固定宽度为8字节的整形定义,在文件 <cstdint>
中有定义。
类似的还有 int8_t
, int16_t
, int32_t
分对应1,2,4字节的整形定义。
他们的格式化字符串使用 PRId8
, PRId16
, PRId32
, PRId64
表示,他们在文件 <cinttypes>
中有定义。
阅读源码后底层实现原理是通过识别系统中字宽,通过宏定义将满足不同条件的情况定义出来。
同样这些特性也是从C++11和C99开始才引入的。
示例代码如下:
#include <cstio>
#include <cinttypes>
std::printf("%zu\n", sizeof(std::int64_t));
std::printf("%s\n", PRId64);
std::printf("%+" PRId64 "\n", INT64_MIN);
std::printf("%+" PRId64 "\n", INT64_MAX);
std::int64_t n = 7;
std::printf("%+" PRId64 "\n", n);
2.1.3 %x
对应无符号整数
在使用 sscanf
函数对取16进制数据时需要注意传入的需要是无符号类型的地址。
示例代码如下:
const char* szNumber = "af"
unsigned int = nNum;
sscanf(szNumber, "%x", &nNum);
2.2 多个逻辑表达式之间在 &&
和 ||
之间的运算符优先级
先看以下示例代码:
if (EXPR1 || EXPR2 || EXPR3 && EXPR4)
{
// ....
}
分析:从语法规则上来说逻辑与( &&
)的运算符优先级要高于逻辑或( ||
)。
所以在进行运算时,会先计算逻辑与,再计算逻辑或。
从语法上分析是没有错误的,但是这样的表达式很容易造成读者4的误解。
改成以下的形式会好很多:
if (EXPR1 || EXPR2 || (EXPR3 && EXPR4) )
{
// ....
}
2.3 有符号整数和无符号整数之间的大小比较
有符号整数和无符号整数在进行大小比较时,有符号数会被转化为无符号数。
这样会引起一些不必要误解,所以应该避免这种用法。
示例代码如下:
int n1 = -1;
unsigned int n2 = 1;
bool bRet = n1 < n2; // bRet = false
注意,这里的运算结果与实际预期不符。
另外,使用无符号数作为循环的迭代条件时,需要注意进行迭代条件在比较时是否能够正常终止迭代。
示例代码如下:
for ( size_t i=100; i>0; --i )
{
// ...
}
注意,这里会出现死循环。
2.4 函数的重载和覆盖
在进行虚函数多态实现时,注意检查函数的一致性。
出现相同函数名称但是积累为非 const
版本,而派生类为 const
时,这时并不会导致多态,而会导致基类函数被隐藏。