如何实现 returns "popped" 元素(即 data/value)的 'Pop' 函数? (链表栈)

How to implement a 'Pop' function that returns the "popped" element (i.e the data/value) ? (linked list stacks)

对于如何实现同时弹出元素和 return 将其作为 return 值的单个函数感到困惑。

到目前为止,我所看到的都是 return 指向新栈头的指针的弹出函数。


这是一个开始,但是...

#define VALUE int

typedef struct node_t {
    VALUE item;
    struct node_t *next;
} node;

.
.
.

// Function
VALUE pop(node *stack_head) {

    // Used to store the node we will delete
    node *deleteNode = stack_head;

    // Error Checking        //  <<====== (btw, is this actually necessary ?)
    if (!deleteNode || !stack_head) {

        if (!stack_head) fprintf(stderr, "\nPop failed. --> ...\n");
        if (!deleteNode) fprintf(stderr, "\nPop Failed. --> ...\n");
        return 0;
    }

    // Storing the value in a variable
    VALUE popped_item = stack_head->item;

    // Updating the head
    stack_head = stack_head->next;    <<====== THERE'S A PROBLEM HERE ! (i think)

    // Freeing/Deleting the 'popped' node
    free(deleteNode);

    // Return 'popped' value
    return popped_item;
}

。 . .

stack_head = stack_head->next;

实际上并没有改变指针stack_head(即堆栈的头部)指向的地址......所以这个值确实是 returned 对于第一个弹出但是随后弹出 return 个错误。

是的,因为它是一个局部变量,但是您如何更改实际指针(指向堆栈头部的指针)以指向新的堆栈头部?

参数 stack_head 是函数 pop 的局部参数,因此当您修改它时,结果在函数外部不可见。

您需要传递要修改的变量的地址,然后在函数中取消引用指针参数以更改它指向的内容。

所以把你的函数改成这样:

VALUE pop(node **stack_head) {

    node *deleteNode = *stack_head;

    if (!*stack_head) {
        fprintf(stderr, "\nPop failed. --> ...\n");
        return 0;
    }

    VALUE popped_item = (*stack_head)->item;

    *stack_head = (*stack_head)->next;

    free(deleteNode);
    return popped_item;
}

并这样称呼它:

node *stack_head = NULL;
// do something to push onto the stack
VALUE v = pop(&stack_head);

好的,这将是一个相当长的摘要,但希望值得。您可以看到我作为结论 here and obtain a modular version of the code here 提供的代码的测试用例。我的建议是您使用这样的结构:

struct {
    size_t top;
    T value[];
}

你可能不应该使用 经典链表 的原因(或者 任何东西 ,真的)在 this video courtesy of Bjarne Stroustrup.问题的基础是你的大部分开销都在 allocationcache misses 中,当你保持 一次分配中的所有内容

如果我为了方便使用而写这个,也许:

#define stack_of(T) struct { size_t top; T value[]; }

这应该允许您相当明智地声明空堆栈,例如:

int main(void) {
    stack_of(int) *fubar = NULL;
}

这对于其他语言中的模板来说足够熟悉,可以很好地工作,而且也不是对预处理器的可怕滥用。我确定我已经写了一个 push_back 函数 somewhere which we can adapt to this version of push 我已经链接到外部,因为它对于这个答案的结论并不重要(在这里耐心等待;我们会回到那个暂时)...

所以现在我们有 stack_of(T)push(list, value),我们可以像这样使用它们:

int main(void) {
    stack_of(int) *fubar = NULL;
    push(fubar, 42);
    push(fubar, -1);
}

pop 的最简单解决方案可能类似于:

#define pop(list) (assert(list && list->top), list->value[--list->top]))

...但这确实有一个缺点,我们稍后会讨论。现在我们有一个测试用例:

int main(void) {
    stack_of(int) *fubar = NULL;
    int x;
    push(fubar, 42);
    push(fubar, -1);
    x = pop(fubar); printf("popped: %d\n", x);
    x = pop(fubar); printf("popped: %d\n", x);
    x = pop(fubar); printf("popped: %d\n", x);
}

...正如您将在调试期间看到的那样,assert 在执行期间失败,告诉我们弹出的数量多于推送的数量...可能是一件好事。尽管如此,这实际上并没有减少堆栈的大小。为此,我们实际上还需要更像 push 的东西,只是我们要去掉这些行:

    list->top = y;                  \
    list->value[x] = v;             \

所以有重构的机会。因此我给你带来 operate():

#define operate(list, ...) {                                         \
    size_t x = list ? list->top : 0                                  \
         , y = x + 1;                                                \
    if ((x & y) == 0) {                                              \
        void *temp = realloc(list, sizeof *list                      \
                                 + (x + y) * sizeof list->value[0]); \
        if (!temp)                                                   \
            return EXIT_FAILURE;                                     \
        list = temp;                                                 \
    }                                                                \
    __VA_ARGS__;                                                     \
}

现在我们可以根据 operate 重新定义 push:

#define push(list, v) operate(list, list->value[x] = v; list->top = y)

... 和 pop 看起来有点像以前那样,但是最后调用 operate 导致 list 缩小(从原来的四倍大小,例如,当您从 4) 的列表中弹出 3 个元素时,使其大小不超过其大小的两倍。

#define pop(list) (assert(list && list->top), list->value[--list->top]); \
                  operate(list, )

总而言之,您可以看到我提供的代码的测试用例 here and obtain a modular version of the code here...