试图了解我的链接列表程序中的内存泄漏

时间:2016-04-20 03:32:08

标签: c++ memory-leaks linked-list

我打开了一个处理内存泄漏的动态内存分配的应用程序。这是我在这个类别中的第一个作业,所以对我来说它是相当新的。我的教授已经对作业进行了评分并告诉我,我的内存泄漏了。内存泄漏在DeleteNode()函数中,我正在删除节点。有人可以向我解释为什么内存泄漏?我对这个主题非常新,我知道我很想念它,我只是需要它指出我想的。我的内存泄漏之外可能还有其他问题(有些地方我忘了查看内存是否已成功分配),但我理解的是,它只是我需要帮助的内存泄漏。另外,我只编写了AddNode()DeleteNode()BuildList()ZapList()函数,我没有对其余函数进行编码,它是一个已经为我们编写的shell 。任何帮助将不胜感激。

#include <iostream>
#include <ctype.h>
#include <new>
//#include <process.h>        //  Needed for call to exit
using namespace std;

struct Node
{
    enum  { DUMMY_VALUE = 1 };  //  Value () stored in dummy head node.
    char  Ch;                   //  Holds the char data.
    Node *Link;                 //  Points to another struct of type Node.
};

typedef Node* NodePtr;

void AbortProgram (void);

void AddNode (char NewChar,  NodePtr List);

void BuildList (NodePtr List);

void ZapList (NodePtr P);

void DeleteNode (char CharToDelete, NodePtr List, int &CharFound);

void StartList (NodePtr &List);

void ShowList (NodePtr List);

void DisplayMenuAndGetMenuChoice (char &MenuChoice);

void TestAddNode (NodePtr List);

void TestBuildList (NodePtr List);

void TestDeleteNode (NodePtr List);

void TestZapList (NodePtr List);
/*****************************  main   ********************************/

int main(void)
{
    NodePtr List = NULL;

    char MenuChoice;

    system("cls");
    cout << "This program allows you to test the routines needed \n"
    "for homework 8. \n\n";

    StartList(List);
    if (List == NULL)                        //  Unexpected error.
        AbortProgram ();

    do
    {
        DisplayMenuAndGetMenuChoice(MenuChoice);

        switch( MenuChoice )
        {
            case 'Q':  break;                // Exit program

            case 'B':  TestBuildList(List);
                break;

            case 'A':  TestAddNode(List);
                break;

            case 'D':  TestDeleteNode(List);
                break;

            case 'Z':  TestZapList(List);
                break;

            default :  cout << "\nError: '" << MenuChoice
                << "' is not an option \n\n";
        }
    }
    while ( MenuChoice != 'Q' );

    return 0;
}

