2 whukk whuKK 于 2016.04.07 23:00 提问

C语言的陷阱:关于函数参数的“副作用”问题

当我们执行以下代码时:

 int x=1,y=0;   
printf("%d,x=%dy=%d",x=y,x,y) ;

会出现结果:
0,x=0y=0
也就是说在printf语句中由于x=y的影响,x的值被改变了。
但是如果我们写一个函数来完成类似的动作:
定义一个swap函数来交换xy变量的值:

 int  swap(int *a,int *b){
    int t;
    t=*a;
    *a=*b;
    *b=t;
    return 1;
}

之后我们用printf来输出,其中会用到swap函数,我们来看看swap函数的运行是否会影响接下来对xy的输出:

 int main(){
    int x=1,y=0;

    printf("return of swap is %d\tx=%d,y=%d\n",swap(&x,&y),x,y); 
    return 0;
}

上面这段代码的输出如下:
return of swap is 1 x=1,y=0
注意到了么?xy的值并没有交换。
难道swap函数没有执行么?为什么会有返回值呢?
我们再次输出xy的值试试:
在return语句之前增加语句:

 printf("\t\t\tx=%d,y=%d",x,y);

return of swap is 1 x=1,y=0
x=0,y=1
hey,xy的值又交换了!但是出现了延迟。
立即交换和有延时的交换之间的区别是为什么呢?

5个回答

caozhy
caozhy   Ds   Rxr 2016.04.07 23:12
已采纳
 不是什么延迟,而是C语言是从后往前求函数参数的值,并且把值送入了堆栈。
所以printf("return of swap is %d\tx=%d,y=%d\n",swap(&x,&y),x,y); 
x y都先被放入了堆栈,然后才执行的swap,x y是复制到堆栈上的,所以再更改它们不会影响堆栈上它们的副本了。

printf("x=%d,y=%d\treturn of swap is %d\n",x,y,swap(&x,&y)); 
这样写,先执行swap,所以就是交换了。
rkakar
rkakar 如果参数是地址的话应该就会产生影响了吧,虽然不会对原来的值产生影响,但是由于副本和原来的地址共同指向同一个值,如果副本改变了,那么原来的值应该也会改变吧?不知道对不对
一年多之前 回复
qq_33233586
qq_33233586 回复whuKK: 楼主,第一个题目运行错了。
一年多之前 回复
qq_33233586
qq_33233586 回复whuKK: 楼主,第一个题目运行错了。
一年多之前 回复
whuKK
whuKK 感谢帮助!
一年多之前 回复
caozhy
caozhy 这个是语言未定义行为,我之前的分析是基于编译器实现的分析。比如说在Visual C++上,输出就是0 1 0
一年多之前 回复
caozhy
caozhy 回复whuKK: 这个是语言未定义行为,我之前的分析是基于编译器实现的分析。比如说在C++上,输出就是0 1 0
一年多之前 回复
whuKK
whuKK 请问为什么执行int x=1,y=0; printf("%d,x=%dy=%d",x=y,x,y) ;语句时x的值就被改变了呢?如果x、y的值先入栈的话x的值应该也不受X=Y的影响啊。
一年多之前 回复
caozhy
caozhy   Ds   Rxr 2016.04.11 17:15

图片说明

jxhkl
jxhkl   2016.04.18 17:16

楼上说的对,下面补充一下自己的认识。假设在main函数中x的地址是0x0000 0000; y的地址是0x0000 0004; 当执行
printf("return of swap is %d\tx=%d,y=%d\n",swap(&x,&y),x,y);函数时,CPU先把参数y复制到堆栈中的一个地址处,假设为0x0000 0008;

然后CPU再将参数x复制到堆栈中的其它一个地址处,假设为0x0000 000C;然后调用swap函数,而swap函数的参数也是复制main函数中地址在
0x0000 0000和0x0000 0004处的x,y。当swap函数执行完之后,0x0000 0000处的地址确实变为0,0x0000 0004处的地址也确实变为了1。
但是当进入printf函数内部进行打印的操作时,printf会取出复制main的放在0x0000 0008和0x0000 000C处的值。也就是1,0.故printf打印出来感觉
swap没有执行。

qq423399099
qq423399099   Ds   Rxr 2016.04.08 09:33

楼上正解
注意printf是从右往左压入参数(压到栈里面),然后再格式化匹配输出,一个个弹栈
(有点类似经典的printf和++在一起的问题)

ennaymonkey
ennaymonkey   2016.04.21 13:24

楼上正解
注意printf是从右往左压入参数(压到栈里面),然后再格式化匹配输出,一个个弹栈
(有点类似经典的printf和++在一起的问题)

Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!