分段错误,在单向链表中插入节点

Segmentation Fault, Inserting a Node in a Singly Linked List

本文关键字:链表 插入 节点 错误 分段      更新时间:2023-10-16

我已经编写了一些代码,用于在"nth"位置的单向链表中插入节点,但是当我运行项目时,我收到分段错误:11

当 n == 到 sizeM 时,我调用将输入的 push_back 在末尾插入节点。当 n == 到 0 时,我调用将输入的 push_font 在开始时插入节点。这两个函数都工作,因为我已经调用了它们以在我的主文件中进行检查。在我进行任何调用以插入我的列表之前包含

440 330 110

headM 指向 440,这是列表中的第 0 个位置。

然后我打了 3 个电话插入

插入(40,3(

插入(10,0(

插入(33,2(

现在的列表应该是 10 440 33 330 110 40

我不确定出了什么问题,但我很确定当我插入 33 时要做的事情。

这是我的 simpleList.h 包含类

#ifndef Lab9_Simple_List__SimpleList__
#define Lab9_Simple_List__SimpleList__
typedef int ListItem;
class SimpleList {
private:
class Node {
public:
    ListItem item;
    Node *next;
};
Node *headM;
int sizeM;
void destroy();
// Deallocate all nodes, and sets headM to zero.
void copy(const SimpleList& source);
// List becomes copy of source.
public:
SimpleList(): headM(0), sizeM(0) { /* Point one */}
// PROMISES: creates empty list.
SimpleList(const SimpleList& source) { copy(source); }// copy constructor
SimpleList& operator =(const SimpleList& rhs);  // assignment operator
~SimpleList() { destroy(); }   // destructor
int size() const {return sizeM;}
void push_back(const ListItem& item);
// PROMISES:
//    Adds a node with an item to the end of the list, and increments sizeM
void push_front(const ListItem& item);
// PROMISES:
//    Adds a node with an item to the beginning of the list, and increments
//    sizeM
void pop_back(const ListItem& item);
// PROMISES:
//    The last node int list is removed
const ListItem& at(int n)const;
// PROMISES:
//    An item is return at the nth position in the list.
//    if n is less than 0 or greater than or equal sizeM gives the error
//    message: "Illegal Access" and terminates the program
ListItem& at(int n);
// PROMISES:
//    An item is return at the nth position in the list.
//    if n is less than 0 or greater than or equal sizeM gives the error
//    message: "Illegal Access" and terminates the program
void insert(const ListItem& theItem, int n);
// PROMISES:
//    A node with a copy of theItem is inserted at the nth position, and sizeM
//    will be incremented if the operation of insert was successfull.
//    if n == sizeM calles push_back
//    if n == 0 calls push_front
//    if n < 0 or n > sizeM returns and does nothing.
void remove(int n);
//  PROMISES:
//    Does nothing if n < 0 or n > sizeM-1. Otherwise, if list is not empty
//    removes the node at the position n.
};
#endif /* defined(Lab9_Simple_List__SimpleList__) */

这是我包含成员定义的文件,不起作用的是插入函数

#include <iostream>
using namespace std;
#include <stdlib.h>
#include "SimpleList.h"

SimpleList& SimpleList::operator =(const SimpleList& rhs)
{
if (this != &rhs) {
    destroy();
    copy(rhs);
}
sizeM = rhs.sizeM;
return *this;
}
ListItem& SimpleList::at(int n)
{
if(n < 0 || n >= sizeM)
{
    cout << "n Illegal Access. Program Terminates...";
    exit(1);
}
Node * p = headM;
for(int i= 0; i < n; i++)
    p = p -> next;
// point four
return p -> item;
}
const ListItem& SimpleList::at(int n)const
{
if(n < 0 || n >= sizeM){
    cout << "n Illegal Access. Program Terminates...";
    exit(1);
}
Node * p = headM;
for(int i= 0; i < n; i++)
    p = p -> next;
// point three - when reached for the first time
return p -> item;
}
void SimpleList::push_back(const ListItem& item)
{
Node *new_node = new Node;
if(new_node == NULL)
{
    cout << "nNo memory available to create a node" << endl;
    exit(1);
}
new_node->item = item;
if (headM == 0) {
    new_node->next = headM;
    headM = new_node;
}
else
{
    Node* p = headM;
    while (p ->next  != NULL)
        p = p ->next;
    p -> next = new_node;
    new_node -> next = NULL;
}
sizeM++;
// point five - when reached for the third time
}

void SimpleList::push_front(const ListItem& item)
{
Node *new_node = new Node;
new_node->item = item;
new_node->next = headM;
headM = new_node;
sizeM++;
// point two
}
void SimpleList::destroy()
{
// This function is not properly designed.
cout << "nSimpleList::destroy was called but didn't do the right job.";
headM = 0;
}

void SimpleList::copy(const SimpleList& source)
{
// this function is incomplete and is not properly designed. It doesnt do
// its job, makeing 'this' SimpleList object a copy of the scoure.
// The only effect of the next line is to tell the compiler
// not to generate an "unused argument" warning.  If you are going to complete
// this funciton, don't leave it in your solution.
(void) source;
cout << "nSimpleList::copy was called but didn't do the right job."
<< "--program is terminated.n";
exit(1);
}
void SimpleList::insert(const ListItem& theItem, int n)
{
if (n < 0 || n > sizeM)
    return;
else if (n == sizeM)
    push_back(theItem);
else if (n == 0)
    push_front(theItem);
else
{
    Node* new_node = new Node;
    new_node->item = theItem;
    Node* temp = headM;
    for(int i = 0; i < (n - 1) ; i++)
    {
        temp = temp->next;
    } 
    new_node->next = temp->next;
    temp->next = new_node;
    sizeM++;
}
}
void SimpleList::remove(int n)
{
if( n < 0 || n > sizeM ) { 
    return;
}
Node* p = headM;
if(n == 0 && headM != NULL)
{
    Node* const p_doomed = headM;
    headM = p_doomed->next;
    delete p_doomed;
    --sizeM;
}
else{
    for(int c = 0; c < n - 1; c++ )
    {
        p = p->next;
    }
    Node* const p_doomed = p->next;
    p->next = p_doomed->next;
    delete p_doomed;
    --sizeM;
}
}

下面是我的主要文件

#include <iostream>
#include <iomanip>
using namespace std;
#include "SimpleList.h"
#define EXERCISE_B
void print(const SimpleList& list);
// PROMISES: prints values in the list from first node (node number 0) to
//           the last node. 
int main()
{    
SimpleList list;
cout << "nList just after creation -- is empty.";
list.push_front(50);
cout << "nAfter calling push_front. list must have: 50n";
print(list);
list.push_back(440);
list.at(0) = 770;
cout << "nAfter calling push_back and at functions, list must have: 770  440n";
print(list);
list.push_back(330);
list.push_back(220);
list.push_back(110);
cout << "nAfter three more calls to push_back, list must have:"
"770, 440, 330, 220, 110n";
print(list);
#if defined (EXERCISE_B)
list.remove(0);
list.remove(2);
cout << "nAfter removing two nodes. list must have: 440, 330, 110n";
print(list);
list.insert(40, 3); //insert node with the value of 40 at the 4th position
list.insert(20, -1); // do nothing
list.insert(30, 30000); // do nothing
list.insert(10, 0); //insert node with the value of 10 at the 1st position
list.insert(33, 2); // insert node with the value 33 at the 3rd position
cout << "nTwo  more nodes inserted, must have: 10, 440, 33, 330, 110, 40n";
print(list);
list.remove(0);
list.remove(1);
list.remove(2);
list.remove(3);
list.remove(4);
list.remove(5);
cout << "nAfter 6 removes, list must have: 440, 330, 40: n";
print(list);
#endif
return 0;
}

void print(const SimpleList& list)
{
for(int i = 0; i < list.size(); i++)
    cout << list.at(i) << "  ";
}

这是我收到的输出

Haydns-MacBook-Pro:desktop Haydn$ g++ lab9_EXE_A.cpp simpleList.cpp
Haydns-MacBook-Pro:desktop Haydn$ ./a.out 
List just after creation -- is empty.
After calling push_front. list must have: 50
50  
After calling push_back and at functions, list must have: 770  440
770  440  
After three more calls to push_back, list must have:770, 440, 330, 220, 110    
770  440  330  220  110  
After removing two nodes. list must have: 440, 330, 110
440  330  110  
Two  more nodes inserted, must have: 10, 440, 33, 330, 110, 40
Segmentation fault: 11

问题出在void SimpleList::remove(int n)的第一行

if (n < 0 || n >= sizeM) {  // CHange to >= and not > !!!

解释:

如果在0 n的情况下调用函数,并且列表已经为空(意味着sizeM==0headM==nullptr(,则使用原始核心,则n<0 || n>sizeM为假。

这意味着函数将继续,e执行流为:

...
p=headM;   // meaning that p is now nullptr
if (n == 0 && headM != NULL)  // false because `n==0 && headM==nullptr`
// if block ignored 
else{  // here: (n!=0 || headM==NULL)  
    for (int c = 0; c < n - 1; c++) // the condition is false 
    // the loop body is ignored
    Node* const p_doomed = p->next;  // ouch !!!! p is nullptr => segfault !!! 

如果在调用该函数时n与堆栈的大小正好一样大,当您remove(3)时,这种情况发生在您的main()中,情况类似。 您尝试在p已经nullptr时过多地访问p->next一次。