笑看风云

记录生活中的启迪与感动
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C或C++为什么被设计成是从右向左处理参数? (

Posted on 2012-03-13 13:45  清晨的风  阅读(1345)  评论(0编辑  收藏  举报

转自:http://blog.sina.com.cn/s/blog_5ed3e6210100ceiu.html

 

一直是一个迷惑人的问题,网上找竟然找不到答案,所以这是本文的目的。

C或C++的编译器是被设计成从右向左的顺序处理函数调用中的变元的。但是编译器扫描变元的顺序是从左向右的。使用从左路向右的顺序,编译器只需要一边扫描变元一边生成代码即可,但从右向左的顺序却要编译器记住扫描过的所胡变元。为什么C++设计者不设计成从左向右的调用顺序呢,难道这是他们工作的疏忽吗,不是的。

原因是为了处理那些变元不固定的函数调用。用个例子来说明,看下面的程序:

#include<iostream>

Using namespace std;

int add(int count,…)//…是说有不确定的变元个数;

{

int i,sum=0;

int *p;

p=&count+1;//p指向比count高一个单元的地址

for(i=1;i<=count;i++)

sum+=*p++;

return sum;

}

int main()

{

cout<<add(3,1,2,3)<<endl;//sum=6;

return 0;

}

我们来看当main调用add的时候内存中的堆栈是怎么样的:

p

sum=0

i

返回地址

3(count)

1

2

3

sp

编译器从右向左的顺序把所有的参数压入规模当中,当执行p=&count+1的时候,使P指向count高一个字节,然后用下个for循环使接下的的三个数相加得到和sum.编译器之所以能够工作是因为编译器知道count的地址,因为它位于函数的返回地址的高一个字节,所以可以直接引用它。

现在假设编译器不是从右向左的顺序把参数压入堆栈的,而是从左向右的顺序,堆栈的情况如下:

p

sum=0

i

返回地址

3

2

1

3(count)

sp

现在如图所示,编译器将无法找个count的地址。因为编译器知道返回地址的地址,但不知道参数的个数因为不知道count等于多少)所以无法定位count。而要知道参数的个数不要知道count等于多少,所以编译器将陷入糊涂当中。

这就是为什么C或C++要从右向左的顺序调用处理变元的原因了。

再来说一下变元的宏,在C或C++中,有些函数的变元计数是由第一个变元间接指定的。如下面的函数调用:

printf(“x = %d y = %d\n”,x,y);

printf的第一个变元是控制字符串,它必须包含调用中每个附加变元的变换码(以%开妈的嵌入式代码)。上面有两个变换码,表明在它的后面有两个要输出的数x和y。

为了使访问有变长变元的列表的函数中的参数更容易,C++提供了 va_list,va_start,va_arg,va_end这样几个宏。和个宏的功能如下:

va_list(p)=void *p;

va_start(p,count)=p=&count+1;

va_arg(p,int)=*((int *)p)++;

va_end(p)用于整理;

用宏把上面的程序改如下:

#include<iostream>

#include<cstdarg>

using namespace std;

int add(int count,...)

{

int i,sum=0;

va_list(p);

va_start(p,count);

for(i=1;i<=count;i++)

sum+=va_arg(p,int);

va_end(p);

return sum;

}

int main()

{

cout<<add(3,1,2,3)<<endl;

return 0;

}