我一直在用C编写一个简单的堆栈:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// Crude stack.
typedef struct Stack {
int size;
void **top;
} Stack;
Stack *stack_new() {
Stack *stack = malloc(sizeof(Stack));
stack->size = 0;
stack->top = 0;
return stack;
}
void stack_free(Stack *stack) {
if (stack->size > 0) {
free(stack->top);
}
free(stack);
}
void stack_push(Stack *stack, void *item) {
stack->size++;
// We copy the existing stack to a new place in the heap, then
// write the pointer to ITEM to the top of the stack.
void *old_top = stack->top;
void *new_top = malloc(stack->size);
memcpy(new_top + sizeof(void*), stack->top, (stack->size - 1) * sizeof(void *));
stack->top = new_top;
free(old_top);
*stack->top = item;
}
void *stack_pop(Stack *stack) {
if (stack->size < 1) {
printf("WARNING: Popping of empty stack!\n");
}
stack->size--;
void *old_top = stack->top;
void *item = *stack->top;
void *new_top = malloc(stack->size);
memcpy(new_top, stack->top + sizeof(void *), stack->size * sizeof(void *));
stack->top = new_top;
free(old_top);
return item;
}
int main() {
Stack *stack = stack_new();
stack_push(stack, "foo");
stack_push(stack, "bar");
stack_push(stack, "baz");
printf("top of stack: %s\n", (char *)stack_pop(stack));
printf("top of stack: %s\n", (char *)stack_pop(stack));
printf("top of stack: %s\n", (char *)stack_pop(stack));
stack_free(stack);
return 0;
}
但是,弹出堆栈后访问值只会给我一个空指针:
$ gcc -Wall stack.c
$ ./a.out
top of stack: baz
top of stack: (null)
top of stack: (null)
我做错了什么?它编译时没有任何警告。
答案 0 :(得分:2)
推送(和弹出)堆栈的代码是错误的。此外,我在阅读您的代码时感到困惑,因为top
实际上代表了堆栈元素的数组,而不仅仅是顶部项目。
特别是,malloc
需要多个字节,但是你已经传递了许多元素。当你的意思是void *
时,使用void **
的风格很差。
我建议您使用[]
表示法并重命名top
,使代码更具可读性:
void **new_content = malloc( (stack->size + 1) * sizeof *new_content);
if ( !new_content)
// error handling...
if ( stack->size > 0 )
memcpy( &new_content[1], &stack->content[0], stack->size * sizeof *new_content );
new_content[0] = item;
free(stack->content);
stack->content = new_content;
++stack_size;
现在,就效率而言,这实际上是实现堆栈的最糟糕方式。每次推送和弹出操作都必须进行分配并复制整个堆栈。如果你实际上让你的堆栈有&#34;顶部&#34;最后,事情变得更加简单:
void **new_content = realloc( stack->content, (stack->size + 1) * sizeof *new_content );
if ( !new_content )
// error handling...
new_content[stack->size++] = item;
stack->content = new_content;
类似的评论适用于您的pop
功能。
答案 1 :(得分:1)
在stack_push()
:
void *new_top = malloc(stack->size);
memcpy(new_top + sizeof(void*), stack->top, (stack->size - 1) * sizeof(void *));
这有几个问题:
stack->size
个项目的数量乘以stack->top
点的大小。memcpy()
为0时,不得调用stack->top
。如果复制到空指针或从空指针复制,则行为未定义。void *
上执行指针运算。这是一个编译器扩展(例如gcc),在标准C中是不可能的。解决这些问题的最简单方法是将new_top
声明为与stack->top
相同的类型:
void **old_top = stack->top;
void **new_top = malloc(stack->size * sizeof *new_top);
if (stack->top) {
memcpy (new_top + 1, stack->top, (stack->size - 1) * sizeof *new_top);
}
stack->top = new_top;
同样,在stack_pop()
:
void **old_top = stack->top;
void *item = *stack->top;
void **new_top = malloc(stack->size * sizeof *new_top);
if (stack->size) {
memcpy(new_top, stack->top + 1, stack->size * sizeof *new_top);
}
stack->top = new_top;
答案 2 :(得分:0)
除了代码中的许多其他问题之外,快速修复&#34;是分配正确的内存量:
void *new_top = malloc(stack->size * sizeof(void*));
使用char*
作为基本指针类型来修复指针算法:
// in stack_push()
memcpy((char*)new_top + sizeof(void*), stack->top, (stack->size - 1) * sizeof(void *));
// in stack_pop
memcpy(new_top, (char*)stack->top + sizeof(void *), stack->size * sizeof(void *));