无符号整数的溢出是有明确定义的行为,因此在一些场合很有用 (例如,字符串 hash)。然而,如果它发生在预料之外的场合,可能产生难以察觉的 bug。
下列程序计算一个 vector<int>
中,除了最后一个元素之外,所有元素之和:
long long sum(const vector<int> &a)
{
long long ans = 0;
for (int i = 0; i < a.size() - 1; i++)
ans += a[i];
return ans;
}
在 a
为空时,a.size() - 1
的值会变为 size_t
的最大值。在比较时,i
被提升为 size_t
,导致循环终止条件永不成立,进而触发越界访问。
没有很好的检查方法。对于示例,代码会触发 -Wsign-compare
警告 (被 -Wall
包含):
t.cc:7:23: 警告:comparison of integer expressions of different signedness:
int
andstd::vector<int>::size_type
{akalong unsigned int
}[-Wsign-compare]
但是,如果错误地解释了该警告,选手可能会把代码改成:
long long sum(const vector<int> &a)
{
long long ans = 0;
for (size_t i = 0; i < a.size() - 1; i++)
ans += a[i];
return ans;
}
这不会触发警告,但是完全没有修复任何 bug。
-Wsign-compare
警告时,一定要正确解释警告可能反映的问题,而不是仅仅改代码使得警告不再出现。对于示例,可以增加一个强制类型转换:
long long sum(const vector<int> &a)
{
long long ans = 0;
for (int i = 0; i < (ssize_t) a.size() - 1; i++)
ans += a[i];
return ans;
}
也可以将代码重构,以避免对 a.size()
进行减法运算:
long long sum(const vector<int> &a)
{
long long ans = 0;
for (int i = 1; i < a.size(); i++)
ans += a[i - 1];
return ans;
}