【问题标题】:Segmentation fault pushing strings in a stack分段错误将字符串推入堆栈
【发布时间】:2015-10-14 04:50:22
【问题描述】:

我仍然遇到一个问题,该问题在与模板和基于链接列表的堆栈一起使用的类分配中不断出现。我经常遇到分段错误,我一生都无法理解为什么。

main() 请求一个值来确定玩游戏的磁盘数量,它调用set_Discs(),将“x”附加到字符串对象tmp_str 以表示游戏的磁盘。然后将该字符串对象推入堆栈rod[0](第一根杆),然后循环返回。我每次通过循环调用cout 来打印顶部堆栈对象作为测试并且它可以工作,因此我可以成功存储字符串对象。然而,在调用下一个函数 print_Game() 时,它会执行 for 循环的第一次迭代(打印第一行)并再次崩溃。

int main() {
TowerHanoi game;
size_t disc_in;

    //How many discs to be used
cout << "Welcome to Towers of Hanoi!"<< "Please enter how many discs you want to play with: " << endl;
cin >> disc_in;

game.set_Discs(disc_in);
game.print_Game();
//Ask for source and target rod

return EXIT_SUCCESS;
}

set_Discs() 函数:

    void TowerHanoi::set_Discs(size_t disc) {
    //cout << "test: " << rod[0].top() << endl;
    discs = disc;

    while (disc > 0) {
        string tmp_str;
        for (size_t i=0; i<disc; i++) {
            tmp_str.append("x");

        }
        disc--;
        rod[0].push(tmp_str);
        cout << "test: " << rod[0].top() << endl;
    }


}

函数print_Game() 是最近的一个合乎逻辑的尝试,如果它不好并且工作不足,请原谅我。循环添加间距,然后打印top 字符串对象,然后调用pop()(在LinkedList.template 中调用removeFromHead())以使下一个字符串对象成为下一次循环的顶部对象,依此类推.参考addToHead()

    void TowerHanoi::print_Game() {

    size_t spaces = discs;
    string topLen = rod[0].top();
    string xString;

    cout << "top length: " << topLen.length() << endl;
    for (size_t a=0; a<discs;a++) {
        LStack<string> rodCopy = rod[0];
            cout << "==="; //3 spaces
                for (size_t i=0; i<spaces-topLen.length();i++) { //add to xString
                    xString.append("=");
                }

                cout << "===" << xString << rod[0].top() << xString << endl;
                rod[0].pop(); 
                spaces--;
    }



}

来自 LStack.template 的一些相关代码(这是 Stack):

    //push
template <typename Obj>
void LStack<Obj>::push(Obj& head_in) {

    list->addToHead(head_in);
    used++;
}

//pop
template <typename Obj>
Obj LStack<Obj>::pop() {
    used--;
    return list->removeFromHead();

}
//top
template <typename Obj>
const Obj& LStack<Obj>::top() {
    return list->list_getHead();
}

LinkedList.template中的相关代码:

template <typename Item>
void LinkedList<Item>::addToHead(Item& entry) {
    node<Item>* temp = head;
    head = new node<Item>();
    head->set_data(entry);
    head->set_link(temp);

}

    template <typename Item>
const Item& LinkedList<Item>::removeFromHead() {
    node<Item>* head_copy = head; //create a copy of head
    head->set_data(head_copy->link()); //set head's data to the previous object
    return head_copy->data(); //return head's original data
}

节点模板:

    template <typename Object>
void node<Object>::set_data(const Object& new_data){
        word = new_data;
}


template <typename Object>
void node<Object>::set_link(node<Object>* new_link){
        next= new_link; 

}


template <typename Object>
void node<Object>::set_previous(node<Object>* new_prev) {
        previous = new_prev; 
}


template <typename Object>
const Object& node<Object>::data() const{  //return the word
        return word;
}


template <typename Object>
const Object& node<Object>::link() const { //return next node (const function)
        return next->data();
}


template <typename Object>
const Object& node<Object>::back() const { //return previous node (const)
        return previous->data();
}

【问题讨论】:

  • “rod”的声明是什么样子的?
  • 这些方法都是静态的吗?没有new 运营商?
  • LStack&lt;std::string&gt; rod[2]; 网络。在addToHead() 中复制了head 后,声明head = new node&lt;Item&gt;; 这是根据另一个人Paul 的建议。
  • @CameronO'Reilly head-&gt;set_data(head_copy-&gt;link()); 应该会产生编译错误。如果您尝试通过head-&gt;set_link(head_copy-&gt;link());head-&gt;set_data(head_copy-&gt;data()); 来“修复”它,您将拥有一个什么都不做然后返回顶部项目的函数。 (人们会期望在该函数中看到对 headdelete 的修改。)
  • 另一个潜在的问题是复制整个列表,例如LStack&lt;string&gt; rodCopy = rod[0];。当涉及到指针时,您必须非常小心地制作原始堆栈的深层副本。否则,您在销毁列表时会遇到问题,例如一个节点似乎是两个列表的成员。 (您还没有显示复制或销毁堆栈的代码)。

标签: c++ string class templates


【解决方案1】:

识别被压入堆栈的数据:

void TowerHanoi::set_Discs(size_t disc) 
{
    while (disc > 0) 
    {
        string tmp_str;               <-- note here, tmp_str is stack variable
        rod[0].push(tmp_str);
    }
}

现在,让我们看看 LStack::push() 函数:

//push
template <typename Obj>
void LStack<Obj>::push(Obj& head_in)   <-- note here, input to push() is pass by reference
{
    list->addToHead(head_in);
    used++;
}

一旦 set_Discs() 函数调用完成,对该函数内部使用的堆栈变量的引用无效。

这可能与在使用 LStack 存储数据的地方调用 print_Game() 函数时程序崩溃/突然终止的问题有关。

【讨论】:

  • 感谢您的建议。那么这是否意味着在node::set_data() 中我应该声明一个新的字符串对象,然后说word = new_string;?因此创建 tmp_str 的副本,然后存储该副本而不是引用?
  • @CameronO'Reilly,是的,你是对的。此外,您必须注意在 pop() 中释放分配的内存。否则,我认为将 LStack::push() 函数的原型更改为 void LStack::push(Obj head_in) 可以正常工作。在这种情况下,其他代码保持不变。 [通过值/复制而不是通过引用传递]。
  • 我已将其更正到没有段错误的程度,但是在打印时,它会打印前两个对象“x”和“xxx”,然后打印第二个对象的副本。像这样,x xxx xxx xxx 会不会是addToHead()的错误?
猜你喜欢
  • 2011-04-22
  • 2018-12-30
  • 2021-12-01
  • 2020-03-02
  • 2013-04-16
  • 1970-01-01
  • 1970-01-01
  • 2012-10-15
相关资源
最近更新 更多