在C ++中的链表中间插入节点时出现段错误

Segfault while inserting node at mid of a linked list in c++

我正在创建一个简单的单向链表并尝试在链表的开头、中间和结尾插入一个节点。但是我的 'insertAtMid' 没有正常工作,它使程序崩溃并出错; “发生异常。分段错误。” 这是整个程序:

/*
    Write a program that creates a singly linked list until user wants and displays it. then insert a node at the beginning, middle, and end of the list.
*/
#include <iostream>
using namespace std;
struct node
{
    int data;
    node *next;
};
class linkedList
{
private:
    node *head, *tail;

public:
    linkedList()
    {
        head = NULL;
        tail = NULL;
    }
    void addNode()
    {
        int item;
        cout << "Enter data for node : ";
        cin >> item;
        node *temp = new node();
        temp->data = item;
        temp->next = NULL;
        if (head == NULL)
        {
            head = temp;
            tail = temp;
        }
        else
        {
            tail->next = temp;
            tail = tail->next;
        }
    }
    node *getHead()
    {
        return head;
    }
    static void display(node *head)
    {
        int i = 1;
        while (head != NULL)
        {
            cout << "Node " << i << " : " << head->data << endl;
            head = head->next;
            i++;
        }
    }
    void insertAtBegining()
    {
        int n;
        cout << "Enter data you want to insert : ";
        cin >> n;
        node *temp = new node();
        temp->data = n;
        temp-> next = head;
        head = temp;
        cout << "Inserted Successfully. Updated list is : "<<endl;
    }
    static void insertAtMid(node * head)
    {
        int n, length=0, middle;
        while (head!=NULL)
        {
           length ++;
           head = head->next;
        }
        middle = length/2;
        while((middle --) > 1)
        {
            head = head->next;
        }
        cout << "Enter data you want to insert : ";
        cin >> n;
        node *temp = new node();
        temp->data = n;
        temp->next = head->next;
        head = temp;
        cout << "Inserted Successfully. Updated list is : "<<endl;
    }
};
int main()
{
    linkedList l1;
    char choice;
    int menuChoice;
    cout << "List is not created yet, please add data to create the list." << endl;
    do
    {
        l1.addNode();
        cout << "Node added. Want to add another node (Y/N) : ";
        cin >> choice;
    } while (choice == 'y' || choice == 'Y');
    cout << "List created successfully. Select from options below : "<<endl;
    cout << "1. Display list \n2. Insert at Begining \n3. Insert at Mid \n4. Insert at End \n5. exit"<<endl;
    cin >> menuChoice;
    switch(menuChoice)
    {
        case 1:
            linkedList::display(l1.getHead());
            break;
        case 2:
            l1.insertAtBegining();
            linkedList::display(l1.getHead());
            break;  
        case 3:
            l1.insertAtMid(l1.getHead());
            linkedList::display(l1.getHead());
            break;
        case 4:
            l1.addNode();
            cout << "Successfully inserted node at end of list. Updated list is :"<<endl;
            linkedList::display(l1.getHead());
            break;  
        case 5:
            exit;
            break;
        default:
            cout << "Invalid selection...!!";            
    }
}

编译器显示错误在第 85 行,那里发生了分段错误。这是错误的屏幕截图: Exception has occurred

根据您的代码实例,您正在计算节点数并将头指针移动到列表末尾。

在执行以下语句之前,head 当前将指向最后一个节点。

while((middle --) > 1)
        {
            head = head->next;
        }

一旦这条语句 head = head->next; 被执行,那么 head 将指向任何地方,或者你可以说,它指向 NULL。 这就是您遇到分段错误的原因,因为 head->next 试图提供对地址的引用,这是一个垃圾。那里什么都不存在。

代替使用head进行迭代,使用临时节点,将其分配给head然后计算列表的长度。

   node *tmp = new node();
   tmp = head;
   int length = 0;
   while(tmp != NULL){
       length++;
       tmp = tmp->next;
   }

   middle = ((length % 2) == 0) ? (length /2 ) : (length + 1)/2);
   tmp = head;
   while((middle --) > 1){
        tmp = tmp->next;
   }
 
   cout << "Enter data you want to insert : ";
   cin >> n;
   node *curr = new node();
   curr->data = n;
   curr->next = temp->next;
   temp->next = curr;

