2

I've been doing this homework assignment and I feel like I've tried anything that I could do, but this is just so frustrating. Basically this program is supposed to simulate the admin console of someone who monitors computers labs. There are 3 options at the main menu, and I've got all of them to work except for the "logoff" function.

The big issue with this program is that the labs are supposed to be arrays and the ID Numbers that are located at a particular computer station are supposed to be stored in a linked list. The part that I am struggling the most with is the linked list.

When I run the program, simulate a login, and then try to logoff my program crashes. So I tried debugging it using Visual Studio, and it's telling me that there is an exception that is unhandled on line 121 (commented below it's located in the logoff function). I tried contacting my teacher multiple times, but he hasn't responded. It would be great if someone could point me in the right direction.

edit: I notice that the remove function could be causing issues. Although it seems to remove the node in the list it seems like there is some garbage data left in one node when I take a look at the debugger. Am I supposed to do some sort of initialization when I remove a node? Like setting variables to NULL or nullptr when I remove a node?

edit 2: I shortened the code. Here is the exception I'm getting: Unhandled exception thrown: read access violation.iter was 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;
}
halfer
  • 19,824
  • 17
  • 99
  • 186
brand5i2g
  • 61
  • 1
  • 8
  • 1
    1) "_So I tried debugging it using Visual Studio, and it's telling me that there is an exception that is unhandled_" Typically it states what _kind_ of exception that is unhandled. Did you try **stepping through** your code, line be line, while investigating the values of the local variables, opposed to just running your program with debugger attached? In addition: Your other, superfluous, comments make it hard to find the line where the crash actually occurs on. 2) "_and the deadline is approaching_" That's why you don't do your assignments on the last weekend. – Algirdas Preidžius Feb 03 '18 at 19:24
  • I admit my understanding of linked lists is not amazing although I do understand the basic concepts. My explanation was very vague I'm going to edit what I do know about the code. – brand5i2g Feb 03 '18 at 19:32
  • Also my teacher thinks it's a great idea to only make himself available the day the assignment is due for 1 hour. I've been working on this for quite a while and I haven't even been able to get help from him. – brand5i2g Feb 03 '18 at 19:40

2 Answers2

3

This code is almost completely wrong. To fix your code, let's start by not asking for user input. Use predetermined values to make the program easier to debug. You can add user input after you get the basics.

Don't use the declaration typedef User* NodePtr There is nothing wrong with it, but it hides the pointer and it can be confusing at your level.

Linked list should initially be empty, as follows:

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

When removing a node, you need different conditions if the node is head or not. Here is a simple version:

#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;
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
  • I learned to make User* as a data type from the book I was reading in class, which is Problem Solving with C++ (9th edition) by Walter Savitch. Is there another good book or link on the "Then Definitive C++ Book Guide and List" post that explains linked lists well that you recommend? – brand5i2g Feb 03 '18 at 22:44
  • As I said earlier, there is nothing wrong with that declaration. It is valid and used frequently. I just recommended not using it in this specific case. See this [link](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) for C++ books – Barmak Shemirani Feb 03 '18 at 23:05
2

Every time you use a delete, you should set the pointer to NULL. You have

delete temp->link

Add

temp->link = NULL

Just after. Nulling deleted pointers is a reasonable precaution. Deleting frees the allocated memory but doesn’t necessarily clear the pointer or set it to NULL. You can check that in the debugger. Set a watch on the pointer. When it’s allocated and in use, you see all the fields. After it gets deleted, the fields will be nonsense because it points to unallocated memory.

Pam
  • 1,146
  • 1
  • 14
  • 18