【问题标题】:Freeing C Stack & Removing Dangling Pointers释放 C 堆栈并移除悬空指针
【发布时间】:2011-07-23 16:27:06
【问题描述】:

我已经用 C 语言实现了一个堆栈,使用一个 stackADT 结构和一组函数:

#ifndef _stack_h
#define _stack_h

// Macros
#define MaxStackSize 100
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// The type of element that may 
// be stored in the stack
typedef int stackElementT;

// The stackADT represents the abstract type used to store
// the elements that have been pushed
typedef struct stackCDT
{
    // A pointer to an array of elements
    stackElementT* elements;
    // Number of elements on the stack
    int count;
    // Number of elements we can push onto
    // the stack before having to resize
    int size;
}* stackADT;

// This function allocates and returns a new stack, which is
// initially empty... AKA - The Constructor
stackADT NewStack(void)
{
    // The stack to return
    stackADT stack;

    // Instanitate a new stack
    stack = (stackCDT*)(malloc(sizeof(stackCDT)));

    // Start with 0 elements of course
    stack->count = 0;
    // Allocate memory for 50 integers
    stack->elements = (stackElementT*)(malloc(50*sizeof(stackElementT)));
    // Establish the size of the stack
    stack->size = 50;

    return stack;
}


/********* GETTER FUNCTIONS *********/
// Returns the number of elements currently pushed 
// onto the stack
int StackDepth(stackADT stack)
{
    return (stack->count);
}

// This function returns the element a the specified index in
// the stack, where the top is defined as index 0
stackElementT GetStackElement(stackADT stack, int index);

// Function to print contents of stack
void PrintStack(stackADT stack)
{
    int i = 0;
    printf("count = %d\nsize = %d\n",stack->count,stack->size);

    for(i = (stack->count - 1); i >= 0; i--)
    {
        if((i%10 == 0) && (i != 0))
            printf("\n");
        printf("%d\t",*(stack->elements + i));
    }
}


// Functions to determine if stack is empty or full
int StackIsEmpty(stackADT stack)
{
    if(stack->count == 0)
        return 1;
    else
        return 0;
}
int StackIsFull(stackADT stack)
{
    if(stack->count == stack->size)
        return 1;
    else
        return 0;
}


// This function pushes the specified element onto the stack
void Push(stackADT stack, stackElementT element)
{
    // A temporary array that we may use later on
    stackElementT* temp = NULL;
    int oldCount = stack->count;
    int i = 0;

    // If the stack if full we need to do a
    // a transfer, resize, and retransfer, then push
    if(StackIsFull(stack))
    {
        // temp will be the same size as the old stack
        temp = (stackElementT*)(malloc((oldCount)*sizeof(stackElementT)));

        // Now we perform the transfer
        for(i = 0; i < oldCount; i++)
        {
            *(temp + i) = *((stack->elements) + i);
        }

        // Free the old memory
        free(stack->elements);
        stack->elements = NULL;

        // Recreate the stack with a 50% increase in size/capacity
        stack->elements = (stackElementT*)(malloc((3*oldCount/2)*sizeof(stackElementT)));
        // Re-establish the size
        stack->size = 3*oldCount/2;

        // Now we perform the transfer back
        for(i = 0; i < oldCount; i++)
        {
            *((stack->elements) + i) = *(temp + i);
        }

        // Free the temp array and 
        // remove dangling pointer
        free(temp);
        temp = NULL;

        // Now we push the element onto the stack
        *((stack->elements) + oldCount) = element;
        // Increase the count
        stack->count = oldCount + 1;
    }
    // If the stack isn't full
    else
    {
        *((stack->elements) + oldCount) = element;
        stack->count = oldCount + 1;
    }

}

// This function pops the top element from the stack and returns
// that value
stackElementT Pop(stackADT stack);


// This function frees the storage associated with the stack
void FreeStack(stackADT stack)
{
    // Start by freeing the elements on the stack
    // and remove dangling pointers
    free(stack->elements);
    stack->elements = NULL;

    // Finally free the stack
    free(stack);
    stack = NULL;
}

#endif

显然我还没有完全完成(需要一个弹出功能)。我关心的是底部功能(FreeStack)。我这样测试了下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include "Stack.h"
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>

int main(void)
{
    stackADT stack;
    int i = 0;

    stack = NewStack();
    PrintStack(stack);

    for(i = 0; i < 60; i++)
    {
        Push(stack,i);
    }

    PrintStack(stack);
    FreeStack(stack);

    _CrtDumpMemoryLeaks();

    return 0;
}

_CrtDumpMemoryLeaks() 函数用于 Visual Studio,它指示是否存在内存泄漏。显然,在调用 FreeStack(stackADT stack) 函数时,我已经密封了任何泄漏。但是,堆栈指针仍然拥有一个内存地址,这是问题所在,因为 FreeStack 函数应该释放堆栈变量指向的内存并将其设置为 NULL。这发生在函数内,但是当我在调试期间返回主函数时,我看到内存地址仍然存在。我在这里缺少什么?如果我能够释放内存,为什么我不能移除悬空指针?

【问题讨论】:

  • 与您的堆栈释放问题无关,但为什么在调整堆栈大小时要移动元素两次?分配一个新的、更大的内存块、移动现有元素一次并将新指针分配给您的结构更简单。
  • 你不应该在头文件中定义函数。定义应在相应的源文件中提供。
  • @Blastfurnace 你说得对!!!
  • @Saurabh 我有另一个类似的问题,但这是 C++。我喜欢继承其他编程语言的旧习惯。无论如何,这不像我在与一个团队一起工作,这只是为了我。

标签: c pointers data-structures memory-management destructor


【解决方案1】:

您通过值而不是地址将堆栈传递给函数,修改函数以接收(stackADT *),您会很高兴。

澄清: 正如 Christian 评论的那样,函数调用和堆栈的使用当然也必须更改(因为现在它是指向指针的指针......)

【讨论】:

  • 请注意,您需要将您的方法调用修改为 FreeStack(&amp;stack); 以使其正常工作,并将所有取消引用更改为双重取消引用((*stack)-&gt;count 等)
  • 谢谢,成功了。但在我检查你的答案之前,我的另一个问题是,如果我只是按值传递,我怎么能释放内存?此外,stackADT 类型被定义为指向名为 stackCDT 的结构的指针。这让我有点困惑,我来自 C++ 和 Java 背景。
  • 您使用传递给函数的指针释放了 stackCDT,但是由于您没有提供指向函数的指针的地址,因此您无法更改指针的原始值,而只能它的副本。您传递的副本仍然是指向有效分配内存的指针。
【解决方案2】:

您在 pop 方法中按值传递 stackADT 对象(指针):

void FreeStack(stackADT stack)

所以stack 指的是该指针的本地副本。当您设置该指针 = NULL 时,您只能在 FreeStack 中修改它。 main 方法有自己的指针副本,不指向 NULL。

【讨论】:

  • 是的,但是 free() 函数会完成它的工作。占用的内存仍然设置为“空闲”。堆栈对象将只保存对 free-d 内存地址的引用;所有权仍然丢失。
猜你喜欢
  • 2016-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-01
  • 2018-06-01
  • 1970-01-01
相关资源
最近更新 更多