在C语言中,我们经常用到形参和实参这一对双生姐妹花,就像《大话西游》里的紫霞和青霞。比如我们可以用她们来达到一些很简单的需求,还是从经典的交换两个数字a,b的值开始。我们有以下程序:
void Swap(int x,int y)
{
int tmp;
tmp = x;
x = y;
y = tmp;
printf("%d,%d\n",x,y);
}
int main(){
int a = 1,b = 2;
Swap(1,2);
return 0;
}
于是通过上面的程序,我们很开心地得到 2,1 的结果,我从实参这里输入 1,2 ,得到的结果是 2,1 ,不是说明正好交换成功了吗?在此呢,要提醒大家一句,有时候眼睛看到的不一定就是真实的。所以,我们需要一面照妖镜,就是下面这道程序:
void Swap(int x,int y)
{
int tmp;
tmp = x;
x = y;
y = tmp;
}
int main(){
int a = 1,b = 2;
Swap(1,2);
printf("交换之后m和n的值是:m = %d,n = %d\n",a,b);
return 0;
}
按照我们的想法,这好像跟上面的没什么区别,只是printf语句的位置不一样而已,所以,输出结果应该是 交换之后m和n的值是:m = 2,n = 1 。奈何,输出结果与你想的略有不同,交换之后m和n的值是:m = 1,n = 2 ,你会发现,你大爷还是你大爷,这a和 b的值根本没变化啊!
那么这是什么原因呢?
这里先说明一下,父函数:指调用函数,如我们平常见到的main函数;子函数:指被调用函数,比如这里的swap函数
记住这句话“形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。”,这句话什么意思?简单来说,父函数调用了子函数,子函数里的形参于是把父函数里的实参的值拷贝了一份拿去自己那里运算,真正的实参是没有动的,形参得到了实参的复制品,开始进行运算,把结果算出来,写到一张纸上(即把数据写入内存),然后这句话“释放所分配的内存单元”,通俗来讲就是把这张纸烧了……..所以实参还老老实实地站在原地,你这时候无论怎么输出,实参不变。
那么怎么才能真正改变主函数里的值呢?
答案是:指针。不论是形参还是实参,只要是变量,就在内存空间有一个专属自己的地址。就像每个人都有第一无二的身份证号。先前实参的值没有被修改,也可以这么来理解,实参和形参分属不同的地址,形参拷贝了实参的值,在自己的地盘计算的不亦乐乎,实参却在自己的一亩三分地不为所动。所以要想让实参动,只能从其根源,即地址入手。程序如下:
void Swap(int *p1,int *p2) // 定义形参为指针变量
{
//a,b地址传递过来后,p1 、 p2 便是指向a,b的指针
int tmp= *p1;
//对p1 解引用得到p1指向的值,即a 的值。用临时变量保存 。
*p1 = *p2;
*p2 = tmp;
}
int main(){
int a = 1,b = 2;
Swap(&a,&b); //把a,b的地址传递给 Swap 函数
printf("交换之后m和n的值是:m = %d,n = %d\n",a,b);
return 0;
}
这个程序运行之后,便是我们理想中的值了。因为无论如何,已分配的变量的内存地址是不会改变的。
由此我们知道:
子函数的改变要想修改父函数的值,必须要
1、传指针。直接将实参的地址进行传递。
2、解引用。通过对指针解引用,去修改其中的值。
下面我们再看一个程序,利用这个性质:当对置空的指针解引用时,会造成崩溃。
如:
int main()
{
int a = 10;
int *p = &a;
p = NULL; //NULL:表示无效指针,即将指针p 置空
printf("%d\n",*p);
return 0;
}
如果运行这个程序,会引起崩溃这是毋庸置疑的,那么再看下面的程序:
void Fun(int *t)
{
t = NULL;
}
int main()
{
int a = 10;
int *p = &a;
Fun(p);
printf("%d\n",*p);
return 0;
}
我们发现这里的程序没有崩溃,依然输出了 10,请思考原因,并且怎么改能够使其崩溃呢?
道理就如同前面讲的,实参与形参的地址不同,所以对实参无影响。
那么我们的目的是为了使一级指针变量p成为空指针,那么就按照前面说的那两个条件:传指针和解引用去执行,那么我们Fun函数的形参要接收的就得是p的地址,但p已经是一个指针了,怎么办?当然,这里就要用到二级指针。所以Fun函数的形参应定义为int **t
这里我们知道了,你要修改谁的值,就传谁的指针,形参的类型根据其改变即可。
传指针我们完成了,接下来就是解引用了,那是 *t 呢?还是**t呢?
自然是前者!因为你要修改的是一级指针的值啊!在此不多赘述。具体程序修改为:
void Fun(int **t)
{
*t = NULL;
}
int main()
{
int a = 10;
int *p = &a;
Fun(&p);
printf("%d\n",*p);
return 0;
}
这样就达到了我们的目的,程序如预想的崩溃了。
此题还是很有代表性的,他教会了我们如何去真正利用指针修改主函数中的值。