函数调用时入栈顺序

子目 2013-07-29 05:12:43
到底是参数先入栈,还是函数地址先入栈?两种说法的都有,感觉很困惑,请大家一起讨论
...全文
1320 34 打赏 收藏 转发到动态 举报
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
lm_whales 2013-07-31
  • 打赏
  • 举报
回复
跟操作系统还是有点关系的,虽然不多。 比如 borland TC,TC++,BC++,MS Quick C++有 近指针near *,远指针 far *,和巨型指针 huge * 近函数 near ,远函数 far VC6, BCB6 就没有了,这些都是因为WIN32 和 DOS,Win16 的原因造成的。 386 以后的CPU有6个段寄存器,VC 只有一个flat模式, CS,DS,SS,ES指向相同地址 FS用于线程,GS不使用 这些都是和操作系统相关的。 堆栈的缺省大小,如何安排堆栈,堆,全局变量,都和操作系统有关系。 编译器安排的不合适的代码,操作系统也不会接受。 比如你的全局变量使用的内存过大, 比如int a[512*1024*1024]; 编译没问题,链接时只有一个警告 告诉你, total image size -2147356672 exceeds max (268435456); image may not run 然后,不能运行 PS: 编译器如何实现各种调用约定,如何调用函数,可以提供多大的堆栈,都和操作系统息息相关。 编译器生成的代码,最终是要和操作系统打交道的。 有些操作系统,居然使用JAVA开发的。这些都说明编译器要充分考虑CPU,操作系统,才能合理安排,编译后的代码。 函数调用约定这种东西,只能由编译器的后端实现,或者前后端结合实现,如果只由前端实现的话; 就很容易出现和操作系统不兼容的情况。 这样,代码和编译器的可移植性,就受到制约,很难移植。
lm_whales 2013-07-31
  • 打赏
  • 举报
回复
操作系统也是有关系的,比如 WIn32 一律 flat 模式 Dos tiny ,small,large,huge 这些都是操作系统在里面起的作用,如果WIN32允许不同的段,那么Win32 也不会都是一种模式 CPU可是提供了6个段寄存器,FS用于线程变量外,GS可没有使用哦, CS,DS, ES,SS都是同一段 这都是操作系统在其中的作用。
引用 30 楼 xdxiaofeng 的回复:
[quote=引用 24 楼 lm_whales 的回复:] [quote=引用 23 楼 xdxiaofeng 的回复:] 是CPU决定的。跟windows或者Linux没有关系。 打开CPU手册,里面有详细的介绍函数调用的过程。
指令是由CPU决定的; 函数是由,编程语言,编译器,操作系统(如果有的话),CPU共同决定的。 CPU,才不管参数,怎么传递呢!爱咋咋!!!! C,C++ 函数由调用约定和具体语境决定。 C 一般C 多采用调用约定 cdcel C++ 多采用标准调用约定 stdcall C++非静态成员函数,第一个参数,即对象本身,隐藏参数 this指针,一般用寄存器传递,其他和 stdcall差不多,只有不定长参数,多半会采用C cdcel的方式 X86,Window ,VC,Win32 ,C++非静态成员函数 第一个参数,即对象本身,隐藏参数 this指针,采用ecx寄存器传递。 调用约定才是参数传递,和返回值的传递方式的核心内容。 CPU只是提供,实现的便利而已。 比如提供寄存器,比如提供堆栈,提供堆栈操作指令,提供过程调用指令,提供跳转指令,提供返回指令。 [/quote] 说CPU是不准确,应该是CPU是提供实现,而由编译器来使用它,却确的说,是编译器后端。 跟操作系统肯定是没关系的。因为编译器生成好机器码以后,如果传递参数就已经定下来了。 操作系统也无法干预。 编译器要运行在操作系统上,不代表操作系统也能决定参数传递过程。 [/quote]
lm_whales 2013-07-31
  • 打赏
  • 举报
回复
引用 33 楼 zhao4zhong1 的回复:
[quote=引用 28 楼 zhctj159 的回复:] 难道不知道call 可以表述成 push addr jmp
;难道不知道
call func_addr
;可以表述成 
push next_instruction_addr   
jmp func_addr
next_instruction_addr:
[/quote] 补充一下,准确的说,X86 call func_addr ;可以表述成,以下代码,直接替换,也接近可以。 push offset next_instruction_addr pushf jmp func_addr next_instruction_addr : ret ;可以表述成,直接替换,也接近可以。 popf jump esp
赵4老师 2013-07-31
  • 打赏
  • 举报
