为什么指针做形参没有改变对应的值

1 引例

首先看这样一个程序,思考一下它的输出是什么:

#include <stdio.h>

void test1(int a[], int *p)
{
    p = &a[2];
}

void test2()
{
    int a[] = { 1,2,3 };
    int b = 5;
    int* p = &b;
    test1(a, p);
    printf("%d ", *p);
}

int main(void)
{
	test2();
}

你可能会以为,程序的输出是3,因为a[2] = 3。然而,事实是它会输出 5。在test1()中,我们传递的是指针变量,为什么还会出现这样的结果呢?

2 分析

2.1 为什么会这样

其实,在C语言中,实际参数是通过值传递的。也就是说,形参是实参的拷贝值。
函数调用时,会在栈中开辟空间,创建一个变量(即形参)存放实参的值,你可以认为,这两个变量只是值相同,其他毫无关联。就像int j = k = 1;一样,jk只是都是值为1。
我们可以把程序稍作改动,来验证上面的说法。

void test1(int a[], int *p)
{
    printf("%p\n", &p);		/*二*/
    printf("%d\n", *p);
    p = &a[2];
}

void test2()
{
    int a[] = { 1,2,3 };
    int b = 5;
    int* p = &b;
    printf("%p\n", &p);		/*一*/
    printf("%d\n", *p);
    test1(a, p);
    printf("%d ", *p);
}

int main(void)
{
	test2();
}

运行程序,结果为:

004FFA48
5
004FF974
5
5 

可以看出,在处,p的地址为004FFA48,而在处,p的地址为004FF974,这便说明了,这两个p不是同一个p。用图示的方法可能更好理解:
进入test2()后,我们手动定义了变量p,并使它指向了b,调用test1()时,程序自动定义了变量P',也使它指向了b。
为什么指针做形参没有改变对应的值
test1()中,我们改变了`P’的指向。
为什么指针做形参没有改变对应的值

test1()执行结束以后,P'被释放。而P的指向一直没有改变。

2.2 怎么改

知道原因以后,又该怎么更改程序,达到我们想要的效果呢?这里,我们用到了双重指针。直接看程序:

void test1(int a[], int **p)
{
    printf("%p\n", &p);		/*二*/
    printf("%p\n", &*p);		/*三*/
    *p = &a[2];		/*四 要是想修改b的值,只需要把这里改为 **p = a[2];*/
}

void test2()
{
    int a[] = { 1,2,3 };
    int b = 5;
    int* p = &b;
    printf("%p\n", &p);		/*一*/
    printf("%d\n", *p);
    test1(a, &p);
    printf("%d ", *p);
}

int main(void)
{
	test2();
}

运行程序:

0095FB84
5
0095FAB0
0095FB84
3    

从运行结果可以看出,处和处的输出是一样的,所以可以达到想要的效果。同样的,用图示的方法来理解:
进入test2(),定义P指向b,运行test1(),创建临时变量P'指向P,可以理解为int **P' = &P.
为什么指针做形参没有改变对应的值
test1()中,*p = &a[2];中,*p即是*p',也就是P的值,所以*p = &a[2];等价于P = %a[2];
为什么指针做形参没有改变对应的值
test1()执行结束以后,P'被释放,*P变为了3.

3 为什么会出错

为什么会“理所当然”地犯下引例中的错误?源头可能是来自数组做形参时的引导。再来看一个例子:

#include <stdio.h>

void test3(int a[])
{
    a[2] = 5;
}

void test4()
{
    int a[] = { 1,2,3 };
    test3(a);
    printf("%d\n", a[2]);
}

int main()
{
    test4();
}

没有那么多套路,输出结果确实是 5。数组名也是指针,为什么它就可以呢?它不是值传递吗?
答案是,它也是值传递,不过,这里的情况和前面的例子稍有不同。这里再写一个例子,读者可以结合下面的例子,按照前面的创建P'和画图的方法,自行思考原因。

#include <stdio.h>

void test4()
{
    int a[] = { 1,2,3 };
    int* b = a;
    b[2] = 5;
    printf("%d\n", a[2]);
}

int main()
{
    test4();
}
上一篇:Linux-rsync命令用法详解


下一篇:Linux命令-2