[英]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指向的地址,也就是*q
或p
。
如果还不清楚。 试着想到盒子。 我用涉及的盒子的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.