回复
引用 28 楼 zhctj159 的回复:
难道不知道call 可以表述成 push addr jmp
;难道不知道
call func_addr
;可以表述成 
push next_instruction_addr   
jmp func_addr
next_instruction_addr:
xiaohuh421 2013-07-30
  • 打赏
  • 举报
回复
win7 VS2008
xiaohuh421 2013-07-30
  • 打赏
  • 举报
回复
一般都是只压栈参数, 然后直接call函数地址. 如果想清楚的知道细节, 可以在调试的时候查看反汇编代码. 或者使用动态或者静态调试工具反汇编代码看. int addVal(int val1, int val2) { return val1+val2; } 调用代码汇编代码: int Val = addVal(3, 4); 00B05493 push 4 00B05495 push 3 00B05497 call addVal (0B022D4h)
SKATE11 2013-07-30
  • 打赏
  • 举报
回复
参数入栈完毕后 用函数地址CALL
www_adintr_com 2013-07-30
  • 打赏
  • 举报
回复
引用 17 楼 healer_kx 的回复:
[quote=引用 13 楼 adlay 的回复:] 函数的地址不会入栈. call 指令会 push 把一个当前 call 指令的下一条指令地址入栈, 在函数里面运行到 ret 的时候, 取出来跳转到那个地址. 参数肯定是在 call 之前入栈的.
非也。。。你看前面的很多人的回复。[/quote]
Orange_ou 2013-07-30
  • 打赏
  • 举报
回复
引用 15 楼 Oringe_new 的回复:
[quote=引用 12 楼 lpcads 的回复:] [quote=引用 4 楼 Oringe_new 的回复:] 函数代码放在代码区 先把参数压栈或者放到寄存器 然后call 函数地址 调用完返回 ???哪里要函数地址压栈了
call的时候如果没push函数地址,那ret时又将何去何从[/quote] call 会调整CS:EIP 函数调用之前参数压栈, 然后call指令 push 当前指令地址作为返回地址(但这个不是函数地址) 然后再把前一个栈帧压栈(维护之前的栈环境)调整CS:EIP 执行完后还原原来的环境 继续执行 [/quote] 搞错了。。。。应该是函数调用之前参数压栈, 然后call指令 push 当前指令地址作为返回地址(但这个不是函数地址) jmp 跳转到函数地址那里 调整CS:EIP 然后再把前一个栈帧ebp压栈(维护之前的栈环境)执行完后还原原来的环境 继续执行下一条指令
healer_kx 2013-07-30
  • 打赏
  • 举报
回复
引用 13 楼 adlay 的回复:
函数的地址不会入栈. call 指令会 push 把一个当前 call 指令的下一条指令地址入栈, 在函数里面运行到 ret 的时候, 取出来跳转到那个地址. 参数肯定是在 call 之前入栈的.
非也。。。你看前面的很多人的回复。
Orange_ou 2013-07-30
  • 打赏
  • 举报
回复
引用 15 楼 Oringe_new 的回复:
[quote=引用 12 楼 lpcads 的回复:] [quote=引用 4 楼 Oringe_new 的回复:] 函数代码放在代码区 先把参数压栈或者放到寄存器 然后call 函数地址 调用完返回 ???哪里要函数地址压栈了
call的时候如果没push函数地址,那ret时又将何去何从[/quote] call 会调整CS:EIP 函数调用之前参数压栈, 然后call指令 push 当前指令地址作为返回地址(但这个不是函数地址) 然后再把前一个栈帧压栈(维护之前的栈环境)调整CS:EIP 执行完后还原原来的环境 继续执行 [/quote] 打漏了call指令 push 当前指令地址的下一条指令作为返回地址(但这个不是函数地址)
Orange_ou 2013-07-30
  • 打赏
  • 举报
