C中的链表列表

时间:2012-11-05 21:15:15

标签: c linked-list stack

我正在阅读“编程访谈暴露”一书中的以下段落,参考C中链表列表的实现:

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

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

现在考虑一下这些例程中会发生什么   功能和错误处理。两个操作都改变了第一个   列表的元素。调用例程的堆栈指针必须是   修改以反映此更改,但您所做的任何更改   传递给这些函数的指针不会传播回   调用程序。你可以通过兼顾两者来解决这个问题   例程采用指向堆栈的指针。这样,你就可以   更改调用例程的指针,使其继续指向   列表的第一个元素。实施此更改会导致   以下内容:

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

有人可以解释为什么我们需要在这里使用双指针?我对提供的解释有点不确定。

4 个答案:

答案 0 :(得分:3)

它类似于C中着名的swap()函数。

案例1:

void swapFails(int x, int y) {
    int temp = x;
    x = y;
    y = temp;
}

案例2:

void swapOk(int *x, int *y) {
    int temp = *x;
    *x = *y;
    *y = temp;
}

我们调用这样的交换:

int x = 10;
int y = 20;

案例1:

swapFails(x, y);

案例2:

swapOk(&x, &y);

请记住,我们想要更改x和y的值。对于CHANGING数据类型的值,我们需要指针。把它提升到指标的下一个级别。对于CHANGING指针的值,我们需要双指针。

对于使用链表的堆栈: 如果您按下值10,20和30,它们将按如下方式存储:

top --- bottom
30 -> 20 -> 10

因此,每当您从堆栈中推送或弹出值(链接列表)时,您都会看到链接列表的顶部或第一个节点发生更改。因此你需要双指针。

答案 1 :(得分:1)

第一个版本发送copy指针,如果在函数内部发生了更改,那么当函数返回调用者<时,本地副本只会被更改/ em>调用者仍然有一个指向地址的指针。

Element *stack =...
push (stack)
void push( Element *stack, void *data ) {
  stack = ... // this changes the local pointer allocated on the function's stack
}
//call returns
stack //still points to old memory

然而,第二个版本传递一个指向堆栈指针的指针,所以当它改变时,它会改变调用函数中的堆栈指针。

答案 2 :(得分:1)

在C中,一切都按值传递,假设我有这个函数:

void foo(void* ptr)
{
    ptr=NULL;
}

如果在main中调用此方法,则传递的指针将不为NULL(除非它已经为NULL)。因为在将指针传递给函数之前会生成指针的副本。如果要修改这是值,你必须传递一个双指针:

void foo(void** ptr)
{
    *ptr=NULL;
}

同样适用于要修改其值的堆栈。

答案 3 :(得分:1)

使用单指针签名,您可以使用以下代码设想

Element *myStack = NULL ;

.... bla bla bla ....

push(myStack, something);

然后在push调用中,您告诉堆栈实现堆栈的头元素在哪里,但是push的实现无法实现告诉你 new 头部在哪里。它无法更改myStack变量,因为C中的参数传递总是按值传递 - 即,push函数会告诉myStack的{​​em>值发生了什么是,但没有机会改变调用者的变量。

为了使事情有效,你需要告诉push原语你需要改变的局部变量的地址

....
push(&myStack, something);

由于myStack本身的类型为Element *,因此指向 myStack变量的指针的类型为Element **