为什么我的堆栈返回null?

时间:2014-07-22 22:41:08

标签: c stack

我一直在用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)

我做错了什么?它编译时没有任何警告。

3 个答案:

答案 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 *));