最近在阅读《程序员的自我修养》,看到10.2节,又想起以前的入栈顺序,对此又深挖了一下:
大家都知道:
1.函数入栈顺序通常是:从右到左
2.从右到左的好处是,第一个参数就在栈顶,我们很方便就定位到了第一个参数的位置
3.网上一搜,大家都说,从右往左入栈的目的是方便的可变参数的使用,获得第一个参数的位置,
好的,大部分的讨论终结于此,那么,为什么我们要获得第一个参数的位置呢?为什么获得第一个参数就方便可变参数的使用和实现呢?
接下来,我们回想一下,可变参数的函数是如何使用的,不得不说到下面几个函数:
void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_end(va_list ap); void va_copy(va_list dest, va_list src);
va_list是用于存放参数列表的数据结构。 va_start函数根据初始化last来初始化参数列表。 va_arg函数用于从参数列表中取出一个参数,参数类型由type指定。 va_copy函数用于复制参数列表。 va_end函数执行清理参数列表的工作。 引用自http://www.jb51.net/article/43192.htm
我们在使用可变参数的时候,一定会使用va_start这个函数,准确的说,它是一个宏:
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
也就是说,我们在使用可变参数的时候,必须要给出第一个参数,这样,va_start才能初始化后续的列表,而要定位第一个参数,直接用ebp-4即可(这里的减4是减去返回地址)
回答最初的问题:
1.为什么要获得第一个参数的位置:首先,C语言库使用可变参数函数时,就必须要求有第一个参数,获得第一个参数,可以定位后续的参数。这里又会想出一个问题,那么,为什么C语言库要至少有1个参数,我个人理解是,有一个参数可以方便用户处理后续的可变参数,因为如果你的代码中写死了后续参数的类型,你不如直接用固定参数,那么既然你使用了可变参数,就说明后续的类型个数,你可能是不确定的,你可以通过第一个参数来确定(当然,是否这样确定,如何确定由你的代码实现,参见printf的实现)
2.如果没有第一个参数,会怎样?我想了下,没有第一个参数,那么你整个参数的类型,是没法确定的(排除有的脑袋进水,能确定类型还是用可变参数的人),当然,要确定也不是没有办法,比如,你可以把参数类型写入一个配置文件中,读取,即配置文件实现了第一个参数的功能。那么基于此,还不如有一个固定的参数,让用户能够方便的获取可变参数的类型和个数。
3.基于1,2,可以知道为什么要有第一个参数,而不允许全部是可变参数?如果理解了1,2,既然C语言设计要求一定要有第一个参数,那么第一个参数就要方便获取,如果从右往左压栈,那么,第一个参数的地址直接ebp-4即可;如果从左往右压栈,由于可变参数的大小类型不知道,很难通过ebp去定位第一个参数的位置