分段故障删除节点

Posted

技术标签:

【中文标题】分段故障删除节点【英文标题】:segmentation fault deleting nodes 【发布时间】:2014-07-03 20:18:41 【问题描述】:

我在编译这段代码时遇到了分段错误,我相信它是由这里的这个块引起的。

EmployPtr iter;    
//Deletes nodes outside of range.
while(netSalary(iter)<45000 || netSalary(iter)>60000)

    EmployPtr nodeToDelete = iter;
    iter = iter->link;
    delete nodeToDelete;

我发现指针非常令人困惑,并且有一个指针未指向内存中的有效对象,但我不确定如何解释它。我知道我需要在删除对象之前将对象的“下一个”(或“链接”我在代码中如何命名)指针重新分配给下一个指针,在对象被删除之后。我试图用代码做到这一点,但我仍然遇到段错误。谁能向我解释发生了什么并帮助我了解如何解决这个问题?

        EmployPtr nodeToDelete = iter->link;
        iter->link = nodeToDelete->link;
        delete nodeToDelete;

[包括其余代码,以防需要引用。]

#include <fstream>
#include <iostream>
using namespace std;

struct Employee

    string firstN;
    string lastN;
    float salary;
    float bonus;
    float deduction;

    Employee *link;
;

typedef Employee* EmployPtr;
void insertAtHead( EmployPtr&, string, string, float, float,float );
void insert( EmployPtr&, string, string, float, float,float );
float netSalary( EmployPtr& );

int main()

    //Open file
    fstream in( "payroll.txt", ios::in );

    //Read lines
    string first, last;
    float salary, bonus, deduction;
    EmployPtr head = new Employee;

    //Inserts all the data into a new node in the linked list, creating a new node each time the loop executes.
    while( in >> first >> last >> salary >> bonus >> deduction)
        insert (head, first, last, salary, bonus, deduction);

    //Close file
    in.close();

    cout << "\t\t\t\t-Salary in the range of ($45,000 - $60,000)-\n" << "Printed in format: First Name, Last Name, Salary, Bonus, Deduction, Net Salary.\n\n";

    //Deletes all nodes in the list that are not between 45,000 and 65,000. It then prints the newly modified list.
    EmployPtr iter;
    for(iter = head; iter!= NULL; iter = iter->link)
    
        //Deletes nodes outside of range.
        while(netSalary(iter)<45000 || netSalary(iter)>60000)
        
            EmployPtr nodeToDelete = iter;
            iter = iter->link;
            delete nodeToDelete;
        

        //Prints list.
        cout << iter->firstN << ", " << iter->lastN << ", " << iter->salary << ", " << iter->bonus << ", " << iter->deduction << ", " << netSalary(iter) <<endl;
    
        return 0;


void insertAtHead(EmployPtr& head, string firstValue, string lastValue,
            float salaryValue, float bonusValue,float deductionValue)

    //method definition


void insert(EmployPtr& afterNode, string firstValue, string lastValue,
        float salaryValue, float bonusValue,float deductionValue)

    //method definition


float netSalary(EmployPtr& node)

   //method definition

[更新代码]

    //Deletes nodes outside of range.
    while((netSalary(head)<45000 || netSalary(head)>60000) && head!=NULL)
    
        EmployPtr nodeToDelete = head;
        head = head->link;
        delete nodeToDelete;
        nodeToDelete->link = head;
    

        //Prints List
    EmployPtr iter;
    for(iter = head; iter!= NULL; iter = iter->link)
    
        cout << iter->firstN << ", " << iter->lastN << ", " << iter->salary << ", " << iter->bonus << ", " << iter->deduction << ", " << netSalary(iter) <<endl;
    

【问题讨论】:

如果您发现指针令人困惑,那么 1. 切换到托管语言,2. 或至少使用 std::list 很遗憾,这对我来说不是一个选择,而且我的指导方针规定我必须实施我自己的列表,所以这也不是一个选择。 编译时出现分段错误?真的 ??编译器真的会给出分段错误吗? 【参考方案1】:

考虑当列表的最后一个元素的净薪水低于 45000 或高于 60000 时会发生什么。最后一个元素的“链接”指针(可能)为 NULL,但您的内部循环不会检查它,所以删除该元素后,它会将 NULL 指针传递给 netSalary 函数。

当你到达列表的末尾时,你必须一直跳出循环。

【讨论】:

【参考方案2】:

我看到两个类似的问题。

一个是你有两次 iter = iter->link。一次在for循环中,一次在while循环中。您可能只需要一个控制回路。

另一个是while()循环从不检查iter是否为null。

所以想象一下以下数据集:15000、16000

for 循环指向 15000 节点。现在它到达了while循环。由于 15000 超出范围,它执行 iter = iter->link 并且 iter 现在指向 16000 节点。然后它删除 15000 节点。 while 循环再次运行,发现 16000 超出范围。它执行 iter = iter->link 并且现在 iter 指向 null。它删除了 16000 节点。 while 循环再次运行,但现在您将其传递为 null!

我会将其更改为使用 while 循环或 for 循环,并且只有一个地方可以检查 null 并移动到下一个节点。

【讨论】:

我不确定如何将代码添加到 cmets,所以我将其添加为您的编辑。你说的很有道理,所以我试着根据这个来修复它。我修复了悬空指针并将 for 循环向下移动,以便我可以使用它来打印列表,但现在我得到的只是一遍又一遍地打印相同的节点.. 好的,我想我的修改没有显示出来,所以我把它们添加回主要问题。 没有崩溃!这是一个进步!我不确定为什么它一遍又一遍地打印同一个节点。打印循环看起来正确。我确实看到了 while 循环的问题。想想如果第一个节点是 50000,而第二个节点是 15000 会发生什么。此时,您可能应该使用调试器。它将让您深入了解正在发生的事情。如果您不知道如何使用其中一个,大量 printf 或 cout 行将帮助您检查代码在做什么。【参考方案3】:

在您的 while 循环中,iter 可以指向列表的末尾,然后在 netSalary(iter) 中您传递了空指针(如果我假设您的列表以空指针结尾)。你的功能可以吗?或者你只是有类似 return iter->salary;

编辑: 这只是一个猜测,因为您没有提供方法的实现。

【讨论】:

【参考方案4】:

我不知道您是否修复了它,但我看到当您销毁节点时,您不会删除指向已删除项目的先前链接。我试过了,希望对你有帮助

EmployPtr iter = head;
EmployPtr prevIt = NULL; // pointer to previous node
while( iter )

    if(netSalary(head)<45000 || netSalary(head)>60000)
    
        EmployPtr nodeToDelete = iter;
        iter = iter->link;
        delete nodeToDelete;

        if( prevIt )
            prevIt->link = iter;
        else
            head = iter; // this happens when the head needs to be deleted, so head must point to nex
    
    else
    
        prevIt = iter;
        iter = iter->link;
    

【讨论】:

以上是关于分段故障删除节点的主要内容,如果未能解决你的问题,请参考以下文章

[仅在多个节点上的MPI分段故障

C++链表删除节点

删除时出现分段错误

删除二叉搜索树中的节点会引发错误

Cassandra集群管理-删除异常节点

C中的红黑树删除故障