我正在写一个堆栈,该堆栈是数据的链接列表(无效类型)。我正在测试的数据是
struct my_data {
int val;
char name[60];
};
struct my_stack_node {
void *data;
struct my_stack_node *next;
};
struct my_stack {
int size;
struct my_stack_node *first;
};
用于推送的数据的初始化如下:
s1 = my_stack_init(sizeof(struct my_data));
if (!s1) {
puts("Error in my_stack_init()");
exit(1);
}
printf("\ns1 initialized, size of data: %lu\n", sizeof(struct my_data));
for (int i = 0; i < NODES; i++) {
data = malloc(sizeof(struct my_data)); // We must allocate static memory
data->val = i;
sprintf(data->name, "Value %d", i);
if (my_stack_push(s1, data)) {
puts("Error in my_stack_push()");
exit(1);
}
} //s1 is the stack we are using here
并通过my_stack_push(s2,data)推送它们;堆栈,并将数据作为参数。
我的推送功能是这样的:
int my_stack_push(struct my_stack *stack, void *data){
if(stack == NULL && sizeof(data)> 0){
printf("Null Stack or data size error.\n");
//la pila debe existir
return -1;
}
else {
struct my_stack_node *nodeToPush = malloc(sizeof(struct my_stack_node));
nodeToPush -> data = data;
if(stack -> first == NULL) {
nodeToPush -> next = NULL;
stack -> first = nodeToPush;
}
else {
nodeToPush -> next = stack -> first;
stack -> first = nodeToPush;
}
}
return 0;
}
我的弹出功能就是这个
void *my_stack_pop(struct my_stack *stack){
struct my_stack_node *node = stack->first;
if(stack->first == NULL){
return 0;
}
stack->first = node->next;
void *ret = node->data;
free(node);
return ret;
}
但是在我的主菜单中,当我弹出它们并尝试进行比较时,出现了分段错误:
while ((data1 = my_stack_pop(s1))) {
data2 = my_stack_pop(fs1);
printf("Node of s1: (%d, %s)\t", data1->val, data1->name);
printf("Node of fs1: (%d, %s)\n", data2->val, data2->name);
if (!data2 || data1->val != data2->val || my_strcmp(data1->name, data2->name)) {
printf("Data in s1 and fs1 are not the same.\n (data1->val: %d <> data2->val: %d) o (data1->name: %s <> data2->name: "
"%s)\n",
data1->val, data2->val, data1->name, data2->name);
exit(1);
}
size1 = sizeof(*data1);
size2 = sizeof(*data2);
free(data1);
free(data2);
}
printf("size of data from s1: %d\t", size1);
printf("size of data from fs1: %d\n", size2);
(2个堆栈是彼此的副本,因此我输入的内容应该与我阅读的相同)。当我在pop函数中返回整个节点(不是数据,而是整个my_stack_node)时,一切都正确..但是错了:
Comparing the data...
s1的节点:(0,值0)//好一个 fs1的节点:(0,值0) 8 8
s1的节点:(-1203217792,NV)//这里开始出错 fs1的节点:(-1203217792,NV) 8 8
s1的节点:(-1203217792,NV) fs1的节点:(-1203217792,NV) 8 8
s1的节点:(-1203217792,NV) fs1的节点:(-1203217792,NV) 8 8
s1的节点:(0,) fs1的节点:(0,) 双重自由或腐败(fasttop) 中止(核心已弃用)
大小与输入的数据相同,但是值和名称不正确(即使在非复制堆栈中),应该是:
New node in s1: (0, Value 0)
New node in s1: (1, Value 1)
New node in s1: (2, Value 2)
New node in s1: (3, Value 3)
New node in s1: (4, Value 4)
New node in s1: (5, Value 5)
New node in s1: (6, Value 6)
New node in s1: (7, Value 7)
New node in s1: (8, Value 8)
New node in s1: (9, Value 9)
但是当我像代码中那样返回(在我的堆栈弹出窗口中)数据本身时,我将内核转储到测试的打印输出中(数据的长度为8个字节,就像一个输入一样)。
当我返回节点(大小= 64)时,它会正确打印错误的数据,但是当我返回数据(大小= 8(如推入的那个))时,它将导致核心错误。
如果我推送相同的数据并读取相同的数据(如返回节点所示,因为即使输出奇怪的输出也是如此),为什么当我返回原本应该打印的数据时却出现核心分割错误像上面的例子一样?
它看起来仅在我读取data2而不是data1时才发生。这是我用来读写文件的代码:
写:
int my_stack_write(struct my_stack *stack, char *filename){
int count = 0;
struct my_stack_node *aux =malloc(sizeof(struct my_stack_node));
FILE *file = fopen(filename, "wb");
if(stack->first != NULL){
aux = stack->first;
count++;
while(aux->next != NULL){
printf("New node in s1: (%p)\n", aux->data);
fwrite(aux ,sizeof(struct my_stack_node), 1, file);
aux = aux->next;
count++;
}
printf("New node in s1: (%p)\n", aux->data);
fwrite(aux ,sizeof(struct my_stack_node), 1, file);
}
fclose(file);
return count;
}
阅读:
struct my_stack *my_stack_read(char *filename){
struct my_stack *stackRead = my_stack_init(sizeof(struct my_stack_node));
struct my_stack_node *stackNode = malloc(sizeof(struct my_stack_node));
FILE *file = fopen(filename, "rb");
if(!file){
puts("Impossible obrir el fitxer");
return NULL;
}else{
while(fread(stackNode, sizeof(struct my_stack_node), 1, file)){
printf("New node in fs1: (%p)\n", stackNode->data);
stackNode = (struct my_stack_node*) stackNode;
my_stack_push(stackRead, stackNode->data);
}
fclose(file);
struct my_stack *InvertedStack = my_stack_init(sizeof(struct my_stack_node));
struct my_stack_node *aux = malloc(sizeof(struct my_stack_node));
while(my_stack_len(stackRead) !=0){
printf("Inverting the stack\n");
aux = my_stack_pop(stackRead);
my_stack_push(InvertedStack, aux);
}
return InvertedStack;
}
}
感谢任何有帮助的人。
该程序的MCVE,因此人们可以检查整个代码并提供更好的帮助:
test2.c:
my_lib.c: https://codeshare.io/G7L8Ab
my_lib.h:
有了这个,一旦对我发生的事情进行编译,您应该拥有更广阔的视野和一个可执行文件。
答案 0 :(得分:2)
您在my_stack_pop
中遇到问题
void *ret = malloc(sizeof(struct my_stack_node));
ret = node->data;
malloc 是无用的(并造成内存泄漏),您也错过了释放 node 的机会
您可以将这两行替换为:
void * ret = node->data;
free(node);
其他备注
my_stack_push
中检查错误之前进行分配,或者在出现错误的情况下释放 nodeToPush 否则会导致内存泄漏sizeof(x)
,其中 x 是void*
,如果您使用的是32b CPU,则始终为4;如果您使用的是64b CPU,则始终为8。例如, sizeof 不是 strlen 最后,关于两个堆栈是彼此的副本,所以我输入的内容应该相同很难帮助您,因为您没有说明如何克隆堆栈
(编辑后的备注)
在my_stack_write
my_stack_node
的内存不起作用,您的目标是保存数据(包含my_data
的数据),而不是指向数据的单元格在my_stack_read
my_stack_node
使用动态分配是没有用的,可以将其放入堆栈(struct my_stack_node stackNode;
)中,否则不要忘记释放它,因为您再次引入了内存泄漏。 li>
my_stack_node
时出现相同错误,您必须读取保存的数据(my_data
)stackNode = (struct my_stack_node*) stackNode;
什么也不做,因为它自己设置了 stackNode my_stack_push(stackRead, stackNode->data);
达不到预期的结果,因为stackNode->data
在文件中读取了错误的值。读写都错了,这就是为什么两个堆栈没有相同内容的原因
答案 1 :(得分:1)
设计良好,可靠且一致的API和库是一项艰巨的工作,尤其是在编程语言中,创建接口要比在面向对象的语言中难得多。
我的意思是非常好,但是您发布的代码存在内存泄漏,未处理的错误,未定义的行为和不良的设计。 sizeof运算符被滥用。我只能猜测您无法理解内存分配的工作原理以及指针和常规void*
指针的概念。
好吧,我们走吧。
所以代码将出现段错误的原因是:
malloc
已完成,但随后在某些行中free
被删除了:
for (int i = 0; i < NODES; i++) {
struct ... * data = malloc(sizeof(struct my_data));
my_stack_push(s1, data); // pushes the data pointer to the stack
void *data1 = my_stack_pop(s1); // pops the data pointer to the stack
...
assert(data1 == data); // data and data1 are the same
free(data1); // and data get's freed
// the memory behind both data and data1 is freed in this point
// thus the pointer s1.first->node->data is invalid
// as the code runs in loop, effectively all the data in this stack are invalid
}
main()
中将空闲指针加倍。 s1
和fs1
都是通过在同一文件上调用my_stack_read
获得的,因此从逻辑上讲,它们应包含相同的值。由于它们存储相同的值,因此存储的指向数据的指针相同,因此释放该指针还将释放第二个列表中的第二个指针并使该指针无效。双重释放是未定义的行为,我想应该会导致类似于正常系统上的分段错误。
while ((data1 = my_stack_pop(s1))) {
data2 = my_stack_pop(fs1);
...
assert(data1 == data2); // same pointers
free(data1);
free(data2); // double free corruption - seg fault
}
修复错误后,代码将运行并打印“所有测试已通过”,可用的实时版本为here。无论如何,以下是一些注意事项:
my_stack_init
中分配堆栈数组:
struct my_stack *my_stack_init(int size){
struct my_stack *stack = malloc(sizeof(struct my_stack_node) * size);
...
stack
现在指向size
个内存字节的sizeof(struct my_stack_node)
个计数。您只需要一个my_stack_node结构。同样,sizeof
返回size_t
。更好的版本是:
struct my_stack *my_stack_init(size_t size){
struct my_stack *stack = malloc(sizeof(struct my_stack_node));
...
my_stack_read
泄漏内存:
struct my_stack_node *aux = malloc(sizeof(struct my_stack_node));
...
aux = my_stack_pop(stackRead);
您发布的代码中的缩进有点偏离。尝试保留一种缩进样式,我可以宣传旧的'Linux kernel coding style,但您可以使用任何一致的东西。同样,您的代码也可以使用一些重组方法-限制变量的范围,或者像在else
之后删除return NULL
一样可以提高可读性。
sizeof
返回size_t
。打印size_t
的正确方法是使用"%zu"
printf修饰符。您可以通过强制转换为void*
并使用"%p"
printf修饰符来打印指针。
通常,这确实不错,但是您需要了解指针指向数据,并且指针本身就是数据(因为它们具有价值)。当前,您的实现仅存储指向数据的指针,因此,客户端代码负责释放指针。在这种实现中容易陷入混乱。可以重写您的堆栈实现,以便为节点和分配数据本身的内存,从而无需使用客户端代码以任何特殊方式处理内存。它可能看起来像:
int my_stack_push(struct my_stack *stack, void *data) {
...
// allocate memory for new link in the list
struct my_stack_node *nodeToPush = malloc(sizeof(struct my_stack_node));
if (nodeToPush == NULL) abort();
// allocate memory for data itself
nodeToPush->data = malloc(stack->size);
if (nodeToPush->data == NULL) abort();
// memory copy the data into the pointer
memcpy(nodeToPush->data, data, stack->size);
..
在这种实现中,堆栈负责释放指针,并且仅存储数据的副本。因此,所有句柄都需要重写以支持它。数据大小可通过stack->size
获得,并在my_stack_init
中使用参数进行初始化。
stackNode->next
的值确实未在my_stack_read
中使用,因为它的值之前已经被释放。我不明白为什么如果您从不使用stackNode->next
的值,为什么甚至将int my_stack_write(struct my_stack *stack, char *filename){
...
node = stack->first;
void *data = node->data;
// write the data behind the node to the file
if (fwrite(data, stack->size, 1, file) != 1) {
return -100;
}
}
写入文件。因此我们可以将数据本身存储在文件中
my_stack_read
以类似的方式,我们可以重写struct my_stack *my_stack_read(char *filename, size_t size) {
...
struct my_stack *stack = stack_init(size);
...
void *newdata = malloc(stack->size);
while (fread(newdata, stack->size, 1, file)) {
my_stack_push(stack, newdata);
}
free(newdata); // as in my proposed implementation my_stack stores copy
// of the memory behind the pointers, we can safely manage own memory
// by freeing the pointer
}
:
{{1}}