user-0 提问: 函数传参是通过寄存器还是栈？

回复:
user: user-1
content: 大部分都是用的栈吧

user: user-2
content: 调用约定 https://docs.microsoft.com/zh-cn/cpp/cpp/calling-conventions?view=vs-2017

user: user-3
content: 以前调试过一次 x86 平台 用栈 x64 前几个参数 是用通用寄存器

user: user-4
content: 引用 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

user: user-4
content: 可以不用push,直接操作栈寄存器就可以了，pop也是类似

user: user-5
content: 什么时候发现的？会不会与python有关？

user: user-5
content: 无怪代码越跑越快，程序越来越大。

user: user-6
content: xxxxxxxxxxxxxxxxxx

user: user-6
content: 支持一个"><xxxxx>

user: user-7
content: gcc在windows下也使用的win64 ABI，win64 ABI中只有一种调用约定，fastcall，没有cdecl、 stdcall、pascal这些。源码中虽然可以写_-cdecl、 __stdcall之类的修饰，但是在win64编译器中不起作用，只是出于源码兼容的考虑允许这么写，实际被忽略了。

user: user-0
content: 引用 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]  老哥你太稳了，不仅释疑，还这么细节，懂起了，等我再凑点分

user: user-0
content: 引用 6 楼 真相重于对错 的回复:1、用栈和用寄存器都可以达到传递参数的目的 2、c/c++只规定了语言部分，并没有规定实现部分。 3、__stdcall....这些只是微软 ,gun c/c++的厂家的自己实现。这些不是c/c++的标准的关键词。 4、如果用到寄存器，却发生中断，也没有问题，因为中断首先就要保存寄存器，中断结束后恢复寄存器。 5、我用vs看基本还是用栈。  gcc我看64位用的寄存器，32位用的栈。 我知道这不是语言的部分，但问题是，既然GCC自己规定了，为什么64位的时候它自己又不遵守了？

user: user-0
content: 引用 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位的则没有用栈

user: user-2
content: 看一下微软的新调用约定

user: user-8
content: 引用 楼主 六道佩恩 的回复:调试查看汇编是通过寄存器传的  但《程序员的自我修养》的“调用惯例”却只说了用栈，没有提用寄存器  连标了__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个参数，然后使用数据栈传递其余的参数。

user: user-9
content: 引用 4 楼 六道佩恩 的回复:[quote=引用 1 楼 突触 的回复:]大部分的调用约定都是用栈的，fastcall 会用寄存器不过参数多的话，也会用栈。  虽然寄存器运算快，但寄存器的数量是有限的，且大部分寄存器都有其特定的用途，当参数占用了寄存器，如果不通过内存空间存储的话，就没办法进行复杂的运算了。。。。。  看汇编，实际还是用的寄存器，没按调用约定来[/quote]  你说的是__cdecl 调用使用了寄存器传参吗       如果是64位的话会出现这种情况。 但32位 我没有遇到过这种情况你可以发张图片看看嘛

user: user-4
content: 1、用栈和用寄存器都可以达到传递参数的目的 2、c/c++只规定了语言部分，并没有规定实现部分。 3、__stdcall....这些只是微软 ,gun c/c++的厂家的自己实现。这些不是c/c++的标准的关键词。 4、如果用到寄存器，却发生中断，也没有问题，因为中断首先就要保存寄存器，中断结束后恢复寄存器。 5、我用vs看基本还是用栈。

user: user-7
content: cdecl调用约定肯定是通过栈传参的

user: user-7
content: cdecl调用约定肯定格式通过栈传参的。如果“连标了__cdecl的库函数，调用时都是通过寄存器的”,要么是inline了，要么你看错了

user: user-0
content: 引用 3 楼 forever74 的回复:x64里面通用寄存器比原来多了，够用了，不用白不用啊。  苹果M1又掀开了新的历史，以后的硬件会越来越个性，我们会越来越理解语言规范里留白的魅力。  用寄存器遇上中断咋办？用栈的话好歹是保存到内存的  主要是，它是标了__cdecl这类使用栈的调用约定的呀，如果不用的话，就不应该标呀，不至于编译器都改了，库的调用约定的实现也该了，但标称的__cdecl却不去掉或者更换？这不合理啊  32位和64位版都用的寄存器，不知道是不是检测了CPU型号才这样做的