函数传参是通过寄存器还是栈?

六道佩恩 2021-04-03 06:38:33
调试查看汇编是通过寄存器传的

但《程序员的自我修养》的“调用惯例”却只说了用栈,没有提用寄存器

连标了__cdecl的库函数,调用时都是通过寄存器的,为什么?不应该是通过栈吗?
...全文
2755 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaoxiangqing 2021-04-10
  • 打赏
  • 举报
回复
大部分都是用的栈吧
熊猫呀 2021-04-06
  • 打赏
  • 举报
回复
以前调试过一次 x86 平台 用栈 x64 前几个参数 是用通用寄存器
真相重于对错 2021-04-05
  • 打赏
  • 举报
回复
引用 13 楼 六道佩恩的回复:
[quote=引用 6 楼 真相重于对错 的回复:]1、用栈和用寄存器都可以达到传递参数的目的 2、c/c++只规定了语言部分,并没有规定实现部分。 3、__stdcall....这些只是微软 ,gun c/c++的厂家的自己实现。这些不是c/c++的标准的关键词。 4、如果用到寄存器,却发生中断,也没有问题,因为中断首先就要保存寄存器,中断结束后恢复寄存器。 5、我用vs看基本还是用栈。
gcc我看64位用的寄存器,32位用的栈。 我知道这不是语言的部分,但问题是,既然GCC自己规定了,为什么64位的时候它自己又不遵守了?[/quote] 我对gcc不是很熟,你既然说是gcc规定。不知道你是从官方文档里得到的还是道听途说呢? https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html
真相重于对错 2021-04-05
  • 打赏
  • 举报
回复
可以不用push,直接操作栈寄存器就可以了,pop也是类似
luj_1768 2021-04-05
  • 打赏
  • 举报
回复
什么时候发现的?会不会与python有关?
luj_1768 2021-04-05
  • 打赏
  • 举报
回复
无怪代码越跑越快,程序越来越大。
'><xxxx> 2021-04-05
  • 打赏
  • 举报
回复
xxxxxxxxxxxxxxxxxx
'><xxxx> 2021-04-05
  • 打赏
  • 举报
回复
支持一个"><xxxxx>
  • 打赏
  • 举报
回复
gcc在windows下也使用的win64 ABI,win64 ABI中只有一种调用约定,fastcall,没有cdecl、 stdcall、pascal这些。源码中虽然可以写_-cdecl、 __stdcall之类的修饰,但是在win64编译器中不起作用,只是出于源码兼容的考虑允许这么写,实际被忽略了。
六道佩恩 2021-04-04
  • 打赏
  • 举报
回复
引用 7 楼 Intel0011 的回复:
[quote=引用 楼主 六道佩恩 的回复:]调试查看汇编是通过寄存器传的 但《程序员的自我修养》的“调用惯例”却只说了用栈,没有提用寄存器 连标了__cdecl的库函数,调用时都是通过寄存器的,为什么?不应该是通过栈吗?
x86上函数参数是压栈传递的,但使用时需要从栈上取出放入寄存器中,或用寄存器作为指针指向栈上的数据, 并没有使用寄存器传递参数,但不绝对,C++就使用了ecx传递了对象的this指针 x64中函数参数传递发生了改变 借PC处理器架构由x86向x64过渡之机,MS清理了Windows x64平台上的函数调用约定, 由原来的数种包括stdcall、thiscall、fastcall、cdecl、pascal等,统一为一种新的fastcall调用方式。 这种调用方式得益于x64平台寄存器数量的增加。    Windows x64平台fastcall调用约定的主要特性如下: 00. 前四个整型或指针类型参数由RCX、RDX、R8、R9依次传递,前四个浮点类型参数由XMM0、XMM1、XMM2、XMM3依次传递。 01. 调用函数为前四个参数在调用栈上保留相应的空间,称作shadow space或spill slot。 即使被调用方没有或小于4个参数,调用函数仍然保留那么多的栈空间,这有助于在某些特殊情况下简化调用约定。 02. 除前四个参数以外的任何其他参数通过栈来传递,从右至左依次入栈。 03. 由调用函数负责清理调用栈。[这是不同于x86下fastcall的地方,x86下的fastcall由被调用者清理栈] 04. 小于等于64位的整型或指针类型返回值由RAX传递。 05. 浮点返回值由XMM0传递。 06. 更大的返回值(比如结构体),由调用方在栈上分配空间,并由RCX持有该空间的指针并传递给被调用函数, 因此整型参数使用的寄存器依次右移一格,实际只可以利用3个寄存器,其余参数入栈。函数调用结束后,RAX返回该空间的指针。 07. 除RCX,RDX,R8,R9以外,RAX、R10、R11、XMM0~XMM5也是易变化的(volatile)寄存器。 08. RBX, RBP, RDI, RSI, R12, R13, R14, R15和XMM6~XMM15寄存器则必须在使用时进行保护。 09. 在寄存器中,所有参数都是右对齐的。小于64位的参数并不进行高位零扩展,也就是高位是无法预测的垃圾数据。 The calling convention for C++ is very similar: the this pointer is passed as an implicit first parameter. The next three parameters are passed in registers, while the rest are passed on the stack. x64 provides a new rip-relative addressing mode. Instructions that refer to a single constant address are encoded as offsets from rip. For example, the mov rax, [addr] instruction moves 8 bytes beginning at addr + rip to rax. Linux、BSD和Mac OS X系统中的应用程序,会优先使用RDI、RSI、RDX、RCX、R8、R9这6个寄存器传递函数所需的前6个参数,然后使用数据栈传递其余的参数。 [/quote] 老哥你太稳了,不仅释疑,还这么细节,懂起了,等我再凑点分
六道佩恩 2021-04-04
  • 打赏
  • 举报
