可变参数入站顺序(个人理解)

全网就那几篇各种复制粘贴,没有个人的见解。疑惑点解释不清楚。每次翻看一个资料得找半个小时。
下面是我的理解(不知道对不对,错误了请给我指一下谢谢)
压栈顺序:
main()函数返回地址->main()参数-》main()函数变量-》main()运行状态-》func()参数由右至左-》call函数(1.压入返回地址,2.跳转)-》
首先将参数从右向左压栈;再调用函数;(压栈过程在下面main 的部分)
将ebp就上一轮调用的函数栈基地址压栈;(用于后面恢复现场)
用新的栈顶指针来更新当前ebp;(即从这里开始是当前函数的函数栈帧基地址)
更新栈顶指针;(sub esp,0CCh ;0CCh 表示的就是当前函数的栈大小)
之前的寄存器压栈保存;ebx 、esi 、edi ;
循环的将整个函数栈空间初始化 0x CCh;(这里只有debug情况下才会有,0xCC是int 3指令的机器码,好像叫调试中断指令,如果CPU意外执行这样的指令,证明程序哪里出错了,产生中断)

由右至左顺序原因:可变参数第一个参数是确定的(可以看相关文章),然后这样压栈后,栈顶元素为第一个参数,也即确定参数。这样就可以算出可变参数占用的空间然后,慢慢算出各个参数。
假如为从左至右入栈,func()函数返回地址和第一个确定的可变参数位置挨在一起。你就无返判断后面可变参数到底有多少个。因为第一个参数是确定的。
以printf为例:
int printf(const char * format, … );
format表示你要输出的格式,比如printf(“%d,%c”,i,j);
就是将i做为整形数,j作为字符输出,这个函数就是格式化输出,而format就是你想要的格式

看到有人提问到,在处理printf/cout时,压栈顺序是什么样的?大家都知道是从右往左,也就是说从右往左的计算,但是,这里的计算不等于输出。

a++和++a的压栈的区别:在计算时,遇到a++会记录此时的a的值作为最后的输出结果。遇到a和++a的时候则不会将此时的计算结果作为最终的输出,只会修改a的值,在最终输出的时候都输出a的值(所以++a和a的结果总是一样的)。

比如:

int a = 2;
cout << ++a << a++ << a << ++a << a++ << a << ++a << endl;
 输出为:7 5 7 7 3 7 7 

但是不禁要问,为什么C语言的函数调用入参压栈顺序一定是自右向左?反过来不可以吗?

首先,在函数调用过程中,最先入栈的是调用函数调用处的下一条指令的地址,这样调用完成后返回到该地址继续执行,这个地址是很重要的,然后才是函数的入参,函数的内部局部变量。调用结束,将栈内容一一出栈,最后栈顶指向了开始存储的指令地址,主函数继续从这里开始还行。

其次,函数调用入参的入栈顺序和C语言支持变长参数有关,比如printf函数就支持变长参数,也即void printf(const char *fmt,……),这个时候并不知道参数有多少个,如果从左向右入栈,那么fmt就在栈底,该参数的位置无法通过栈顶指针偏移来确定,因为不知道栈顶和栈底之间有多少个参数,大小是多少,无法确定,但是,如果从右向左入栈的话,fmt参数就在栈顶的位置,通过这个固定的普通参数就可以通过偏移逐一寻址到后续参数的地址。

汇编操作:
https://blog.51cto.com/u_10799170/1715186

参考:https://blog.csdn.net/simpleman7210/article/details/9328029
https://blog.csdn.net/weixin_44503157/article/details/120228791

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值