【问题标题】:Why use a pointer to a pointer to the stack when creating a push function?为什么在创建 push 函数时使用指向堆栈指针的指针?
【发布时间】:2010-12-19 14:20:18
【问题描述】:

我正在看一个实现堆栈的链接列表的教科书示例。我不明白为什么推送操作需要使用指向堆栈的指针的指针。请参阅以下示例:

bool push( Element **stack, void *data)
{
    Element *elem = new Element;
    if(!elem) return false;

    elem->data = data;
    elem->next = *stack;
    *stack = elem;
    return true;
}

如果有人能帮助澄清为什么 push 方法的第一个参数是指向指针的指针,我将不胜感激。谢谢。

太棒了,感谢您提供的所有出色帮助。

【问题讨论】:

  • C 没有新的关键字。 C++ 可以。也就是说,C++ 在分配内存失败时会抛出异常,因此无需检查 null。
  • 另外请记住,您可以使用引用而不是指针来让编译器为您完成繁琐的工作。

标签: c++ pointers linked-list stack


【解决方案1】:

函数需要修改Element指针的值,所以需要一个指向那个指针的指针。

换一种说法:当一个函数需要修改某个东西时,它会获取一个指针。

在这种情况下,那个东西本身就是一个指针。所以函数最终得到一个指向指针的指针。

【讨论】:

  • 好吧...如果我错了请纠正我:如果我们只是想在链表的末尾添加一些东西(不是推送函数,只是一个 addItem 函数),我们不需要指向列表指针的指针?
  • 通常,您会有一个指向最后一个元素的指针,其中包含一个您想要更改的带有 NULL 值的指针。所以,是的,你只需要一个指向最后一个元素的指针。
  • 嗯.. 在这种情况下,您的 **stack 指针用于跟踪列表末尾的位置。如果您不需要跟踪这样的值,那么不,在将元素添加到链接列表时,您根本不需要担心它。您可以想象只使用指向列表本身的指针。
  • .. 但是如果你要调用一个重复添加到列表末尾的函数,那么你需要一些其他的方法来知道末尾在哪里。您可以遍历每个函数调用的列表以查找最后一个元素;但保留指向列表末尾的指针可能更方便。
  • 这是一个堆栈,而不是一个列表。您通常只与堆栈的头部交互,而不是另一端。
【解决方案2】:

你必须更新一个指针。

列表只不过是指向Element 的指针。 所以你可以把它重写为

bool push(List* stack, void* data);

你没有看到,如果你不使用双指针,你的实际声明是

bool push(List stack, void* data);

这根本不会改变原始列表。

但交替地,

bool push(Element* &stack, ...)

也可以,因为它允许您更新。

【讨论】:

    【解决方案3】:

    指针只是一个保存的变量,该值是内存地址。

    指向指针的指针也只是一个保存值的变量。该值是指针的内存地址。

    当您想更改指针的时,您使用指向指针的指针。

    //Not a very useful example, but shows what I mean...
    void getOffsetBy3Pointer(const char *pInput, char **pOutput)
    {
      *pOutput = pInput + 3;
    }
    

    你这样调用这个函数:

    const char *p = "hi you";
    char *pYou;
    getOffsetBy3Pointer(p, &pYou);
    assert(!stricmp(pYou, "you"));
    

    现在考虑如果我们尝试用单个指针来实现这个函数会发生什么。

    //Note: This is completely wrong
    void BadGetOffsetBy3Pointer(const char *pInput, char *pOutput)
    {
      //*pOutput refers to the first actual char element that pOutput points to.
      pOutput = pInput + 3;
      //pOutput now points to pInput + 3, but the variable we passed in remains distinct.
    }
    

    你这样调用这个函数:

    const char *p = "hi you";
    char *pYou = NULL;
    BadGetOffsetBy3Pointer(p, pYou);
    assert(pYou == NULL);
    

    请注意,在 BadGetOffsetBy3Pointer 中,我们可以更改某些字符,但无法更改 pYou 指向的内容。

    【讨论】:

      【解决方案4】:

      堆栈由指向最后一个被压入它的元素的指针表示。为了通过压入一个元素来改变堆栈,这个指针必须被更新,所以我们将一个指向它的指针传递给 push 函数。

      【讨论】:

        【解决方案5】:

        看看有人会如何使用这个推送功能:

        Data d1, d2;
        
        Stack *s = null; // s := null
        push(&s, &d1); // s := {d1}->null
        push(&s, &d2); // s:= {d2}->{d1}->null
        

        现在可以继续使用变量s了,已经被push修改为指向增长栈顶。

        在堆栈中,你总是希望维护一个指向顶部的指针,而 push 函数使维护这个指针变得更加容易。

        【讨论】:

          【解决方案6】:

          栈基本上是一个指针的链表。每个指向它下面的一个。 因为您有一个新元素,并且您希望该元素在列表中排在第一位(因此使用术语“堆栈”,您必须更改指向列表开头的位置。

          要更改“指向列表头的指针”中的值,您需要该地址 指针。

          【讨论】:

            【解决方案7】:

            因为这条线:

                *stack = elem;
            

            基本上你是在修改函数内部的原始指针。

            【讨论】:

              猜你喜欢
              • 2019-11-23
              • 2019-08-27
              • 2023-03-12
              • 2020-01-03
              • 2012-10-07
              • 2018-07-11
              • 1970-01-01
              • 2020-03-09
              • 1970-01-01
              相关资源
              最近更新 更多