2014-10-05 65 views
2

考虑一个主文件,以及另一个实现数据结构的文件(比如:链表)。数据结构实现是否可以知道它是否在堆上?

链表的调用者可以将对象放在堆栈或堆上的链表上,我认为这是调用者的责任。

所以当实现链表时,它是如何知道它是否在堆上?考虑从列表中移除节点的典型“方法”。链表如何知道它是否应该释放内存?根据我的理解,释放堆栈上的东西会导致未定义的行为。

因为这是一个类项目的一部分,我无法传递一些东西(isOnHeap)来表明调用者是否把内存放在堆上(澄清:因为在我们的实现中不能这样做) ,所以我假设这个问题可能有一个共同的解决方案,特别是考虑它会有多普遍。请注意,链表实现必须处理自己的内存释放(假设这是由于它的实现对调用者隐藏而给出的)。

+1

如果这是数据 - 在某种程度上重要结构代码(但是为什么?),那么应该从头开始设计API。 – user2864740 2014-10-05 02:16:40

+0

该标记指示这是一个与C相关的问题(在标题中添加了C)。我确实看到了这个问题。但是,请考虑问题的背景。 – 2014-10-05 02:24:59

回答

1

下面是简单的is_on_stack(地址)功能:

int *stack_start = nullptr; 

bool is_on_stack(void* ptr) { 
    int stack_probe; 
    if(stack_start < &stack_probe ) 
    return stack_start < ptr && ptr < &stack_probe; // stack grows upwards; 
    else 
    return &stack_probe < ptr && ptr < stack_start; // stack grows downwards; 
} 

int main() { 
    int stack_probe = 0; 
    stack_start = &stack_probe; 

    int dummy; 

    bool r1 = is_on_stack(&dummy); // shall be true 

    int* heap_thing = (int*)malloc(sizeof(int)); 

    bool r2 = is_on_stack(heap_thing); // shall be false   
} 

这里的关键点是,这些线路在main()函数的开头:

int stack_probe = 0; 
    stack_start = &stack_probe; 
+0

谢谢,但我找到了一种可能的替代解决方案 - 所以我要重新问一些更具体的东西。因为这在技术上可以回答这个问题,所以我会选择它作为任何其他可能发现它有用的答案。 – 2014-10-05 02:55:31

+0

@ c-smile - 我认为你的代码有错误。在'is_on_stack'中,你将'stack_probe'分配为一个'int',然后将它的(未定义的)内容与'int'指针'stack_start'进行比较。这显然是错误的。我想你想在这个函数的所有测试中使用'&stack_probe'而不是'stack_probe'。 – DoxyLover 2014-10-05 03:56:42

+0

另外,纠正我,如果我错了,我相信这是每个​​C规范未定义的行为。您正在比较指向不同对象的指针。虽然我同意这应该适用于大多数C实现,但我觉得我需要指出这一点。 – DoxyLover 2014-10-05 03:59:33

1

C中的数据结构实现不应该释放任何东西,除非明确告知这样做。典型的链表实现可能具有“创建节点”,“将节点插入到列表中”,“从列表中删除节点”和“销毁节点”等功能。这些都不需要检查堆栈与堆。通常情况下,唯一可能在堆栈上的是整个列表的头部指针&;即使你也可以通过你设计的API函数来分配和释放(并使用堆)。

如果你真的想能够建立一个入侵链接列表,其中节点可以保存在栈中,我们也可以谈论这一点,但鉴于你发布的背景,我怀疑是这种情况。

无论如何,没有便携式的解决方案可以知道堆栈或堆上是否有东西,但是您可以在这里看到一些特定于平台的技巧:How to know if a pointer points to the heap or the stack? - 请注意,对于班级作业而言,需要这些。

+0

然后我有点困惑,因为我们学习释放内存的一个典型例子是确保链接列表结构遍历并释放所有分配的内存。如果列表实现对调用者隐藏,那么调用者将如何知道如何释放已在堆上分配的节点上的任何数据?顺便提一下,我不是在质疑你的观点,这是一个真正的问题。对我来说,呼叫者应该承担责任确实更有意义。 但是,我们在列表实现中给出的“remove”和“destroy”函数指示释放动态内存。 – 2014-10-05 02:44:30

1

...我无法通过的东西(isOnHeap)指示呼叫者是否已经把内存堆上

这作物起来有时却没有真正在这方面。由于运行时库的不同版本,问题通常会显示出来。问题是一个库分配一个调用者必须释放的块。

这个问题的一个例子可以在Windows Net API函数中看到。例如,假设您调用NetGroupEnum function来枚举组。图书馆将分配GROUP_INFO_*结构。呼叫者完成结构后,呼叫者有责任拨打图书馆的NetApiBufferFree function

在这种情况下,项目需要提供分配器和删除器功能。然后你的例程简单地分配其他人提供的例程;然后在不再需要对象时使用例程删除。

为了解决最初的问题,通常可以梳理出分配的位置。但堆栈与堆通常无关紧要。

1

通常,链接列表节点是动态创建的,并将分配在堆中。但是,如果您选择将已经在堆栈中的节点放入列表中,则会违反此假定。我假设你问这种情况。

然后,取决于库的实现,free可能什么都不做,只是注册那些在堆中可用的内存。如果内存恰好在堆栈中,它可以选择做任何事情。或者它可能会给出错误并退出。但它是不确定的。

+2

通常 - 是的。但有时在堆栈上使用数据会更有效。在某些情况下,完全相同的列表可能会混合使用项目 - 堆和堆栈。通常项目知道如何摧毁自己(它有参考销毁功能或一些如此)... – 2014-10-05 02:35:01