C++ 排序 stl 向量失败(SIGTRAP)

C++ sorting stl vector fails (SIGTRAP)

我尝试了相对简单的任务——创建一个向量并对其进行排序。但不幸的是,当从堆栈中销毁向量时,它因 SIGTRAP 而失败。当我删除排序时,它可以,但我更需要它。

我已将问题缩小到尽可能小的源代码。当我 运行 它时,我得到 windows 错误 "Program stopped unexpectedly"。当我使用附加的 GDB 时,它会在完成第一个 while 循环后突出显示矢量析构函数。 (见下图)

vector<Vec> veci(n);resize(n) 我都试过了。我感觉这可能是由排序算法以某种方式对向量中不存在的项目进行排序引起的,但代码并未指出问题...你看到了吗?

Windows 10、mingw gcc

#include <stdio.h>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <ctime>
#include <stack>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
#include <sstream>

using namespace std;

struct Vec {
    int cena;
    int vaha;
    float pomer;

    bool operator < (const Vec& oth) const
    {
        return !(pomer < oth.pomer);
    }
};

int main(int argc, char** argv) {

    stringstream file;
    file << "9550 40 600 14 223 38 230 3 54 1 214 13 118 4 147 15 16 2 104 5 56 49 154 40 106 24 234 18 34 33 195 7 74 10 129 12 159 42 37 41 10 11 185 6 243 45 87 32 57 20 87 9 26 16 201 39 0 23 128 39 194 21 10 46 1 8 28 30 59 26 130 35 160 22 91 34 180 19 16 31 1 17 72";

    while (file.good()) {
        int id;
        int n;
        int M;
        file >> id;
        file >> n;
        file >> M;
        vector<Vec> veci;
        veci.resize(n);
        for (int i = 0; i < n; i++) {
            int c, v;
            file >> v;
            file >> c;
            veci[i].vaha = v;
            veci[i].cena = c;
            veci[i].pomer = c / (v+1);
        }

        sort(veci.begin(), veci.end());

    }

    printf("end");
    return 0;
}

您的问题源于使用 while (file.good()),这实际上总是一个错误(在这种情况下)。

这没有正确检测到文件结尾,最后将最后一项写入向量两次——但你只为它在那里写入一次腾出了空间,所以这写过了结尾分配的 space.

当排序尝试处理现在损坏的堆上的数据时,您开始看到问题。

修正比较简单:正确读取数据,如:

#include <stdio.h>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <ctime>
#include <stack>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>
#include <sstream>
#include <iterator>

using namespace std;

struct Vec {
    int cena;
    int vaha;
    float pomer;

    bool operator < (const Vec& oth) const {
        return !(pomer < oth.pomer);
    }

    friend std::istream &operator>>(std::istream &is, Vec &v) {
        return is >> v.vaha >> v.cena >> v.pomer;
    }

    friend std::ostream &operator<<(std::ostream &os, Vec const &v) {
        return os << "{" << v.vaha << ", " << v.cena << ", " << v.pomer << "}";
    }
};

int main(int argc, char** argv) {

    stringstream file;
    file << "9550 40 600 14 223 38 230 3 54 1 214 13 118 4 147 15 16 2 104 5 56 49 154 40 106 24 234 18 34 33 195 7 74 10 129 12 159 42 37 41 10 11 185 6 243 45 87 32 57 20 87 9 26 16 201 39 0 23 128 39 194 21 10 46 1 8 28 30 59 26 130 35 160 22 91 34 180 19 16 31 1 17 72";

    std::vector<Vec> veci { std::istream_iterator<Vec>(file), std::istream_iterator<Vec>{ } };

    sort(veci.begin(), veci.end());
    std::copy(veci.begin(), veci.end(), std::ostream_iterator<Vec>(std::cout, "\n"));
    return 0;
}

最后一点:您的比较功能与大多数人的预期相反。如果您在结构的 pomer 字段中以 NaN 结束,它也会(完全)失败(NaN 不遵循严格的弱排序,正如 std::sort 的比较运算符所要求的那样)。

好的,问题是 veci[i].pomer = c / (v+1); 分配给两个 int 的浮点变量除法,因此解决方案是使用 pomer = float(c) / (v+1).

不过,我认为原始代码应该只是排序不正确。我不明白为什么它会完全失败,尤其是在 vector 的 desctrutor 上...有人吗?

//编辑:我不知道为什么现在可以了。但正如@Jerry Coffin 回答的那样,问题可能出在流上。待会再反应。

导致 SIGTRAP 的直接问题是您的比较运算符:

bool operator < (const Vec& oth) const
{
    return !(pomer < oth.pomer);
}

它被实现为“! <”,即>=,所以returns true 用于相等的元素。 operator< 如果要被 std::sort 使用,则绝不能 return true 用于相等的元素。需求汇总here.

您可以改为 return power > oth.pomer

请注意,file.good() 在您的情况下工作正常,因为在解析最终的 72 值后没有空格,并且 int 的提取将被 eof 终止,影响流状态。尽管它非常脆弱,因为流状态在任何时间点都很好,所以您可以假设未来的流操作可以正常工作。在信任您尝试流入的变量之前,最好在某个时候测试流状态是否仍然良好,特别是如果您正在做类似 resize 的事情,然后是数组索引。

更健壮的例子I/O:

#define ASSERT(X, MSG) \
    do { \
        if (!(X)) { \
            std::cerr << "ASSERT fail @" << __LINE__ \
                << " !(" #X ") " << MSG << "\n"; \
            exit(EXIT_FAILURE); \
        } \
    } while (false)

while (file >> skipws && file.good())
{
    int id;
    int n;
    int M;
    ASSERT(file >> id >> n >> M, "unable to read 'id n m' fields");
    vector<Vec> veci;
    veci.resize(n);
    for (int i = 0; i < n; i++) {
        int c, v;
        ASSERT(file >> v >> c,
               "unable to read 'c v' fields for [" << i << ']');
        ASSERT(v + 1 != 0, "'v' value would trigger divide by zero");
        veci[i].vaha = v;
        veci[i].cena = c;
        veci[i].pomer = c / (v+1);
    }
}