题目是什么意思呢?
就是一个函数的输入参数是一个指针,该函数需要改变该指针指向的地址,如: 现在有一个全局数组b,现在需要编写一个函数 输入参数是一个指针a,需要通过该函数将该指针a指向数组b,即:
int b[3] = {1,2,3};
void fcn(参数);
void main()
{
int* a;
fcn(输入参数a)
}
执行完fcn后,使参数的地址改变,这个功能怎么来实现呢?
首先说明结论:使用二级指针。
为了更好的理解这个问题,我们首先来学习一下指针最经典的例子,交换两个数来说明函数的形参和实参之间的关系。
首先来探究以下实参和形参的关系是怎样的。
形参为普通变量类型;
void test1(int a, int b)
{
printf(">> formal addr a: %d, formal addr b: %d\n",&a,&b); //打印形参地址
printf(">> formal value a: %d, formal value b: %d\n",a,b); //打印形参值
}
int main()
{
int a = 1, b = 2;
printf(">> actual addr a: %d, actual addr b: %d\n", &a,&b);
printf(">> actual value a: %d, actual value b: %d\n",a,b);
test1(a,b);
return 0;
}
下面是执行结果:
>> actual addr a: 6422300, actual addr b: 6422296
>> actual value a: 1, actual value b: 2
>> formal addr a: 6422272, formal addr b: 6422276
>> formal value a: 1, formal value b: 2
可以看到形参和实参的值虽然相同,但是他们的地址却不相同,所以函数在被调用的时候传入的参数(实参)实际上是被复制到另一个地址(形参地址)中去了,函数中对传入参数的操作实际上是对形参地址中的数进行操作,而与实参无关。所以下面的函数不能实现交换两个数的功能。
void swap_1(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
printf(">> formal addr a: %d, formal addr b: %d\n",&a,&b);
printf(">> formal value a: %d, formal value b: %d\n",a,b);
}
int main()
{
int a = 1, b = 2;
swap_1(a,b);
printf(">> actual addr a: %d, actual addr b: %d\n", &a,&b);
printf(">> actual value a: %d, actual value b: %d\n",a,b);
return 0;
}
输出如下:
>> formal addr a: 6422272, formal addr b: 6422276
>> formal value a: 2, formal value b: 1
>> actual addr a: 6422300, actual addr b: 6422296
>> actual value a: 1, actual value b: 2
可以看到在swap_1函数中a,b两个数的值是被调换了的,但是函数中的a,b的地址和主函数中a,b的地址根本不是同一个,主函数中a,b还是原来的数,所以这个函数起不到交换两个数功能。
既然形参和实参的关系是地址不同而值相同那么我们将实参的地址当作参数传给形参,然后在函数中对形参所指向的地址中的值(该地址就是实参的地址)进行改变是否就可以完成两个数的交换了?
OK!下面我们来编写函数测试以下:
void swap_2(int* a, int* b)
{
int temp;
printf(">> formal addr a: %d, formal addr b: %d\n",&a,&b); //打印形参地址
printf(">> formal value a: %d, formal value b: %d\n",a,b); //打印形参的值
printf(">> formal addr value a: %d, formal addr value b: %d\n",*a,*b); //打印以形参值为地址的值
temp = *a;
*a = *b;
*b = temp;
printf(">> formal addr a: %d, formal addr b: %d\n",&a,&b);
printf(">> formal value a: %d, formal value b: %d\n",a,b);
printf(">> formal addr value a: %d, formal addr value b: %d\n",*a,*b);
}
int main()
{
int a = 1, b = 2;
swap_2(&a,&b);
printf(">> actual addr a: %d, actual addr b: %d\n", &a,&b);
printf(">> actual value a: %d, actual value b: %d\n",a,b);
return 0;
}
结果如下:
>> formal addr a: 6422272, formal addr b: 6422276
>> formal value a: 6422300, formal value b: 6422296
>> formal addr value a: 1, formal addr value b: 2
>> formal addr a: 6422272, formal addr b: 6422276
>> formal value a: 6422300, formal value b: 6422296
>> formal addr value a: 2, formal addr value b: 1
>> actual addr a: 6422300, actual addr b: 6422296
>> actual value a: 2, actual value b: 1
从结果可以看出,这个函数可以交换输入参数的值。下面我们来分析一下为什么这个函数能够实现交换功能。
以变量a为例,首先,从结果的第一行可以看出,传入函数的参数是地址(即实参a的地址:6420300),这个地址作为一个值存放在形参a(地址:6422272)中,然后定义一个int型变量来存放地址6422272(形参a的地址)中的值6422300(实参a的地址),然后将指向6422296(实参b的地址)地址中的值 赋给 指向6422300(实参a的地址),开始地址6422300地址中的值为2,现在该地址的值变为1,同理,在执行函数之后地址6422296中的值变为1,从而实现了两个数的交换。
在这个过程中,是函数调用a,b两个值的地址,并在函数中改变这两地址中的值。与上一个函数的本质区别就是:上一个函数swap1只是将a,b的值给复制到两个新的地址当中,并改变新的地址中的值,与a,b地址无关。而swap_2则是直接操作a,b地址中的值,进而可以实现交换两个数的功能。
注意:实参和形参是在两个不同地址,虽然起的名字是一样的,当然这个名字可以自己随意起。为了更加清楚的说明实参和形参是两个东西,下面我将形参的变量名给改一下:
void swap_2(int* formal_a, int* formal_b)
{
int temp;
printf(">> formal addr a: %d, formal addr b: %d\n",&a,&b);
printf(">> formal value a: %d, formal value b: %d\n",a,b);
printf(">> formal addr value a: %d, formal addr value b: %d\n",*a,*b);
temp = *formal_a;
*formal_a = *formal_b;
*formal_b = temp;
printf(">> formal addr a: %d, formal addr b: %d\n",&a,&b);
printf(">> formal value a: %d, formal value b: %d\n",a,b);
printf(">> formal addr value a: %d, formal addr value b: %d\n",*a,*b);
}
int main()
{
int a = 1, b = 2;
swap_2(&a,&b);
printf(">> actual addr a: %d, actual addr b: %d\n", &a,&b);
printf(">> actual value a: %d, actual value b: %d\n",a,b);
return 0;
}
结果如下:
>> formal addr a: 6422272, formal addr b: 6422276
>> formal value a: 6422300, formal value b: 6422296
>> formal addr value a: 1, formal addr value b: 2
>> formal addr a: 6422272, formal addr b: 6422276
>> formal value a: 6422300, formal value b: 6422296
>> formal addr value a: 2, formal addr value b: 1
>> actual addr a: 6422300, actual addr b: 6422296
>> actual value a: 2, actual value b: 1
和之前的结果一样,可以知道,函数在执行的时候其实是不管你名字怎么起的,而是关心地址,而你起的名字实际也只是那个地址的代号而已。
通过上面的例子已经清楚了函数的形参与实参的区别,那么现在就来解决文章开头提出的问题
如何通过函数改变一个传入指针作为实参的地址,其实在理解上面的关于函数的形参和实参就非常容易懂了。
首先我们来看下面一个例子:
int b[3] = {1,2,3};
void fcn(int* *a)
{
*a = b;
}
int main()
{
unsigned int i = 0;
int* a;
for(i=0;i<3;i++)
{
printf(">> %d \n",a[i]);
}
fcn(&a);
for(i=0;i<3;i++)
{
printf(">> %d \n",a[i]);
}
}
函数输出为:
>> 0
>> -1
>> 4194304
>> 1
>> 2
>> 3
主函数中,我们定义了一个指针a,并且没有初始化它,之后我们是不能够直接对它指向的地址进行赋值,因为现在它的地址是随机的,对该地址进行操作后有可能会导致程序崩溃。我们想要用它就只能够对它自己的值也就是它所指向的地址进行操作。函数fcn通过形参来改变输入实参的值是怎么做到的呢?
首先看函数的形参 (int **a) ,表示什么意思呢?就是说传入的一个二级指针,指向指针的指针,这又是什么意思呢?比如你有一张藏宝图,它说宝藏在a地,你到a地之后也只得到一张藏宝图,该藏宝图说宝藏在b地,你只有到达b地才能够得到宝藏。这就是一个二级指针,第一个指向的地址是a,a的内容也是一个指针,指向b,b地址下才是真正的内容。
我们在主函数中定义了一个指针a,让它传入函数fcn,我们分析一下这个过程:
函数开辟一个形参的地址,该地址中的内容为指向传入参数地址的值,那么函数中我们将数组b的首地址赋给该地址,也就是将实参的地址更改成了数组b的首地址。
最主要的点就是弄清楚指针与地址以及该地址的值的关系,还有就是形参与实参的关系。