简体   繁体   English

链表的堆栈实现中的双指针

[英]Double pointers in stack implementation of linked list

I was reading a book recently and this is the explanation for implementing stack in Linked list. 我最近在读一本书,这是在链接列表中实现堆栈的说明。 Here follows the explanation: 以下是说明:

typedef struct Element { 
    struct Element *next; 
    void *data;
} Element;

The corresponding prototypes for push and pop follow: 对应于push和pop的原型如下:

void push( Element *stack, void *data );
void *pop( Element *stack );

Now consider what happens in these routines in terms of proper functionality and error handling. 现在,从适当的功能和错误处理方面考虑这些例程中发生了什么。 Both operations change the first element of the list. 这两个操作都会更改列表的第一个元素。 The calling routine's stack pointer must be modified to reflect this change, but any change you make to the pointer that is passed to these functions won't be propagated back to the calling routine. 必须修改调用例程的堆栈指针以反映此更改,但是您对传递给这些函数的指针所做的任何更改都不会传播回调用例程。 You can solve this problem by having both routines take a pointer to a pointer to the stack. 您可以通过使两个例程都使用指向堆栈的指针来解决此问题。 This way, you can change the calling routine's pointer so that it continues to point at the first element of the list. 这样,您可以更改调用例程的指针,使其继续指向列表的第一个元素。 Implementing this change results in the following: 实施此更改将导致以下结果:

void push( Element **stack, void *data );
void *pop( Element **stack );

However, what I wonder is, what is the need to put double pointers for the stack? 但是,我想知道的是,需要为堆栈放置双指针吗? I understand the concept of double pointers, but however, when a new node is created using Element *node1 = (Element *) malloc (sizeof(Element)); 我了解双指针的概念,但是,当使用Element *node1 = (Element *) malloc (sizeof(Element));创建新节点时Element *node1 = (Element *) malloc (sizeof(Element)); , we already have the pointer to the node. ,我们已经有了指向该节点的指针。 Why not just send this pointer itself instead of using double pointer? 为什么不直接发送此指针而不是使用双指针呢?

Because you don't have a standalone Stack object, every time you change the top node in the stack, the primary reference to that stack needs to change for everything using it. 因为没有独立的Stack对象,所以每次更改堆栈的顶部节点时,对于使用该堆栈的所有内容,都需要更改对该堆栈的主要引用。 This is why the double pointer is needed -- because the old top of the stack isn't valid anymore. 这就是为什么需要双指针的原因-因为堆栈的旧顶部不再有效。

If you created a top-level stack object, which has a pointer to an Element and maybe some other data like a element count, then you would be able to send that in to push/pop as a single pointer because that object/struct would remain the same, only the data inside it would change. 如果您创建了一个顶层堆栈对象,该对象具有指向Element的指针以及其他一些数据(例如element count),则您可以将其作为单个指针发送到push / pop中,因为该对象/结构可以保持不变,只是其中的数据会改变。

// pseudocode
struct Stack {
  Element * top = NULL;
  int size = 0;
}

struct Element {
  Element * next;
  void * data;
}

function push(Stack * stack, void * data) {
  Element * element = new Element(data);
  element->next = stack->top;
  stack->top = element;
  size++;
}

// pop left as an exercise to the reader.. :)

int main(void*) {
    Stack * stack = new Stack;
    push(stack, "foo"); <== no need for double pointer
    pop(stack); 
    printf("%d\n", stack->size);
}

This is a bit confusing due to a common mix-up of concepts of pointers and passing by reference . 由于pointers概念和passing by reference的常见混淆,这有点令人困惑。

Let's take look at the original push function. 让我们看一下原始的push功能。 The stack parameter represents a pointer to the beginning of the stack. stack参数表示指向堆栈开头的指针。 This pointer is provided by the caller. 该指针由调用者提供。 If the beginning of the stack needs to be modified, the push function won't be able to do this because it was passed by value , ie, inside function body it's a copy of the original. 如果需要修改堆栈的开头,则push函数将无法执行此操作,因为它是通过value传递的 ,即在函数体内,它是原始副本的副本。 Same goes for pop . pop

It's easiest to understand with an example. 用一个例子最容易理解。 Let's say the beginning of the stack resides at address 0x1234 . 假设堆栈的开头位于地址0x1234 When push is being called, the value 0x1234 is passed to it by the caller. 调用push ,调用者将 0x1234传递给它。 push can do whatever it desires with this value, but it won't change the data of the original pointer and the changes won't be reflected at the caller context. push可以使用此值执行任何所需的操作,但是它不会更改原始指针的数据,并且更改不会反映在调用者上下文中。

Why not send the node1 pointer that you've created inside push : Element *node1 = (Element *) malloc (sizeof(Element)); 为什么不发送在push内部创建的node1指针: Element *node1 = (Element *) malloc (sizeof(Element)); ? That's exactly what you should do but you still need double pointer: 那正是您应该做的,但是您仍然需要双指针:

void push( Element **stack, void *data )
{
    Element *node1 = (Element *) malloc (sizeof(Element));
    node1->data = data;
    node1->next = *stack;
    *stack = node1;
}

If you attempt to achieve this without double pointer, the caller won't see the changes and will remain with an old stack pointer: 如果您尝试在没有双指针的情况下实现此目标,则调用者将看不到更改,并且将保留旧的堆栈指针:

void push( Element *stack, void *data )
{
    Element *node1 = (Element *) malloc (sizeof(Element));
    node1->data = data;
    node1->next = stack;
    stack = node1;         //The data structure was updated but caller won't see it!
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM