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

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

#include  #include  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()函数是否无效?

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

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

快速举例:

 #include  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() ,我们传递一个指向指针的副本。 这个指针包含我们原始指针的地址和瞧,我们现在可以引用原始指针并改变原始指针应指向的位置。

希望它能解释得多:)

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

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

作为一个简短示例,假设您想要运行一个文件,将每个字符附加到前向链接的节点列表中。 你不会使用像你那样的追加方法(参见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用于始终填充要添加到列表的下一个链接节点,从头节点开始。

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

 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 = NULLint i = 0类似。 现在如果你传递&i你没有传递地址0 ,你传递i的地址。 使用&p传递指针的地址也会发生同样的事情。

现在在funcA中,你想要进行分配,所以你要使用malloc但是如果你要做q = malloc(...和q将在主函数p中void* q将不会被分配。为什么?想想funcB ,j保存i的地址,如果你想修改i,你会做*j = 1 ,因为如果你做j = 1那么你就会把j指向另一个内存区而不是i。它是与funcA的q相同。将其视为* 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 node的指针 ,你需要一个指向这些指针的指针才能分配给它们。

 void append( struct node **q, int num){ while (*q){ q = &(*q)->link; } *q = malloc(sizeof **q); (*q)->data = num; (*q)->link = NULL; } 

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

从本质上讲,正如Jite和其他人所说的那样是正确的。

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

输入data_struct

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

要么

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

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

我认为理由如下:

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

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

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

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