回复
引用 6 楼 真相重于对错 的回复:
1、用栈和用寄存器都可以达到传递参数的目的 2、c/c++只规定了语言部分,并没有规定实现部分。 3、__stdcall....这些只是微软 ,gun c/c++的厂家的自己实现。这些不是c/c++的标准的关键词。 4、如果用到寄存器,却发生中断,也没有问题,因为中断首先就要保存寄存器,中断结束后恢复寄存器。 5、我用vs看基本还是用栈。
gcc我看64位用的寄存器,32位用的栈。 我知道这不是语言的部分,但问题是,既然GCC自己规定了,为什么64位的时候它自己又不遵守了?
六道佩恩 2021-04-04
  • 打赏
  • 举报
回复
引用 11 楼 突触 的回复:
[quote=引用 4 楼 六道佩恩 的回复:][quote=引用 1 楼 突触 的回复:]大部分的调用约定都是用栈的,fastcall 会用寄存器不过参数多的话,也会用栈。 虽然寄存器运算快,但寄存器的数量是有限的,且大部分寄存器都有其特定的用途,当参数占用了寄存器,如果不通过内存空间存储的话,就没办法进行复杂的运算了。。。。。
看汇编,实际还是用的寄存器,没按调用约定来[/quote] 你说的是__cdecl 调用使用了寄存器传参吗 如果是64位的话会出现这种情况。 但32位 我没有遇到过这种情况你可以发张图片看看嘛 [/quote] putchar的声明:

int __cdecl putchar(int _Ch);
代码:

	int a='A';
	putchar( a );
64位编译后的汇编:

   0x000000000040153d <+13>:	mov    DWORD PTR [rbp-0x4],0x41
   0x0000000000401544 <+20>:	mov    eax,DWORD PTR [rbp-0x4]
   0x0000000000401547 <+23>:	mov    ecx,eax
   0x0000000000401549 <+25>:	call   0x402af8 <putchar>
32位:

   0x0040150e <+14>:	mov    DWORD PTR [esp+0x1c],0x41
   0x00401516 <+22>:	mov    eax,DWORD PTR [esp+0x1c]
   0x0040151a <+26>:	mov    DWORD PTR [esp],eax
   0x0040151d <+29>:	call   0x4025f8 <putchar>
哦,这么看,32位确实用栈了(是吧?),不过为啥没用push? 看了下自定义的函数,32位的确实也用栈了,64位的则没有用栈
赵4老师 2021-04-04
  • 打赏
  • 举报
回复
看一下微软的新调用约定
Intel0011 2021-04-04
  • 打赏
  • 举报
