C语言中有一条重要准则,函数参数按值传递,只传入不传出,比如:
int test(char ca){ ca = 'b'; }
void main()
{
char ca = 'a';
printf("before fun call, ca = %c",ca);
test(ca);
printf("after fun call, ca = %c", ca);
}
输出结果:before fun call, ca= a after fun call, ca = a
为避免混淆,把main里的变量ca称ca(1),test函数的参数ca称ca(2)。ca(1)和ca(2)完全独立,不能因名字相同而混淆。调用test(ca)时,只是拷贝一份ca(1)的值'a’传给test的参数ca(2),在test函数内ca(2)是另一个变量,函数内部对它的读写操作和外面的ca(1)没有任何关系。所以说这个传递过程传入不传出,即“肉包子打狗,有去无回”。
但当用指针做参数时,很多人开始迷糊了。看下例:
int test(char *inchar){ *inchar = 'b'; }
void main()
{
char ca = 'a';
printf("before fun call, ca = %c",ca);
test(&ca);
printf("after fun call, ca = %c", ca);
}
此时输出是:before fun call, ca = a after fun call, ca = b
有人觉得这里的参数不是既传入又传出数值了么?这是一个错觉,看仔细点,函数调用test(&ca),传入的是什么,而函数调用结束后被改变的又是什么?
没错,传入的是变量的地址,但改变的是变量的值。或者说,传入指针的值,改变指针所指向内存里的值。所以上面例子依旧符合函数参数传递原则,只传入,不传出,因为函数调用前后,实参&ca的值并没改变。
所以函数的确是单向按值传参,传入函数内的仅仅是外部变量的只读值,不能通过它再返回内部变量值。但函数只return一个值常常不够,这时可以用指针“明修栈道,暗渡陈仓”:把指向某内存的指针作为参数传入,在函数内改变指针所指内存的内容,这样函数退出时,虽然没改变指针自身值,但改变了指针所指内存的值。通过这种间接方式就能在函数内改变外部变量值。
那如果想改变指针本身的值怎么办?道理一样:想在函数内修改外部变量值就传递指向它的指针,那么改变外部指针的值,就传递指针的指针呗。只要把指针看作普通变量,一层层套用就可以了。如:
char c=’c’;
char *pc=&c;
char **pcc=&pc;
这样逐级取址,得到指针的指针,作为参数传入函数,就可以反过来改变指针的值。依次类推,指针的指针的指针也一样,不要绕昏了。
为快速判断某函数是否会向外传出参数值,有一个傻瓜式的*号规则:只有函数内有类似”*pVal =”这种带“*”号的赋值语句时,才可能向外传值,或者反过来,没*号,函数参数不可能向外传值。所以下面程序中,一看就知道param值不会被返回到外部,因为赋值语句中没有*号:void TryToRetVal( int * param) { param= VAL1; }
反之如果为* param = VAL1; 有*号赋值,说明可能有参数向外传值的情况,就要注意观察外层指针参数对应的内存值的变化。这一判断准则对于分析跟踪复杂函数实现中的数据流变化会有所帮助。