不定长参数函数的实现

源码如下:

int sum(int num, ...)
{
int *p = &num + 1;
int ret = 0;
while(num--)
{
   printf("%d\n", num);
   ret += *p++;
}
return ret;
}

int main(int argc, char* argv[])
{
printf("%d\n", sum(3, 5, 7, 9));
return 0;
}

所谓不定长参数,就是函数的形参数量不定,类型也可能是不定的。我们把像上面的函数sum中如“int num”这样的参数叫做“有名参数”,后面用“…”代表的都是“匿名参数”,有名参数是可以在函数中通过变量名直接访问的,匿名函数则无法通过变量名直接访问,只能是通过相对有名参数的位置(地址)来访问了。

关键在于:
(1)匿名参数的个数和类型必须通过有名参数传递给被调函数
如printf中的第一个参数“const char *fomat”,在format中不仅告诉了printf参数的个数,还必须指定正确的类型,二者缺一不可。
(2)被调函数本身有办法直接或间接定位参数的个数和类型
即描述参数个数和类型的参数的位置应该是固定的,函数有办法定位它们,而不是如匿名参数那般不确定的。像C语言的标准调用方式,即从右至左压栈且调用方清理栈的方式是比较合适的,尤其是前者,如果在第一个参数中指定各参数的个数和格式,则根据栈的规律可以知道,返回地址上方即是第一个参数(即ebp+8),以后的匿名参数则可依次确定了。

上面源码中的sum汇编代码如下:

7:        int *p = &num + 1;
00401038   lea         eax,[ebp+0Ch]
0040103B   mov         dword ptr [ebp-4],eax
8:        int ret = 0;
0040103E   mov         dword ptr [ebp-8],0
9:        while(num--)
00401045   mov         ecx,dword ptr [ebp+8]
00401048   mov         edx,dword ptr [ebp+8]
0040104B   sub         edx,1
0040104E   mov         dword ptr [ebp+8],edx
00401051   test        ecx,ecx
00401053   je          sum+5Ch (0040107c)
10:       {
11:           printf("%d\n", num);
00401055   mov         eax,dword ptr [ebp+8]
00401058   push        eax
00401059   push        offset string "%d\n" (0042201c)
0040105E   call        printf (00401110)
00401063   add         esp,8
12:           ret += *p++;
00401066   mov         ecx,dword ptr [ebp-4]
00401069   mov         edx,dword ptr [ebp-8]
0040106C   add         edx,dword ptr [ecx]
0040106E   mov         dword ptr [ebp-8],edx
00401071   mov         eax,dword ptr [ebp-4]
00401074   add         eax,4
00401077   mov         dword ptr [ebp-4],eax
13:       }
0040107A   jmp         sum+25h (00401045)
14:       return ret;
0040107C   mov         eax,dword ptr [ebp-8]
15:   }

由此又可以进一步验证两个问题:
(1)指针的运算。
指针的加减都是以类型的大小为一个单位的,无论是p = &num+1(相当于加4),还是*p++(先计算*p,在执行p++,相当于加4)
如此,若我真的要将地址加1而非加一个数据单位,又当如何呢?
答案是只能先将&num转换成单字节变量指针,如(char *)&num
这种做法其实还是遵循着指针的加减1是数据单位的加减1,只不过是上述做法,将数据单位强制转换成了1Byte而已。
(2)while(num--)的执行
这个过程看上去有点儿古怪,先是把num的值保存到了一个临时变量(寄存器ecx)中,然后执行num--,但是判断与0的关系的时候,用的又不是num的值,而是临时变量ecx中保存的num原值。这是我始料不及的。
这也就是为什么明明num已经为0了,while还可以执行一次的原因,因为用来做判断的那个值始终比num的实际值要大一号:P

所有的都记录下来备查吧:P


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值