/*********************   DisplayMenuAndGetMenuChoice *********************
 Displays a menu of options and reads the user's choice into the
 parameter MenuChoice. Unbuffered input is used, so the user does
 not have to enter a newline character after typing a menu choice.
 The MenuChoice is upcased.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void DisplayMenuAndGetMenuChoice (char &MenuChoice)
{
    const char* Option[] = {"B(uildList", "A(ddNode", "D(eleteNode",
        " Z(apList", "Q(uit", "" };

    char DottedLine[] ="\n- - - - - - - - - - - - - - - "
    "- - - - - - - - - - - - - - - -\n ";
    int K = 0;

    cout << DottedLine;

    while ( Option[K][0] != 0 )  // while we haven't gotten to ""
    {
        cout << Option[K];         // Display menu option
        cout << "  ";              // Add some white space.
        ++K;
    }

    cout << "=> ";
    MenuChoice = toupper(cin.get());
    cin.ignore(10,'\n');

    cout <<  DottedLine;
}

/************************    TestAddNode  ********************************
 Facilitates the testing of the function AddNode, a function which
 adds a node to the tail end of a linked list. If the enter key is
 pressed in response to the prompt, it is assumed that the user
 wants to exit and this function is aborted.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestAddNode (NodePtr List)
{
    char NewChar;

    cout << "\n\n----------------   Testing AddNode   -------------------\n\n";

    cout << "Character to be added? ";
    NewChar = cin.get();
    cin.ignore();

    if (NewChar == '\n')  // User pressed just enter key.
    {
        cout << "Aborting AddNode...";
        return;
    }

    cout << NewChar;
    cout << " --  Adding \'" << NewChar  << "\'";

    AddNode (NewChar, List);

    cout << "\n\nThe new list: ";
    ShowList(List);
}

/*************************    TestBuildList     **************************
 Facilitates the testing of the function BuildList, which is supposed
 to build an ordered linked list of characters.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestBuildList (NodePtr List)
{
    cout << "\n\n=================  Testing BuildList  ===================";
    cout << "\n\nType the characters for the list -  "
    "when finished, press enter key\n\n ->";

    BuildList(List);

    cout << "\n\nAfter BuildList, List = ";
    ShowList(List);
}

/***********************    TestDeleteNode   *****************************
 Facilitates the testing of DeleteNode, a function which is supposed
 to delete characters from a linked list.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestDeleteNode (NodePtr List)
{
    int  CharFound;
    char CharToBeDeleted;

    cout << "\n\n*****************   Testing DeleteNode   *******************";

    cout << "\n\nCharacter to be deleted? ";
    CharToBeDeleted = cin.get();
    cin.ignore();

    DeleteNode (CharToBeDeleted, List, CharFound);

    if ( CharFound )
        cout << "\n\n'" << CharToBeDeleted << "' has been deleted,";
    else
        cout << "\n\n'" << CharToBeDeleted << "' was not in the list,";

    cout << "\n\nList = ";
    ShowList(List);
}

/***********************    TestZapList  *********************************
 Facilitates the testing of ZapList, a function that is supposed to
 return all storage allocated for a linked list to the heap (except the
 storage occupied by the dummy head node.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void TestZapList (NodePtr List)
{
    cout << "\n\n^^^^^^^^^^^^^^^^^   Calling ZapList  ^^^^^^^^^^^^^^^^^^^^^^^";

    ZapList(List);

    cout << "\n\nList = ";
    ShowList(List);
}

/****************************  AbortProgram   ****************************
 Displays an error message and returns a non-zero error code to
 the operating system.

 Requires exit function prototyped in process.h.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void AbortProgram (void)
{
    cout << "\n\n\a A error has occurred while using the new operator. \n"
    "Returning to the operating system\n";
    cout << "Press ENTER key to continue: ";
    cin.get();
    exit(1);
}

/************************    StartList  *********************************
 DESCRIPTION   Creates an empty list, i.e. a singly linked list that
 contains only a dummy head node.

 PARAMETER

 OUT, List   A pointer to the head node of the list. If the
 dynamic memory allocation is unsuccessful, List will
 hold NULL on exit.

 PRECONDITION  List points to NULL. If List points to an actual node,
 calling this routine will create inaccessable memory.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void StartList (NodePtr &List)
{
    List = new(nothrow) Node;
    if (List == NULL)
        return;                        // Memory allocation error.

    List->Ch   = List->DUMMY_VALUE;  // Fill in dummy head node fields
    List->Link = NULL;               // Initialize end of list.
}

/*************************   ShowList  ***********************************
 DESCRIPTION  Displays the character field of all of the nodes in List, a
 singly linked list with a dummy head node. The list is
 enclosed in quotes.

 The constant MAX_CHARS_PER_LINE controls the maximum
 number of characters displayed before a newline char
 is displayed.

 PARAMETER

 IN, List   A pointer to a singly linked list with a dummy head node.

 NOTE         To facilitate debugging this routine displays "NULL"
 if called with List == NULL.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void ShowList (NodePtr List)
{
    const int MAX_CHARS_PER_LINE = 50;

    int CharCount = 0;

    if ( List == NULL )
    {
        cout << " NULL LIST\n\n";
        return;
    }

    cout << "\"";                 //  Display quote for ease of testing.
    while ( List->Link != NULL )
    {
        List = List->Link;
        cout << List->Ch;
        if ( List-> Ch != '\n' )   // Increment CharCount unless newline
            ++CharCount;             //   char is encountered in List
        else
            CharCount = 0;
        if ( CharCount % MAX_CHARS_PER_LINE == 0 )
            cout << "\n     ";
    }

    cout << "\"\n\n";
}

/*****************************   ZapList  ********************************
 DESCRIPTION  Frees all the storage space currently occupied by the
 linked list pointed to by List. Does NOT delete the delete
 the dummy head node.

 PARAMETER

 OUT, List  A pointer to a singly linked list with a dummy head node.
 After this call, List will contain only the dummy head node.

 PRECONDITION List must point to a linked list that has a dummy head node
 and a tail node that points at NULL.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void ZapList (NodePtr List)
{
    NodePtr Temp;

    Temp=List->Link;//Temp holds position of first Node after dummy node

    while (Temp != NULL)
    {
        List->Link=List->Link->Link;//rerouting dummy node pointer to skip next node and point to one after

        delete Temp;

        Temp=List->Link;
    }
}

/****************************   AddNode  *********************************
 DESCRIPTION  Adds a node containing NewChar to the end of List.

 PARAMETERS

 IN, NewChar The character to be added to the end of the list.

 IN, List    A pointer to a singly linked list with a dummy head node.
 The value of List (address of dummy head node) is not
 changed by this routine, so List is passed by value.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void AddNode (char NewChar, NodePtr List)
{
    NodePtr NewNode;
    NodePtr PlaceHolder;

    if (List->Link == NULL) // this if statement is used when the list coming in is empty (containing just the dummy)
    {
        List->Link = new Node;

        List->Link->Ch = NewChar;

        return;
    }

    PlaceHolder=List->Link; //placeholder and NewNode both point to first node after dummy node
    NewNode=List->Link;

    while (NewNode != NULL)
    {
        NewNode=PlaceHolder->Link; //Advance NewNode one node down the line

        if (NewNode != NULL) // if NewNode is not poing to Null, allow it to point at same value as NewNode
            PlaceHolder=NewNode;
    }                        //After loop, NewNode will be pointing at Null
                             //Placeholder will be one position behind NewNode

    NewNode= new Node;
    NewNode->Link = NULL;
    NewNode->Ch=NewChar;
    PlaceHolder->Link = NewNode;
}

/****************************    DeleteNode   ****************************
 DESCRIPTION  Deletes the first node of List that contains the char
 CharToDelete. The storage occupied by the deleted
 node is returned to the heap.

 PARAMETERS

 IN, CharToDelete  The character to be deleted.

 IN, List    A pointer to a singly linked list with a dummy head node.
 The value of List is not changed by this routine but the
 linked list itself is changed.

 OUT, CharFound Set to 1 if the CharToDelete is found and deleted and
 0 otherwise.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void DeleteNode (char CharToDelete, NodePtr List, int &CharFound)
{
    NodePtr NodeToBeDeleted;
    NodePtr PlaceHolder;

    NodeToBeDeleted=List->Link; //Both NodeToBeDeleted and Placeholder point to first node after dummy
    PlaceHolder=List->Link;

    if (List->Link == NULL)// this if-statement is here for empty linked lists coming in
    {                      // immediately set CharFound to 0 and end function
        CharFound=0;
        return;
    }

    while (CharFound != 1) // as soon as character is found, break out
    {
        if (NodeToBeDeleted->Ch == CharToDelete) // check to see if CharToDelete is found
        {
            delete NodeToBeDeleted;
            CharFound = 1;
        }

        if (CharFound == 0) // if not found, advance NodeToBeDeleted to the next postion
            NodeToBeDeleted=PlaceHolder->Link;

        if (NodeToBeDeleted == NULL) // as soon as NodeToBeDeleted points to null, stop testing
            return;

        if (NodeToBeDeleted->Ch != CharToDelete)
            PlaceHolder = NodeToBeDeleted;
        // only advance Placeholder to NodeToBeDeleted if CharToBeDeleted
        // isn't found. Once found, placeHolder remains one position behind
        // to allow linking of the list one node before deleted node
        // to one node after deleted node
    }

    PlaceHolder->Link=PlaceHolder->Link->Link;

    NodeToBeDeleted = NULL;

}

/****************************   BuildList    *****************************
 DESCRIPTION   Builds a singly linked list with a dummy head node. The
 characters in the list are in the same order in which the
 user enters them, i.e. new characters are added to the tail
 end of the list.

 Input terminates when the enter key is pressed.

 PARAMETERS

 IN, List    A pointer to a singly linked list with a dummy head node.
 It is imperative that List be initialized before calling
 this routine.

 NOTE          Before building the new list, ZapList is called. This
 ensures that a memory leak does not develop.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void BuildList (NodePtr List)
{
    char NewChar;
    NodePtr CurrentNode;

    ZapList(List);// ADDED AFTER IT WAS GRADED FOR HW9

    cin.get(NewChar);

    while (NewChar != '\n')
    {
        CurrentNode = new Node;    // attempt to create new node and have CurrentNode point to it

        if (CurrentNode == NULL)
            return;

        CurrentNode->Link = NULL;  //in new node, have node Link pointer point to NULL
        CurrentNode->Ch=NewChar;

        List->Link=CurrentNode;    //connect the newly created and filled node (Current Node) to list
        List=List->Link;           //advance List to next pointer

        cin.get(NewChar);
    }
}

2 个答案:

答案 0 :(得分:2)

该死,狙击。

以下伊万的回答是正确的。它通常不会出现段错误,因为在重新分配之前,释放的内存实际上没有任何内容,它在引用它时仍然包含有效数据,因此通常不会出现问题。

内存泄漏来自于您正在删除正在(或在这种情况下已被删除)的变量上的链接指针。因此,您放松了列表其余部分的句柄。

您的PlaceHolder指针应指向NodeToBeDeleted上方的一个节点,而不是指向同一节点。

答案 1 :(得分:1)

您可能会遇到内存泄漏的一种方法是在此测试中使用未初始化的变量:

while (CharFound != 1)

CharFound从调用函数(TestDeleteNode)传入,该函数未初始化变量CharFound

如果CharFound传入的值为1(可能会发生),那么你根本不会完成你的循环,在这种情况下你只能执行

PlaceHolder->Link=PlaceHolder->Link->Link;

会在“PlaceHolder-&gt;链接&#39;

中泄漏内存

在正常情况下,正如@Ivan所说,你正在删除对象,无论是Ivan提到的对象(NodeToBeDeleted->Ch),还是PlaceHolder->Link->Link,你都会deleted PlaceHolder->Link 1}} notnull指向的内存。