附加列表中指针的指针追加

时间:2013-03-06 10:12:50

标签: c list pointers

我通常在python中编程。为了提高我的模拟性能,我正在学习C.在向链表实现追加函数时,我有一个问题需要理解指针指针的使用。这是我的书(Kanetkar的C语言理解)代码的摘录。

#include <stdlib.h>
#include <stdio.h>

struct node{
    int data;
    struct node *link;
};

int main(){
    struct node *p; //pointer to node structure
    p = NULL;   //linked list is empty

    append( &p,1);
    return 0;
}

append( struct node **q, int num){
    struct node *temp, *r;  //two pointers to struct node
    temp = *q;

    if(*q == NULL){
        temp = malloc(sizeof(struct node));
        temp -> data = num;
        temp -> link = NULL;
        *q = temp;
    }
    else{
        temp = *q;
        while( temp -> link != NULL)
            temp = temp -> link;
        r = malloc(sizeof(struct node));
        r -> data = num;
        r -> link = NULL;
        temp -> link = r;
    }
}

在这段代码中,我将双指针** q传递给append函数。我知道这是地址的地址,即在这种情况下为NULL的地址。

我只是没有 为什么 就像这样。从append()函数中的所有内容中删除一个*运算符并简单地将NULL的地址(即p而不是&amp; p)传递给append()函数是否无效?

我用Google搜索了这个问题。答案要么太难理解(因为我只是一个C初学者),要么太简单了。我很感谢任何提示,评论或链接,我可以在这里阅读。

7 个答案:

答案 0 :(得分:16)

当您将事物传递给C中的函数时,无论是变量还是指针,它都是原始函数的副本。

快速举例:

#include <stdio.h>
void change(char *in)
{
    // in here is just a copy of the original pointer.
    // In other words: It's a pointer pointing to "A" in our main case 
    in = "B";
    // We made our local copy point to something else, but did _not_ change what the original pointer points to.
}
void really_change(char **in)
{
    // We get a pointer-to-a-pointer copy. This one can give us the address to the original pointer.
    // We now know where the original pointer is, we can make _that one_ point to something else.
    *in = "B";
}
int main(int argc, char *argv[])
{
    char *a = "A";
    change(a);
    printf("%s\n", a); /* Will print A */
    really_change(&a);
    printf("%s\n", a); /* Will print B */
    return 0;
}

因此,对change()的第一个函数调用会传递一个指向地址的指针的副本。当我们执行in = "B"时,我们只更改我们传递的指针的副本。

在第二个函数调用really_change()中,我们传递了一个指向指针的副本。这个指针包含我们原始指针的地址和瞧,我们现在可以引用原始指针并改变原始指针应指向的位置。

希望它能解释得更多:)

答案 1 :(得分:6)

首先它不是“地址的地址”。它是指针变量的地址。例如:如果您传递包含零的int变量n的地址,则表示您没有传递零地址;你传递的是一个变量的地址(在本例中是一个int变量,在你的情况下是一个指针变量)。变量在内存中有地址。在这种情况下,参数是恰好是指针变量的变量的地址,即列表的头部。

关于为什么这样做?简单。 C中的所有变量(通过指针衰减的数组都不能承受)通过传递。如果要通过引用(地址)修改某些内容,则需要传递的“值”必须是地址,接收它的形式参数必须是指针类型。简而言之,您将“值”传递给内存地址而不仅仅是基本的缩放器值。然后,该函数使用它(通过正式指针参数)来相应地存储数据。可以把它想象成“把我想要的东西放在”这个“记忆地址”。