回复
引用 12 楼 lpcads 的回复:
[quote=引用 4 楼 Oringe_new 的回复:] 函数代码放在代码区 先把参数压栈或者放到寄存器 然后call 函数地址 调用完返回 ???哪里要函数地址压栈了
call的时候如果没push函数地址,那ret时又将何去何从[/quote] call 会调整CS:EIP 函数调用之前参数压栈, 然后call指令 push 当前指令地址作为返回地址(但这个不是函数地址) 然后再把前一个栈帧压栈(维护之前的栈环境)调整CS:EIP 执行完后还原原来的环境 继续执行
onlyhuiyi 2013-07-30
  • 打赏
  • 举报
回复
推荐看 汇编语言 王爽的 有具体的调用函数的汇编执行过程。 我记得是先把 参数压入 栈中, 下一步过程就 adlay前辈说的过程了
www_adintr_com 2013-07-30
  • 打赏
  • 举报
回复
函数的地址不会入栈. call 指令会 push 把一个当前 call 指令的下一条指令地址入栈, 在函数里面运行到 ret 的时候, 取出来跳转到那个地址. 参数肯定是在 call 之前入栈的.
lpcads 2013-07-30
  • 打赏
  • 举报
回复
引用 4 楼 Oringe_new 的回复:
函数代码放在代码区 先把参数压栈或者放到寄存器 然后call 函数地址 调用完返回 ???哪里要函数地址压栈了
call的时候如果没push函数地址,那ret时又将何去何从
renxingsong2009 2013-07-30
  • 打赏
  • 举报
回复
函数调用约定 http://baike.baidu.com/view/2315027.htm
lm_whales 2013-07-30
  • 打赏
  • 举报
回复
函数地址在 call 这条指令中,执行后,直接就跳转过去了。
lm_whales 2013-07-30
  • 打赏
  • 举报
回复
函数调用,压栈的是函数参数,和状态寄存器,调用语句的下一条指令的地址 这样 返回后,直接执行下一条指令。 要理解这种情况,必须了解汇编语言,不然只能是走马观花,不能有什么深刻印象。
lm_whales 2013-07-30
  • 打赏
  • 举报
回复
有些CPU有硬件堆栈,这种堆栈很少,自然只能存放返回地址 而很多CPU,比如X86系列,直接使用内存作堆栈。 堆栈里存放的是参数和返回地址 返回地址通过 call 这种调用方式,自动入栈,所以只能是先把参数入栈; 才可以调用函数,使得返回地址和状态寄存器的内容自动入栈。 如果返回地址先入栈,就要先调用函数,然后就会执行函数代码; 此时参数还没有入栈,那么参数就永远没有机会入栈了,函数就没有参数可用了。 除非像线程那样处理,否则,没有,函数返回地址,先入栈的可能。
帖子不能编辑 2013-07-30
  • 打赏
  • 举报
回复
引用 24 楼 lm_whales 的回复:
[quote=引用 23 楼 xdxiaofeng 的回复:] 是CPU决定的。跟windows或者Linux没有关系。 打开CPU手册,里面有详细的介绍函数调用的过程。
指令是由CPU决定的; 函数是由,编程语言,编译器,操作系统(如果有的话),CPU共同决定的。 CPU,才不管参数,怎么传递呢!爱咋咋!!!! C,C++ 函数由调用约定和具体语境决定。 C 一般C 多采用调用约定 cdcel C++ 多采用标准调用约定 stdcall C++非静态成员函数,第一个参数,即对象本身,隐藏参数 this指针,一般用寄存器传递,其他和 stdcall差不多,只有不定长参数,多半会采用C cdcel的方式 X86,Window ,VC,Win32 ,C++非静态成员函数 第一个参数,即对象本身,隐藏参数 this指针,采用ecx寄存器传递。 调用约定才是参数传递,和返回值的传递方式的核心内容。 CPU只是提供,实现的便利而已。 比如提供寄存器,比如提供堆栈,提供堆栈操作指令,提供过程调用指令,提供跳转指令,提供返回指令。 [/quote] 说CPU是不准确,应该是CPU是提供实现,而由编译器来使用它,却确的说,是编译器后端。 跟操作系统肯定是没关系的。因为编译器生成好机器码以后,如果传递参数就已经定下来了。 操作系统也无法干预。 编译器要运行在操作系统上,不代表操作系统也能决定参数传递过程。
加载更多回复(14)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