std::sort 导致 operator< 中的分段错误
std::sort causes segmentation fault in operator<
我正在尝试对 3D 整数向量 (IntVec
) 的列表 (std::vector
) 进行排序。不知何故,std::sort
在 IntVec
的 operator<
中导致了分段错误。这是我的代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <fstream>
struct IntVec
{
public:
long x;
long y;
long z; // Using ints does not cause the Segmentation Fault ?!
friend bool operator<(const IntVec &lhs, const IntVec &rhs)
{
return (lhs.z < rhs.z) || // Segmentation Fault happens here
((lhs.z == rhs.z) && (lhs.y < rhs.y))
|| ((lhs.y == rhs.y) && (lhs.x < rhs.x));
}
};
int main(void)
{
std::vector<IntVec> vec;
const int N = 2178;
std::ifstream s("res.txt");
for (int i = 0; i < N; i++)
{
IntVec t;
s >> t.x;
s >> t.y;
s >> t.z;
vec.push_back(t);
}
// Using vec.begin() and vec.end() does not change anything
std::sort(vec.data(), vec.data() + vec.size());
}
我可以给你数据集;但是,我首先想看看我的代码中是否有一些重大的概念错误或一些我没有看到的错误。我发现问题特定于该数据集。如果我遗漏一个条目,则不会发生段错误。我觉得这很奇怪,因为这样的错误应该更明显并且与内存管理有关。另请注意,对 x
、y
和 z
使用整数不会导致任何问题。
Here 是代码的 godbolt 版本。
Here is a related SO question。 但是,我认为我的代码没有导致此错误的相同缺陷。我认为我的排序关系是 "strictly <".
你的算子逻辑有问题(不满足严格的弱排序要求)。最后一个子句也需要 lhs.z == rhs.z
。否则,lhs.z
可以是 > rhs.z
,但您仍然会得到一个肯定的结果,从而导致排序不一致。
标准库算法让您有责任做到这一点,打破由此产生的假设很容易导致混乱(阅读:未定义的行为),例如分段错误。
想象一下实现中的评论,说的是 "at this point, we know that a
is less than b
, so we do not need to perform any range/bounds check on b
"。当 a
意外地 比 b
大 时,缺少边界检查可能会导致错误的内存访问。然而,结果可能要微妙得多,并会导致奇怪的错误,因此正确处理很重要。
您可能希望考虑使用更短、更不易出错的方法来实现此排序:
return std::tie(lhs.z, lhs.y, lhs.x) < std::tie(rhs.z, rhs.y, rhs.x);
在元组上使用 operator<
(这就是 std::tie
给你的)automatically (and correctly!) performs the lexicographic breakdown for you.
the cppreference page for std::tie
上实际上有一个很好的例子,表明这是一件很常见的事情。
我正在尝试对 3D 整数向量 (IntVec
) 的列表 (std::vector
) 进行排序。不知何故,std::sort
在 IntVec
的 operator<
中导致了分段错误。这是我的代码:
#include <iostream>
#include <algorithm>
#include <vector>
#include <fstream>
struct IntVec
{
public:
long x;
long y;
long z; // Using ints does not cause the Segmentation Fault ?!
friend bool operator<(const IntVec &lhs, const IntVec &rhs)
{
return (lhs.z < rhs.z) || // Segmentation Fault happens here
((lhs.z == rhs.z) && (lhs.y < rhs.y))
|| ((lhs.y == rhs.y) && (lhs.x < rhs.x));
}
};
int main(void)
{
std::vector<IntVec> vec;
const int N = 2178;
std::ifstream s("res.txt");
for (int i = 0; i < N; i++)
{
IntVec t;
s >> t.x;
s >> t.y;
s >> t.z;
vec.push_back(t);
}
// Using vec.begin() and vec.end() does not change anything
std::sort(vec.data(), vec.data() + vec.size());
}
我可以给你数据集;但是,我首先想看看我的代码中是否有一些重大的概念错误或一些我没有看到的错误。我发现问题特定于该数据集。如果我遗漏一个条目,则不会发生段错误。我觉得这很奇怪,因为这样的错误应该更明显并且与内存管理有关。另请注意,对 x
、y
和 z
使用整数不会导致任何问题。
Here 是代码的 godbolt 版本。
Here is a related SO question。 但是,我认为我的代码没有导致此错误的相同缺陷。我认为我的排序关系是 "strictly <".
你的算子逻辑有问题(不满足严格的弱排序要求)。最后一个子句也需要 lhs.z == rhs.z
。否则,lhs.z
可以是 > rhs.z
,但您仍然会得到一个肯定的结果,从而导致排序不一致。
标准库算法让您有责任做到这一点,打破由此产生的假设很容易导致混乱(阅读:未定义的行为),例如分段错误。
想象一下实现中的评论,说的是 "at this point, we know that a
is less than b
, so we do not need to perform any range/bounds check on b
"。当 a
意外地 比 b
大 时,缺少边界检查可能会导致错误的内存访问。然而,结果可能要微妙得多,并会导致奇怪的错误,因此正确处理很重要。
您可能希望考虑使用更短、更不易出错的方法来实现此排序:
return std::tie(lhs.z, lhs.y, lhs.x) < std::tie(rhs.z, rhs.y, rhs.x);
在元组上使用 operator<
(这就是 std::tie
给你的)automatically (and correctly!) performs the lexicographic breakdown for you.
the cppreference page for std::tie
上实际上有一个很好的例子,表明这是一件很常见的事情。