繁体   English   中英

链接列表中的指针的指针追加

[英]pointer of a pointer in linked list append

我通常用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代替&p)传递给append()函数是否无效?

我已经用谷歌搜索了这个问题。 答案要么太难理解(因为我只是C初学者),要么太简单了。 感谢您可以阅读有关此内容的任何提示,评论或链接。

当您将内容传递给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"执行操作时in = "B"我们仅更改我们传递的指针副本。

在第二个函数调用really_change() ,我们传递了一个指向指针的副本。 该指针包含原始指针和地址的地址,我们现在可以引用原始指针并更改原始指针应指向的内容。

希望它能解释得更多:)

首先它不是“地址的地址”。 它是指针变量的地址。 例如:如果传递包含零的int变量n的地址,则不会传递零地址; 您正在传递变量的地址(在本例中为int变量,在本例中为指针变量)。 变量在内存中有地址。 在这种情况下,参数是碰巧是指针变量的变量的地址,即列表的开头。

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

作为一个简短的示例,假设您要遍历文件,将其中的每个字符都附加到节点的前向链接列表中。 您不会使用的那样使用附加方法(有关原因,请参见画家的算法 )。 查看是否可以遵循此代码,该代码使用指针到指针,但没有函数调用。

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 -pointer-pointer从头节点开始始终填充要添加到列表中的下一个链接节点。

您需要以这种方式使函数能够分配内存。 简化代码:

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将为void* q ,则不会分配主函数p中的void* q 。为什么? funcB ,j持有i的地址,如果您要修改i,则将执行*j = 1 ,因为如果您执行j = 1则会使j指向另一个内存区域而不是i。与funcA的q相同,将其视为<type of p>* q它是指向p类型的指针,该指针为void *,但是对于funcB,它是一个int。现在您要修改地址p为指向,这意味着您不想修改q指向的地址,而是想要修改q指向的地址,也就是*qp

如果还不清楚。 试着想到盒子。 我用涉及的盒子的funcA画了一个简单的例子。 每个框都有一个名称(在框内),该框位于进程的虚拟内存中的任意地址,并且每个框都包含一个值。 在此视图中,我们处于已调用funcA(&p)且将完成malloc的状态。

在此处输入图片说明

嘿,为什么要这样想,想想当有人通过结构来附加功能时,这种情况下整个结构struct node{int data; struct node *link; }; struct node{int data; struct node *link; }; 在您的情况下将被复制到append function堆栈框架上,因此最好传递结构指针的地址,以便仅将4个字节复制到堆栈上。

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

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

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

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

}

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

本质上,正如杰特(Jite)和其他人所说,这是正确的。

每当您希望将更改应用于C中的数据结构(由另一个函数执行的更改)时,都需要将“引用”传递给此数据结构,以使更改在change()函数完成之后仍然存在。 这也发生在Python中,除非您明确创建副本,否则您将传递对对象的引用。 在C语言中,您必须精确指定要执行的操作。 为了进一步简化,它可以是:

输入data_struct

change(data_struct)=>这是我的data_struct的副本,进行更改,但是我不在乎调用方函数中所应用的更改

要么

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

现在,根据原始“类型”的不同,您可能会有*或**。 不过,请记住,您可以拥有多少个“间接”是有限制的,不确定是否由系统或编译器确定,如果有人能回答我是接受者。 我从来没有超过3个间接。

我认为原因如下:

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

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

*(&p)== * q ==温度;

通过做* q == temp; 我们实现了为指针p分配一些值的目标,而指针p最初没有指向任何地方。

暂无
暂无

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

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