双指针vs单指针

有人可以向我解释/给出一个推理,为什么变量i在主函数中的值在下面的代码片段中不会通过函数test1改变,而它确实通过test2改变了? 我认为单个指针应该足以改变i的值。 为什么我们应该使用双指针?

#include  void test1(int* pp) { int myVar = 9999; pp = &myVar; } void test2(int** pp) { int myVar = 9999; *pp = &myVar; } int main() { printf("Hej\n"); int i=1234; int* p1; p1 = &i; test1(p1); printf("does not change..., p1=%d\n",*p1); test2(&p1); printf("changes..., p1=%d\n",*p1); return 0; } 

在C中,参数按值传递。 这意味着在test1当你传递pp ,副本由指针组成,当你更改它时,对副本而不是指针本身进行更改。 对于test2 ,副本是双指针,但是当你取消引用并在此处分配时

 *pp = &myVar; 

你正在改变所指向的东西,而不是改变pp本身。 请注意, test2中的此行为未定义,如此处的其他一些答案中所述

如果要更改T类型变量的值,则必须在该类型( T* )上使用指针。 由于您想要更改指针( T = int* ),您必须提供指向指针的指针( T* = int** ),否则您只需要更改副本。

注意

 int myVar = 9999; *pp = &myVar; 

将导致未定义的行为,因为pp现在将包含退出函数后无效的局部变量的地址。

但是你没有改变i的值,你正在改变pp指向的地址,如果你只想改变i的值,那么它足以将你的测试更改为:

 void test3(int* pp) { int myVar = 9999; *pp = myVar; } 

因为f(x)不能改变x的值,无论x是int,float还是指针。

 void test1(int* pp) { int myVar = 9999; pp = &myVar; } 

这个函数传递一个指针pp 。 该函数修改该指针。 但由于参数是按值传递的,因此调用者无法看到该修改。

你需要写这样的函数:

 void test1(int* pp) { *pp = 9999; } 

期望此函数的调用者传递int变量的地址。 然后,该函数将int值(在这种情况下为9999)写入该地址。 这是关键。 您传递了一个地址,然后您将值写入该地址。 你破碎的版本只是修改了地址。 你错过了间接。

当函数返回时,调用者可以观察对其地址传递给函数的int变量的修改。 调用代码可能如下所示:

 int i = 0; test1(&i); // pass address of int variable printf("%d\n", i); // prints the new value, 9999 

 void test2(int** pp) { int myVar = 9999; *pp = &myVar; } 

现在,这是以非常严肃的方式打破的。 这个函数确实返回了一个指向调用者的指针。 但是一旦函数返回,它就会返回一个指向超出范围的对象的指针。 这被称为未定义的行为 。 不要这样做。 永远不要传递一个函数,一个指向该函数中定义的局部变量的指针。

pp = &myVar;myVar的地址分配给指针pp 。 如果要更改pp指向的值,请使用

 *pp = myVar; 

代替。

在回答第二个问题时,如果要更改指向的对象而不是更改现有对象的值,请将指针传递给指针。

 int* p1; p1 = &i; test1(p1); //1st test2(&p1); //2nd 

简单来说,第1个是pass by value ,第2个是pass by address

说明:

在第一种情况下,传递指针实际上是test1内部的p的副本,因此test1pp是该函数的本地,并且它是在test1创建的。 你分配了地址,当function失效时,它就被破坏了。

但在第二种情况下,您将指针的地址传递给test2函数。 因此test2的指针pp将指向main中的指针p ,因此使用*pp = &myVarpp分配新地址将自动设置p的值(因为您正在取消引用指针)。 因此,当test2终止时, p将指向修改的位置。