作为一个简短示例,假设您想要运行一个文件,将每个字符附加到 转发 链接的节点列表中。你使用你所拥有的追加方法(见The Painter's Algorithm为什么)。看看你是否可以遵循这个代码,它使用指向指针,但没有函数调用。

typedef struct node
{
    char ch;
    struct node *next;
} node;


node *loadFile(const char *fname)
{
    node *head = NULL, **next = &head;
    FILE *fp = fopen(fname, "r");
    if (fp)
    {
        int ch;
        while ((ch = fgetc(fp)) != EOF)
        {
            node *p = malloc(sizeof(*p));
            p->ch = ch;
            *next = p;
            next = &p->next;
        }
        *next = NULL;
        fclose(fp);
    }
    return head;
}

盯着那一段时间看看你是否能理解如何使用指向指针next来始终填充要添加到列表中的下一个链接节点,从头节点开始。

答案 2 :(得分:3)

你需要这样做才能使函数能够分配内存。简化代码:

main()
{
  void *p;
  p = NULL;
  funcA(&p);

  int i;
  i = 0;
  funcB(&i);
}

funcA(void **q)
{
  *q = malloc(sizeof(void*)*10);
}

funcB(int *j)
{
  *j = 1;
}

此代码以这种方式完成,因此子函数funcA可以分配p指针。首先,将void* p视为int i。您通过p = NULL执行的操作与int i = 0类似。现在,如果您通过&i,但未传递0的地址,则会传递i的地址。传递指针地址的&p也会发生同样的事情。

现在在funcA中,你想要进行分配,所以你使用malloc但是如果你做q = malloc(...而q在主函数p中已经void* q就不会有funcB被分配了。为什么?想想*j = 1,j保存i的地址,如果你想修改i,那么你会j = 1,因为如果你做<type of p>* q那么你会把j指向另一个内存区域而不是i。对于funcA,它与q相同。把它想象为*q它是指向p的类型的指针,它是void *,但是在funcB的情况下,它是一个int。现在你要修改p指向的地址,这意味着你不想修改q所指向的地址,你想要修改指向q的指针地址,又名p或{ {1}}。

如果尚不清楚。试着想一下箱子。我已经用所涉及框的funcA绘制了一个快速示例。每个框都有一个名称(在框内),此框位于任意地址的进程的虚拟内存中,每个框都包含一个值。在此视图中,我们处于调用funcA(&p)并且将完成malloc的状态。

enter image description here

答案 3 :(得分:2)

嘿,为什么你以这种方式思考它,想想当有人传递结构来追加函数时,在这种情况下,你的案例中的整个结构struct node{int data; struct node *link; };将被复制到append function的堆栈框架上,所以最好传递结构指针的地址,以便只将4个字节复制到堆栈上。

答案 4 :(得分:2)

你不需要if / else;在这两种情况下,您都会将新节点链接到操作前为NULL的指针。这可以是根节点,也可以是链中最后一个节点的 - &gt;下一个节点。两者都是struct node的指针,你需要一个指针指向这些指针才能分配给它们。

void append( struct node **q, int num){

    while (*q){ q = &(*q)->link; }

    *q = malloc(sizeof **q);
    (*q)->data = num;
    (*q)->link = NULL;

}

为什么有人会这样做?基本上因为它更短,它只使用一个循环而没有附加条件,不使用其他变量,并且可以证明它是正确的。 当然应该为malloc的结果添加一个测试,这需要一个额外的条件。

答案 5 :(得分:1)

本质上,正如Jite&amp;其他人说的是对的。

每当您想要对C中的数据结构应用更改(由另一个函数执行更改)时,您需要将“引用”传递给此数据结构,以便更改在change()函数完成之后保持不变。这也是在Python中发生的事情,除非您明确地复制,否则将对对象的引用传递给对象。在C中,您必须精确指定要执行的操作。为了简化它,它可以是:

输入data_struct

change(data_struct)=&gt;这是我的data_struct的副本,进行更改,但我不关心调用函数有关您应用的更改

更改(&amp; data_struct)=&gt;这是我的data_struct的地址(“引用”),应用您的更改,调用函数将在应用后看到此更改。

现在,根据原始“类型”的不同,您可能有*或**。然而,请记住,你可以拥有多少“间接”,但不确定天气是系统或编译器确定的,如果有人对我是一个接受者有答案。我从来没有超过3个间接。

答案 6 :(得分:0)

我认为原因如下:

struct node * p; //指向节点结构的指针     p = NULL;

在主块中写入上面的代码片段,表示指针p的值为NULL,因此它不指向内存中的任何内容。所以我们传递指针p的地址,以便创建一个新节点,并将新节点的地址分配给指针p的值。

*(&amp; p)== * q == temp;

通过做* q == temp;我们实现了为指针p分配一些值的目标,这个指针最初指向了无处。