链表数组作业——运行时错误

Array of Linked List Homework - Runtime Error

我一直在做这个家庭作业,我觉得我已经尝试了我能做的任何事情,但这太令人沮丧了。基本上这个程序应该模拟监控计算机实验室的人的管理控制台。主菜单中有 3 个选项,除了 "logoff" 功能外,我都可以使用它们。

这个程序的大问题是实验室应该是数组,位于特定计算机站的 ID 号应该存储在链表中。我最费劲的部分是链表。

当我运行程序模拟登录,然后尝试注销时我的程序崩溃了。所以我尝试使用 Visual Studio 调试它,它告诉我第 121 行有一个未处理的异常(下面注释它位于注销函数中)。我多次尝试联系我的老师,但他没有回应。如果有人能指出正确的方向,那就太好了。

编辑:我注意到删除功能可能会导致问题。虽然它似乎删除了列表中的节点,但当我查看调试器时,似乎在一个节点中留下了一些垃圾数据。删除节点时是否应该进行某种初始化?就像在删除节点时将变量设置为 NULL 或 nullptr 一样?

编辑 2:我缩短了代码。这是我得到的异常: 抛出未处理的异常:读取访问冲突。iter 是 nullptr。

#include <iostream>
using namespace std;

struct User
{
    int IDNumber;
    int stationNumber;
    User *link;
};

typedef User* NodePtr;

const int COMPUTER_LABS = 4; 
const int COMPUTERLAB_SIZES[] = { 5, 6, 4, 3 }; number of labs

void head_insert(NodePtr& head, int IDNum, int stationNum)
{
    NodePtr temp_ptr;
    temp_ptr = new User;

    temp_ptr->IDNumber = IDNum;
    temp_ptr->stationNumber = stationNum;

    temp_ptr->link = head;

    head = temp_ptr;
}

void remove(NodePtr& head, int position)
{

    if (head == NULL)
        return;

    NodePtr temp = head;

    if (position == 0)
    {
        head = temp->link;
        delete temp;
        return;
    }

    for (int i = 0; temp != NULL && i<position - 1; i++)
        temp = temp->link;

    if (temp == NULL || temp->link == NULL)
        return;

    NodePtr next = temp->link->link; 


    delete temp->link; 

    temp->link = next;

}

//This function will take in our multidimensional array and prompt the user for data on simulating a login
//At the end, the spot will be assigned a number
void login(NodePtr labs[])
{
    int IDNum, labNum, stationNum;
    cout << "Enter the 5 digit ID number of the user logging in: " << endl;
    cin >> IDNum;
    cout << "Enter the lab number the user is logging in from (1-4): " << endl;
    cin >> labNum;
    cout << "Enter computer station number the user is logging in to (1-6): " << endl;
    cin >> stationNum;

    if (labs[labNum - 1]->link == NULL && labs[labNum - 1]->stationNumber == NULL)
    {
        labs[labNum - 1]->stationNumber = stationNum;
        labs[labNum - 1]->IDNumber = IDNum;
    }
    else
        head_insert(labs[labNum - 1], IDNum, stationNum);

}

void showLabs(NodePtr labs[])
{
    for (int i = 0; i < COMPUTER_LABS; i++)
    {
        cout << i + 1 << ": ";
        for (int j = 0; j<COMPUTERLAB_SIZES[i]; j++)
        {
            for (NodePtr iter = labs[i]; iter != NULL; iter = iter->link)
            {
                if (iter->stationNumber == (j + 1))
                {
                    cout << j + 1 << ": " << (iter->IDNumber) << " ";
                    break;
                }
                else if ((iter->link) == NULL && (iter->stationNumber) != (j + 1))
                    cout << j + 1 << ": empty ";
            }
        }
        cout << endl;
    }

}

