启用 g++ 优化会导致分段错误

Turning on g++ optimization leads to segmentation fault

本文关键字:分段 错误 g++ 优化 启用      更新时间:2023-10-16

我的程序在编译时使用以下内容时出现分段错误:

g++ -std=c++11 iForest.cpp -o iForest -O2

我已经阅读了这个线程 - 打开 g++ 优化会导致段错误 - 我不明白,但我认为我在那里没有犯同样的问题。我还检查了我的代码。我真的看不出哪里可能存在问题。请提供一些帮助。这是我的代码:

    #include <bits/stdc++.h>
using namespace std;
class iTree{
public:
    iTree(): root(NULL) {}
    iTree(const vector<double>& _items): items(_items){}
    iTree(const string &fname){ readData(fname); }
    ~iTree(){delete root;}
    void print(){
        for(int i = 0; i < np; ++i){
            for(int j = 0; j < nd; ++j){
                cout << items[i*nd + j] << " ";
            }
            cout << endl;
        }
    }
private:
    int height, np, nd; //np: # of points, nd: # of dimensions of a point
    vector<double> items;   // items.size() = np*nd;
    struct Node{
        double val;
        Node *left, *right;
        int attri;  // index of the attribute we pick
        Node(): val(0.0), left(NULL), right(NULL), attri(-1) {}
        ~Node(){ delete left; delete right;}
    } *root;
    void readData(const string &fname){
        ifstream ifs(fname);
        ifs >> np >> nd;
        items.resize(np*nd, 0.0);
        for(int i = 0; i < np*nd; ++i)  ifs >> items[i];
        ifs.close();
    }
};
int main(){
    iTree forest("data.dat");
    forest.print();
    return 0;
}

当我使用 g++ -std=c++11 iForest.cpp -o iForest 编译时没有生成段错误。如果我不打印,也不会产生段错误。但是我认为我的 print() 函数中没有任何错误。

如果您想

运行它,这是我使用的"数据.dat":

10 5
509304 9 0 2 1.0
509305 9 0 2 0.0
509306 9 0 2 0.0
509307 9 0 2 0.0
509308 9 0 2 0.0
509309 9 0 2 0.0
509310 9 0 2 0.0
509311 9 0 2 0.0
509312 9 0 2 0.0
509313 9 0 2 0.0

当您通过构造函数iTree(const string &fname){ readData(fname); }输入时,您的类包含未初始化的变量root(您在示例中这样做)。然后你的析构函数会delete root;

要解决此问题,您可以初始化root,最简单的方法是将{}放在其声明中的;之前。

为了避免以后出现错误,最好将NodeiTree都声明为不可复制和不可移动,直到您准备好为它们实现复制操作。 默认的复制/移动行为将导致内存错误,因为违反规则三次。

这是更正后的程序,减少到一个文件(数据现在是一个嵌入的字符串,以使演示更容易)。

我嵌入了解释性注释。请注意关注点的分离,unique_ptr的使用,因此不需要析构函数。这反过来给了我自由移动的语义。

// note 8: include the proper headers
#include <vector>
#include <sstream>
#include <iostream>
#include <memory>
class iTree{
public:
    iTree(): root(nullptr) {}
    iTree(const std::vector<double>& _items): items(_items){}
    // note 5: separate concerns - a tree needs to know nothing about files - only about istreams.
    iTree(std::istream & is){ readData(is); }
    // note 3: destructor now un-necessary
    void print(){
        for(int i = 0; i < np; ++i){
            for(int j = 0; j < nd; ++j){
                std::cout << items[i*nd + j] << " ";
            }
            std::cout << std::endl;
        }
    }
private:
    int height, np, nd; //np: # of points, nd: # of dimensions of a point
    std::vector<double> items;   // items.size() = np*nd;
    struct Node{
        double val;
        // note 1: unique_ptr instead of raw pointer
        std::unique_ptr<Node> left, right;
        int attri;  // index of the attribute we pick
        Node(): val(0.0), left(nullptr), right(nullptr), attri(-1) {}
        // note 4: destructor now un-necessary
    };
    // note 2: unique_ptr instead of raw pointer
    std::unique_ptr<Node> root;
    void readData(std::istream &is){
        is >> np >> nd;
        items.resize(np*nd, 0.0);
        for(int i = 0; i < np*nd; ++i)  is >> items[i];
    }
};
int main(){
    static constexpr auto data =
R"data(10 5
509304 9 0 2 1.0
509305 9 0 2 0.0
509306 9 0 2 0.0
509307 9 0 2 0.0
509308 9 0 2 0.0
509309 9 0 2 0.0
509310 9 0 2 0.0
509311 9 0 2 0.0
509312 9 0 2 0.0
509313 9 0 2 0.0
)data";
    // note 6: now I can express the entire example in one file - no need for a data file.
    std::istringstream is(data);
    // note 7: because I didnt define destuctors I now have move semantics for free
    //         which will be useful for performance and give me more expressive code.
    auto forest = iTree(is);
    forest.print();
    return 0;
}

预期输出:

509304 9 0 2 1
509305 9 0 2 0
509306 9 0 2 0
509307 9 0 2 0
509308 9 0 2 0
509309 9 0 2 0
509310 9 0 2 0
509311 9 0 2 0
509312 9 0 2 0
509313 9 0 2 0

正如DeiDei在他的评论中所写,你删除了一些你没有分配的指针。我会更进一步说,你应该使用智能指针和 RAII 习语来消除这些问题。C-points通常会导致堆积如山的问题。此外,请使用nullptr而不是NULL宏。

编辑:要添加精度,请使用不可复制且只能移动的std::unique_ptr,正如M.M在他的帖子中指出的那样。