在使用 STL 中的算法 (如 sort
、lower_bound
)、容器 (如 set
或 map
) 时,如果比较函数不是偏序关系,则行为是未定义的,可能导致程序崩溃或输出错误结果。
危险性:高
查错难度:高
外部链接:CWE-475: Undefined Behavior for Input to API
在使用 STL 中的算法 (如 sort
、lower_bound
)、容器 (如 set
或 map
) 时,如果比较函数是偏序关系,但偏序关系违背选手的预期,则可能产生错误结果。
危险性:高
查错难度:高
外部链接:CWE-1025: Comparison Using Wrong Factors
struct Point
{
int x, y;
bool operator<(const Point &rhs) const
{
return x < rhs.x && y < rhs.y;
}
};
set<Point> p;
刚入门的选手可能试图用上面的构造解决二维偏序问题,然而这是不可能的。
sort(v.begin(), v.end(), [](auto a, auto b) {return a.second <= b.second;});
选手试图实现按第二个元素为关键字排序若干 pair<int, int>
,但是把 <
误写成 <=
。这违背了偏序关系的性质,可能导致程序崩溃或输出错误结果。
int a[N];
struct cmp_by_a
{
bool operator()(int x, int y) const
{
return a[x] < a[y] || (a[x] == a[y] && x < y);
}
};
int main()
{
set<int, cmp_by_a> some_set;
a[0] = 114;
a[1] = 514;
some_set.insert(0);
some_set.insert(1);
a[0] = 1919;
a[1] = 810;
// ...
}
在本例中,修改 a
的值导致偏序性质被破坏,对 some_set
的后续操作是未定义行为,可能导致程序崩溃或输出错误结果。
int main()
{
set<double> s;
s.insert(1.0);
s.insert(2.0);
s.erase(0.0 / 0.0 - 0.0 / 0.0);
cout << s.size() << '\n';
}
该程序的输出可能令你震惊。
set<char *> s;
这样做“字符串集合”显然是不行的……
没有很好的检查方法,只能人工审查比较函数。
对于例 1,唯一可行的修复是使用正确的高级数据结构写二维偏序。
对于例 2,将 <=
改为 <
即可。
对于例 3,如果必须进行此类修改,应该在修改前将受到影响的元素移出 set
,修改后再重新插入。例如,可以将修改操作封装为:
void modify_a(int x, int v)
{
s.erase(x);
a[x] = v;
s.insert(x);
}
对于例 4,建议避免使用 set<double>
(或其他浮点类型的 set
),因为浮点数本身就是不精确的,即使排除了 NaN
,在比较时仍然可能产生奇怪的问题。
对于例 5,最简单的修复是改用 set<string>
。