//This function will simulate a logoff by looking for the ID number that was typed in
//It will then clear that computer station and log the user off
void logoff(NodePtr labs[])
{
    int IDNumber;
    bool isBreak = false;
    cout << "Enter 5 digit ID number of the user to find: " << endl;
    cin >> IDNumber;

    for (int i = 0; i < COMPUTER_LABS; i++)
    {
        int j = 0;
        for (NodePtr iter = labs[i]; iter != NULL; iter = iter->link, j++) //This is where it says exception unhandled
        {
            if (iter->IDNumber == IDNumber)
                remove(iter, j);
        }
    }

    cout << "User " << IDNumber << " is logged off." << endl << endl;
}


int main()
{
    NodePtr computerLabs[COMPUTER_LABS];
    for (int i = 0; i < COMPUTER_LABS; i++)//Initialize nodes in array so that we don't get any undefined behavior from not initializing
    {
        computerLabs[i] = new User;
        computerLabs[i]->stationNumber = NULL;
        computerLabs[i]->IDNumber = NULL;
        computerLabs[i]->link = NULL;
    }

    int userChoice; //This is for making a choice at the main menu
    do
    {
        cout << "LAB STATUS" << endl;
        cout << "Lab # Computer Stations" << endl;

        showLabs(computerLabs); //Show the current computer labs and their statuses

        cout << endl << "MAIN MENU" << endl;
        cout << "0) Quit" << endl;
        cout << "1) Simulate login" << endl;
        cout << "2) Simulate logoff" << endl;

        cin >> userChoice; // ask user for choice 

        if (userChoice == 1)
            login(computerLabs);
        else if (userChoice == 2)
            logoff(computerLabs);
    } while (userChoice != 0);
    return 0;
}

每次使用删除时,都应该将指针设置为NULL。你有

delete temp->link

添加

temp->link = NULL

就在这之后。清空已删除的指针是一种合理的预防措施。 删除会释放分配的内存,但不一定会清除指针或将其设置为 NULL。您可以在调试器中进行检查。将手表放在指针上。当它被分配和使用时,你会看到所有的字段。删除后,这些字段将是无意义的,因为它指向未分配的内存。

这段代码几乎完全错误。要修复您的代码,让我们从不要求用户输入开始。使用预先确定的值使程序更易于调试。您可以在掌握基础知识后添加用户输入。

不要使用声明typedef User* NodePtr它没有任何问题,但它隐藏了指针,在您的水平上可能会造成混淆。

链表最初应该是空的,如下:

for(int i = 0; i < COMPUTER_LABS; i++)
    computerLabs[i] = NULL;

删除节点时,节点是否head需要不同的条件。这是一个简单的版本:

#include <iostream>
using namespace std;

struct User
{
    int id;
    User *next;
};

const int COMPUTER_LABS = 4; 

void login(User *labs[], int lab, int id)
{
    User *p = new User;
    p->id = id;
    p->next = labs[lab];
    labs[lab] = p;
}

void logoff(User *labs[], int lab, int id)
{
    User *prev = nullptr;
    User* walk = labs[lab];
    while(walk)
    {
        if(walk->id == id)
        {
            User *next = walk->next;
            delete walk;
            if(prev)
                prev->next = next;
            else
                labs[lab] = next;
            break;
        }
        prev = walk;

        walk = walk->next;
    }

}

void showLabs(User *labs[])
{
    for(int i = 0; i < COMPUTER_LABS; i++)
    {
        cout << "Lab number " << i << ": ";
        User* walk = labs[i];
        while(walk)
        {
            cout << "id: " << walk->id << ", ";
            walk = walk->next;
        }
        cout << endl;
    }
}

int main()
{
    User *labs[COMPUTER_LABS];
    for(int i = 0; i < COMPUTER_LABS; i++)
    {
        //linked list is initially empty, this is "head"
        labs[i] = NULL;
    }

    login(labs, 0, 100);
    login(labs, 0, 101);
    login(labs, 1, 200);
    login(labs, 1, 201);
    showLabs(labs);
    logoff(labs, 0, 100);
    showLabs(labs);

    return 0;
}