考虑一个主文件,以及另一个实现数据结构的文件(比如:链表)。
链表的调用者可以将对象放在堆栈或堆上的链表上,我认为这是调用者的责任。
因此,在实现链表时,它是如何知道它是否在堆上?考虑一种典型的方法"从列表中删除节点。链接列表如何知道它是否应该释放该内存?根据我的理解,释放堆栈中的某些东西会导致未定义的行为。
因为这是类项目的一部分,所以我无法传递一些东西(isOnHeap)来指示调用者是否已将内存放在堆上(澄清:无法在我们的实现中不允许这样做) ),所以我假设这个问题可能有一个共同的解决方案,特别是考虑到它的情况有多常见。请注意,链接列表实现必须处理释放其自己的内存(假设这是因为它的实现对调用者是隐藏的而给出。)
答案 0 :(得分:1)
C中的数据结构实现不应该释放任何内容,除非明确告知它这样做。典型的链表实现可能具有如下功能:"创建节点","在此处将节点插入列表","从列表中删除节点","摧毁节点。"这些都不需要检查堆栈与堆。通常,堆栈上唯一可能是头部和外部的东西。整个列表的尾部指针;即使您可以通过您设计的API函数进行分配和释放(并使用堆)。
如果您真的希望能够构建一个可以将节点保留在堆栈中的侵入式链表,我们也可以讨论这个,但鉴于您发布的背景,我怀疑是这种情况。
无论如何,没有可移植的解决方案来了解堆栈或堆上是否存在某些内容,但是您可以在此处看到一些特定于平台的技巧:How to know if a pointer points to the heap or the stack? - 请注意,对于类分配你不太可能需要这些。
答案 1 :(得分:1)
...我无法传递某些内容(isOnHeap)来指示调用者是否已将内存放在堆上
这种情况偶尔会发生,但实际上并非如此。由于运行时库的不同版本,问题通常会显示出来。问题是库分配了一个调用者必须释放的块。
在Windows Net API函数中可以看到该问题的一个示例。例如,假设您调用NetGroupEnum function来枚举组。库将分配GROUP_INFO_*
结构。在呼叫者完成结构后,呼叫者有责任呼叫图书馆NetApiBufferFree function。
在这种情况下,项目需要提供分配器和删除器功能。然后你的例程简单地分配其他人提供的例程;然后在不再需要该对象时删除例程。
为了解决原始问题,通常可以梳理出分配的位置。但堆栈与堆通常无关紧要。
答案 2 :(得分:1)
通常,链表节点是动态创建的,并将在堆中分配。但是,如果您选择将已存在于堆栈中的节点放入列表,则会违反此假设。我想你是在问这种情况。
然后,取决于库的实现,free可以做什么,只是注册该内存,如果它在堆中。如果内存恰好在堆栈中,它可以选择做任何事情。或者它可能会出错并退出。但它没有定义。
答案 3 :(得分:1)
这是简单的is_on_stack(addr)函数:
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;