回复
引用 楼主 六道佩恩 的回复:
调试查看汇编是通过寄存器传的 但《程序员的自我修养》的“调用惯例”却只说了用栈,没有提用寄存器 连标了__cdecl的库函数,调用时都是通过寄存器的,为什么?不应该是通过栈吗?
x86上函数参数是压栈传递的,但使用时需要从栈上取出放入寄存器中,或用寄存器作为指针指向栈上的数据, 并没有使用寄存器传递参数,但不绝对,C++就使用了ecx传递了对象的this指针 x64中函数参数传递发生了改变 借PC处理器架构由x86向x64过渡之机,MS清理了Windows x64平台上的函数调用约定, 由原来的数种包括stdcall、thiscall、fastcall、cdecl、pascal等,统一为一种新的fastcall调用方式。 这种调用方式得益于x64平台寄存器数量的增加。    Windows x64平台fastcall调用约定的主要特性如下: 00. 前四个整型或指针类型参数由RCX、RDX、R8、R9依次传递,前四个浮点类型参数由XMM0、XMM1、XMM2、XMM3依次传递。 01. 调用函数为前四个参数在调用栈上保留相应的空间,称作shadow space或spill slot。 即使被调用方没有或小于4个参数,调用函数仍然保留那么多的栈空间,这有助于在某些特殊情况下简化调用约定。 02. 除前四个参数以外的任何其他参数通过栈来传递,从右至左依次入栈。 03. 由调用函数负责清理调用栈。[这是不同于x86下fastcall的地方,x86下的fastcall由被调用者清理栈] 04. 小于等于64位的整型或指针类型返回值由RAX传递。 05. 浮点返回值由XMM0传递。 06. 更大的返回值(比如结构体),由调用方在栈上分配空间,并由RCX持有该空间的指针并传递给被调用函数, 因此整型参数使用的寄存器依次右移一格,实际只可以利用3个寄存器,其余参数入栈。函数调用结束后,RAX返回该空间的指针。 07. 除RCX,RDX,R8,R9以外,RAX、R10、R11、XMM0~XMM5也是易变化的(volatile)寄存器。 08. RBX, RBP, RDI, RSI, R12, R13, R14, R15和XMM6~XMM15寄存器则必须在使用时进行保护。 09. 在寄存器中,所有参数都是右对齐的。小于64位的参数并不进行高位零扩展,也就是高位是无法预测的垃圾数据。 The calling convention for C++ is very similar: the this pointer is passed as an implicit first parameter. The next three parameters are passed in registers, while the rest are passed on the stack. x64 provides a new rip-relative addressing mode. Instructions that refer to a single constant address are encoded as offsets from rip. For example, the mov rax, [addr] instruction moves 8 bytes beginning at addr + rip to rax. Linux、BSD和Mac OS X系统中的应用程序,会优先使用RDI、RSI、RDX、RCX、R8、R9这6个寄存器传递函数所需的前6个参数,然后使用数据栈传递其余的参数。
突触 2021-04-04
  • 打赏
  • 举报
回复
引用 4 楼 六道佩恩 的回复:
[quote=引用 1 楼 突触 的回复:]大部分的调用约定都是用栈的,fastcall 会用寄存器不过参数多的话,也会用栈。 虽然寄存器运算快,但寄存器的数量是有限的,且大部分寄存器都有其特定的用途,当参数占用了寄存器,如果不通过内存空间存储的话,就没办法进行复杂的运算了。。。。。
看汇编,实际还是用的寄存器,没按调用约定来[/quote] 你说的是__cdecl 调用使用了寄存器传参吗 如果是64位的话会出现这种情况。 但32位 我没有遇到过这种情况你可以发张图片看看嘛
真相重于对错 2021-04-04
  • 打赏
  • 举报
回复
1、用栈和用寄存器都可以达到传递参数的目的 2、c/c++只规定了语言部分,并没有规定实现部分。 3、__stdcall....这些只是微软 ,gun c/c++的厂家的自己实现。这些不是c/c++的标准的关键词。 4、如果用到寄存器,却发生中断,也没有问题,因为中断首先就要保存寄存器,中断结束后恢复寄存器。 5、我用vs看基本还是用栈。
  • 打赏
  • 举报
回复
cdecl调用约定肯定是通过栈传参的
  • 打赏
  • 举报
回复
cdecl调用约定肯定格式通过栈传参的。如果“连标了__cdecl的库函数,调用时都是通过寄存器的”,要么是inline了,要么你看错了
六道佩恩 2021-04-03
  • 打赏
  • 举报
回复
引用 3 楼 forever74 的回复:
x64里面通用寄存器比原来多了,够用了,不用白不用啊。 苹果M1又掀开了新的历史,以后的硬件会越来越个性,我们会越来越理解语言规范里留白的魅力。
用寄存器遇上中断咋办?用栈的话好歹是保存到内存的 主要是,它是标了__cdecl这类使用栈的调用约定的呀,如果不用的话,就不应该标呀,不至于编译器都改了,库的调用约定的实现也该了,但标称的__cdecl却不去掉或者更换?这不合理啊 32位和64位版都用的寄存器,不知道是不是检测了CPU型号才这样做的
加载更多回复(4)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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