我建议您不要使用 head 指针进行迭代,因为它可能会导致悬空引用。

哇,你真的很困惑...首先,你有一个 class,有 private: 个成员 headtail。您不会将 headtail 作为参数传递给您的成员函数。您的 class 成员函数可以访问您的 class 私有成员。

所以你不做:

    static void display(node *head)
    {
        ...

相反,只需:

    void display ()
    {
        ...

您不要混合应用程序和界面

您的应用程序是您处理从程序用户界面获取的数据的方式。你把两者分开。您的菜单就是您的界面,您的 linked 列表就是您的应用程序(粗略地说)。您在程序界面中执行所有输出和数据输入,并将数据传递给应用程序进行处理。这使您的代码可维护且更易于阅读(并且更容易遵循逻辑)

您不会将数据提示隐藏在 addnode() 函数中。相反,您获取要在 main() 的初始部分或 switch() 语句中添加的数据,然后将整数值传递给 addnode()。示例:

    node *addNode (int n)   /* choose meaningful return to indicase success/failure */
    {
        // int item;
        // cout << "Enter data for node : ";  /* don't mix interface & application */
        // cin >> item;
        node *temp = new node();
        
        temp->data = n;
        temp->next = NULL;
        
        if (head == NULL)
            head = tail = temp;
        else {
            tail->next = temp;
            tail = temp;
        }
        
        return temp;        /* return pointer to allcated node */
    }

然后:

    /** this is interface, input/output happens here */
    std::cout << "List is not created yet, please add data to create the list.\n";
    do
    {
        int n;
        if (!(std::cin >> n)) {
            std::cerr << "error: invalid integer input.\n";
            return 1;
        }
        
        if (!l1.addNode (n)) {
            std::cerr << "error: adding node.\n";
            return 1;
        }
        
        std::cout << "Node added. Want to add another node (y/n) : ";
        if (!(std::cin >> choice)) {
            std::cerr << "error: invalid integer input.\n";
            return 1;
        }
    } while (choice == 'y' || choice == 'Y');

(对于 switch() 语句中的菜单也是如此)

您验证每个用户输入

除非您检查return,否则您无法正确使用任何输入功能。 (从技术上讲,您检查流状态)。您可以通过检查每个输入是否成功或处理错误来做到这一点。永远不要 cin >> data; 并希望 data 是好的。例如(最少):

                    int n;
                    std::cout << "Enter data for node : ";
                    if (!(std::cin >> n)) {
                        std::cerr << "error: invalid integer input.\n";
                        break;   /* note: this just lets the menu fail before clearing error */
                    }
                    if (l1.insertAtBegining (n)) {
                        std::cout << "Inserted Successfully. Updated list is : \n";
                        l1.display();
                    }
                    break;

对于使用 iostream 的任何输入,您必须 .clear() 流状态才能进行进一步的输入,并且您必须清空导致错误的输入缓冲区中的内容。一个更完整的例子是:

        while (!(std::cin >> menuChoice)) {     /* you need to handle error here */
            std::cerr << "error: invalid integer input.\n";
            std::cin.clear();
            std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
            std::cout << "\nchoice: ";
        }

参见 std::basic_istream::ignore and bookmark the cppreference.com link -- 这是您在 Internet 上最好的参考资料 -- 使用它。

(您应该更新其余代码以正确处理输入)

不要使用 head

进行迭代

您的 head 指针指向列表开头的节点。不要用它来遍历你的列表。 (完成后——它不再指向列表的开头,并且您丢失了指向开头的指针)。相反,你声明一个临时指针来迭代,例如:

    void display ()
    {
        int i = 1;
        node *iter = head;
        while (iter != NULL)
        {
            std::cout << "Node " << i << " : " << iter->data << '\n';
            iter = iter->next;
            i++;
        }
    }

不要养成 using namespace std; 的习惯,请参阅:Why is “using namespace std;” considered bad practice?. While you are at it, have a look at C++: “std::endl” vs “\n”

在中间插入

当您在列表中间插入 at 时,您必须天真地跟踪 prevcurrent 节点,以便插入您的节点,以便 prev->next = node_to_insert;node_to_insert->next = current;。或者,您可以使用指针正确迭代 address-of-the-nodepointer-to-node 以便您可以简单地替换node_to_insert 节点地址的内容,并且 node_to_insert->next 等于 指向节点的指针 ->next。参见 Linus on Understanding Pointers

如果你这样做,事情就会变得容易得多。您的 insertAtMid() 函数变为:

    node *insertAtMid (int n)
    {
        int length=0, middle;
        node *iter = head,
            **iteradr = &head;
        
        while (iter != NULL) {
           length++;
           iter = iter->next;
        }
        
        middle = length/2;
        iter = head;
        
        while (middle--)
        {
            iteradr = &iter->next;
            iter = iter->next;
        }
        
        // cout << "Enter data you want to insert : ";      /* mixing */
        // cin >> n;
        
        node *temp = new node();
        temp->data = n;
        
        if (!head)
            head = tail = temp;
        else {
            *iteradr = temp;
            temp->next = iter;
        }
        
        //cout << "Inserted Successfully. Updated list is : "<<endl;    /* mixing */
        
        return temp;
    }

总而言之,进行更多(轻微)更改并修复您的“混合应用程序和界面”问题,您可以这样做:

#include <iostream>
#include <limits>

struct node {
    int data;
    node *next;
};

class linkedList
{
  private:
    node *head, *tail;

  public:
    linkedList()
    {
        head = NULL;
        tail = NULL;
    }
    
    node *addNode (int n)   /* choose meaningful return to indicase success/failure */
    {
        // int item;
        // cout << "Enter data for node : ";  /* don't mix interface & application */
        // cin >> item;
        node *temp = new node();
        
        temp->data = n;
        temp->next = NULL;
        
        if (head == NULL)
            head = tail = temp;
        else {
            tail->next = temp;
            tail = temp;
        }
        
        return temp;        /* return pointer to allcated node */
    }
    
    node *getHead()
    {
        return head;
    }
    
    void display ()
    {
        int i = 1;
        node *iter = head;
        while (iter != NULL)
        {
            std::cout << "Node " << i << " : " << iter->data << '\n';
            iter = iter->next;
            i++;
        }
    }
    
    node *insertAtBegining (int n)
    {
        // cout << "Enter data you want to insert : ";      /* don't mix */
        // cin >> n;
        node *temp = new node();
        temp->data = n;
        temp->next = head;
        head = temp;
        // cout << "Inserted Successfully. Updated list is : "<<endl;
        
        return temp;
    }
    
    node *insertAtMid (int n)
    {
        int length=0, middle;
        node *iter = head,
            **iteradr = &head;
        
        while (iter != NULL) {
           length++;
           iter = iter->next;
        }
        
        middle = length/2;
        iter = head;
        
        while (middle--)
        {
            iteradr = &iter->next;
            iter = iter->next;
        }
        
        // cout << "Enter data you want to insert : ";      /* mixing */
        // cin >> n;
        
        node *temp = new node();
        temp->data = n;
        
        if (!head)
            head = tail = temp;
        else {
            *iteradr = temp;
            temp->next = iter;
        }
        
        //cout << "Inserted Successfully. Updated list is : "<<endl;    /* mixing */
        
        return temp;
    }
};

int main (void)
{
    linkedList l1;
    char choice;
    int menuChoice;
    
    /** this is interface, input/output happens here */
    std::cout << "List is not created yet, please add data to create the list.\n";
    do
    {
        int n;
        if (!(std::cin >> n)) {
            std::cerr << "error: invalid integer input.\n";
            return 1;
        }
        
        if (!l1.addNode (n)) {
            std::cerr << "error: adding node.\n";
            return 1;
        }
        
        std::cout << "Node added. Want to add another node (y/n) : ";
        if (!(std::cin >> choice)) {
            std::cerr << "error: invalid integer input.\n";
            return 1;
        }
    } while (choice == 'y' || choice == 'Y');
    
    std::cout << "List created successfully. Select from options below : \n";
    
    for (;;) {  /* loop continually */
        std::cout << "\n  1. Display list\n  2. Insert at Begining\n"
                    "  3. Insert at Mid\n  4. Insert at End\n  5. exit\n\n"
                    "choice: ";
        
        while (!(std::cin >> menuChoice)) {     /* you need to handle error here */
            std::cerr << "error: invalid integer input.\n";
            std::cin.clear();
            std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '\n');
            std::cout << "\nchoice: ";
        }
        
        switch(menuChoice)
        {
            case 1:
                l1.display();
                break;
            case 2: {
                    int n;
                    std::cout << "Enter data for node : ";
                    if (!(std::cin >> n)) {
                        std::cerr << "error: invalid integer input.\n";
                        break;
                    }
                    if (l1.insertAtBegining (n)) {
                        std::cout << "Inserted Successfully. Updated list is : \n";
                        l1.display();
                    }
                    break;
                }  
            case 3: {
                    int n;
                    std::cout << "Enter data for node : ";
                    if (!(std::cin >> n)) {
                        std::cerr << "error: invalid integer input.\n";
                        break;
                    }
                    if (l1.insertAtMid (n)) {
                        std::cout << "Inserted Successfully. Updated list is : \n";
                        l1.display();
                    }
                    break;
                }
            case 4: {
                    int n;
                    std::cout << "Enter data for node : ";
                    if (!(std::cin >> n)) {
                        std::cerr << "error: invalid integer input.\n";
                        break;
                    }
                    if (l1.addNode (n)) {
                        std::cout << "Inserted Successfully. Updated list is : \n";
                        l1.display();
                    }
                    break;
                }  
            case 5:
                return 0;
                break;
            default:
                std::cout << "Invalid selection...!!\n";
                break;
        }
    }
}

(注意: 你应该添加一个析构函数,以便在你使用 main() 以外的列表时释放列表内存)

并且从不列出一个列表l1(嗯,一个)。你知道这对老眼来说有多难吗?

示例Use/Output

$ ./bin/ll_class_menu
List is not created yet, please add data to create the list.
1
Node added. Want to add another node (y/n) : y
2
Node added. Want to add another node (y/n) : y
3
Node added. Want to add another node (y/n) : y
4
Node added. Want to add another node (y/n) : y
5
Node added. Want to add another node (y/n) : n
List created successfully. Select from options below :

  1. Display list
  2. Insert at Begining
  3. Insert at Mid
  4. Insert at End
  5. exit

choice: 1
Node 1 : 1
Node 2 : 2
Node 3 : 3
Node 4 : 4
Node 5 : 5

  1. Display list
  2. Insert at Begining
  3. Insert at Mid
  4. Insert at End
  5. exit

choice: 2
Enter data for node : 6
Inserted Successfully. Updated list is :
Node 1 : 6
Node 2 : 1
Node 3 : 2
Node 4 : 3
Node 5 : 4
Node 6 : 5

  1. Display list
  2. Insert at Begining
  3. Insert at Mid
  4. Insert at End
  5. exit

choice: 3
Enter data for node : 7
Inserted Successfully. Updated list is :
Node 1 : 6
Node 2 : 1
Node 3 : 2
Node 4 : 7
Node 5 : 3
Node 6 : 4
Node 7 : 5

  1. Display list
  2. Insert at Begining
  3. Insert at Mid
  4. Insert at End
  5. exit

choice: 4
Enter data for node : 8
Inserted Successfully. Updated list is :
Node 1 : 6
Node 2 : 1
Node 3 : 2
Node 4 : 7
Node 5 : 3
Node 6 : 4
Node 7 : 5
Node 8 : 8

  1. Display list
  2. Insert at Begining
  3. Insert at Mid
  4. Insert at End
  5. exit

choice: 5

这里有很多,慢慢看吧。确保您了解所做的更改以及进行更改的原因。如果您还有其他问题,请告诉我。