我正在用C语言编写一个堆栈结构和推/弹方法作为一种教育练习(不做作业,我只是想做点什么,虽然我希望我的作业很好),而且我陷入两难境地。
当我从堆栈中弹出一个节点时,我应该返回堆栈上还是堆上的值?
如果我在堆上返回值,如果使用堆栈的程序没有释放()内存,则会出现内存泄漏,但如果我使用堆栈则可能存在范围问题和用户(可能只是我的)我的堆栈实现将不得不处理额外的工作。
到目前为止,我一直在使用堆来处理堆栈结构及其方法,当我尝试将弹出的数据放在堆栈上时,当我尝试在测试程序中使用它时,数据已损坏
(最好不要在这里设计堆栈实现,这样堆栈的用户可以改变元素大小,从而将许多不同的类型推送到堆栈和不同大小的字符串,因为它将数据作为void指针处理)。
我的堆栈实现看起来像这样(我是C的新手,所以这可能有点乱):
#include "cstack.h"
struct node{
struct node* child;
struct node* parent;
void* data;
int has_child;
unsigned long int elemsize;
};
struct cstack{
struct node* root_node;
struct node* last_node;
unsigned long int elemsize;
void (*push)(cstack*); /*function pointers here incase it suits someones workflow, not that anyone will use my stack implementation*/
void* (*pop)(cstack*, void* data);
};
void push(cstack* stack, void* data){
struct node* currnode;
int isroot = 0;
if(stack->root_node == NULL){
stack->last_node = (struct node*)malloc(sizeof(struct node));
stack->root_node = stack->last_node;
stack->root_node->has_child = 0;
stack->root_node->parent = NULL;
stack->root_node->elemsize = stack->elemsize;
isroot = 1;
}
currnode = stack->last_node;
/*If they have added a node themselves, which is fine, it will cause undefined behaviour so let's make sure
the node has no children, but if they have hacked it let's hope they set the correct flags >->*/
while(currnode->has_child){
currnode = currnode->child;
}
if(!isroot){
currnode->has_child = 1;
currnode->child = (struct node*)malloc(sizeof(struct node));
currnode->child->data = malloc(stack->elemsize);
currnode->child->parent = currnode;
currnode->child->elemsize = stack->elemsize;
memcpy(currnode->child->data, data, stack->elemsize);
stack->last_node = currnode->child; /*update the last node position of the stack*/
}else{
currnode->data = malloc(stack->elemsize);
memcpy(currnode->data, data, stack->elemsize);
}
}
void* pop(cstack* stack){
void* rtrn;
if(stack->root_node != NULL){
struct node* currnode = stack->last_node;
while(currnode->has_child){
currnode = currnode->child;
}
/*This is where the variable to return get's set
when I was using the stack my code was
rtrn = &(*currnode->data)*/
rtrn = malloc(currnode->elemsize);
memcpy(rtrn, currnode->data, currnode->elemsize);
if(currnode->parent != NULL){
stack->last_node = currnode->parent;/*we're popping the top node so the last node in the stack should be the parent node of the current last node*/
currnode = currnode->parent; /*elevate to parent node*/
currnode->has_child = 0;
free(currnode->child->data);
currnode->child->data = NULL;
free(currnode->child);
currnode->child = NULL;
}else{ /*it's the root node*/
free(currnode->data);
currnode->data = NULL;
free(currnode);
stack->root_node = NULL;
stack->last_node = NULL;
}
}else{
rtrn = (void*)NULL; /*this cast is redundant because NULL should be a void pointer anyway, but if it isn't then we need to make sure it is*/
}
return rtrn;
}
void change_elem_size(cstack* stack, unsigned long int elemsize){
stack->elemsize = elemsize;
}
cstack* new_stack(unsigned long int elemsize){
cstack* rstack;
rstack = (cstack*)malloc(sizeof(cstack));
rstack->root_node = (struct node*)malloc(sizeof(struct node));
rstack->elemsize = elemsize;
rstack->pop = pop;
rstack->push = push;
rstack->last_node = rstack->root_node;
rstack->root_node->parent = NULL;
rstack->root_node->has_child = 0;
rstack->root_node->child = NULL;
return rstack;
}
我的另一个想法是让他们传入一个指向预先分配的变量的指针,这样他们就会知道它在堆栈上并在必要时增加它的大小。
我希望你能帮助我,我不确定我是否应该去堆积,并希望我/人有足够的常识来释放()数据。
答案 0 :(得分:1)
我强烈建议关注最低惊喜的校长。在这种情况下,它应该与对象及其内存如何进入堆栈对称。重要的问题是:谁拥有该对象的时间?谁首先创造了对象的记忆?我还会考虑堆栈的预期性能方面。通常,从堆栈顶部插入(推送)和移除(弹出)对象应该是O(1)。您的潜在选择如何与此相符?
更普遍地思考我通常会期望如果我必须获取对象的内存,然后将对象“插入”某个数据结构,则所述结构将负责对象的所有权。如果我要弹出该项目,只是从数据结构中删除该项目,而不再是所有者,所有权将返回给调用者。
相反,如果我首先没有参与对象的内存分配而且DST为我做了,我希望以后我不必参与销毁/解除分配
你显然可以偏离这一点,但是对于你需要/希望提供文档的每一个偏差,以便你的结构的用户知道会发生什么,特别是这些可能会偏离他们遇到的大多数其他人。
另一个考虑因素是,如果你想要返回指向原始对象的指针以外的任何东西,你也将花费时间复制/重建对象。这当然会在复制程序和性能影响方面提出自己的问题。当真正复杂的对象开始进出数据结构时会发生什么。
答案 1 :(得分:0)
我喜欢我的pop函数返回被删除的元素而不释放它,因为它可以让使用它的人更自由。
但这是一个选择问题,你可以做一个拥有void原型的pop函数,你也可以释放元素。