大佬教程收集整理的这篇文章主要介绍了为什么函数的参数在 C 中以相反的顺序填充到寄存器中?,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
这是一个简单的函数:
voID dab(int a,int b,int c,int d) {
printf("Hallo: %d,%d,%d\n",a,b,c,d);
}
除了打印参数只是为了表明它们被正确传递,它几乎什么都不做。
现在,我将调用函数:
int main() {
dab(6,7,8,9);
}
简单的通话,没什么奇怪的。查看调用函数的 gcc 9.4.0 编译的程序集:
@H_826_5@movl $9,%ecx movl $8,%edx movl $7,%esi movl $6,%edi //Goes in reverse,right to left call dab
从逻辑上讲,人们会认为它会从左到右填充(我们打字的方式)。按照这个顺序:6、7、8、9。但它是从右到左。按照这个顺序:9、8、7、6。像这样从左到右填充它不是更有意义吗:
@H_826_5@movl $6,%edi movl $7,%esi movl $8,%edx movl $9,%ecx //Goes from left to right,thE intuitive way
我知道这没什么区别,但是在编码时,为了可读性,直觉和简单性不是很重要吗?以防万一,我测试了它,两种填充方式都给出了相同的结果
你好:6、7、8、9
如果参数从左到右而不是从左填充,对编译器制造商来说不是更容易阅读和更有意义吗?
顺便说一句,你好,这不是打字错误。目的是
这是为了方便被调用者:如果参数按“调用”顺序推送,那么被调用者可用的“第一个”参数将是调用的“最后一个”。
param0
param1
param2
param3
return address
<- top of stack is here
在尝试计算参数时是否必须反转所有偏移量。
通过以相反的顺序推送,被调用者以更合理的顺序获取参数:参数 0 位于偏移量 0(+ 一些常量偏移量)处:
param3
param2
param1
param0
return address
<- TOS
当被调用的函数采用可变数量的参数时,这一点更加重要:如果被调用者不知道参数的数量,如果按调用顺序推送,它甚至无法访问“固定参数”。而以相反的顺序,它可以轻松访问固定参数(例如传入的参数数量),或者它可以轻松使用占位符方案,例如0
或 null
表示参数列表的结尾。
我知道这没什么区别,但是在编码时,为了可读性,直觉和简单性不是很重要吗?如果参数从左到右填充而不是从左填充,不是更容易阅读并且对编译器制造商更有意义吗?
生成的程序集的可读性通常并不重要。只有源代码的可读性。当开启优化时,汇编代码在可读性方面通常会变得一团糟。
此外,这里最有意义的是高度依赖平台。
,这顶多是历史的回声,对 x86 架构的现代使用没有任何意义。
在早期的计算机中,如果您希望参数 A、B 和 C 以该顺序出现在堆栈中,以便被调用的函数在从堆栈中“弹出”参数时会按该顺序看到 A、B 和 C,那么你会以相反的顺序压入它们,C、B 和 A,因为堆栈是后进先出。
现代系统中的当前做法主要是一次保留整个堆栈帧,然后使用帧指针的偏移量写入帧内的位置。我们可以按照我们想要的任何顺序编写参数 A、B 和 C,并通过使用这些位置的偏移量将它们放到堆栈中的正确位置。在较旧的机器中,push 或 pop 指令可能更受欢迎,因为它们比一般的 store-to-some-offset 指令短。因此,使用这些指令需要以与弹出所需的顺序相反的顺序进行推送。但我们不再受这些指令的束缚。
由于某些参数在使用堆栈之前在寄存器中传递这一事实而变得更加复杂。尽管如此,在编译器的某个地方,有一些代码可以分析函数调用并生成一些内部编译器语言代码以将参数传递给被调用的函数。该代码可能会以相反的参数顺序生成该代码,或者作为代码过去编写方式的回声,或者作为函数调用解析方式的巧合。如果不使用优化,则可能会直接实现该内部代码。
但是,当启用优化时,操作可能会重新排序。当参数是复合表达式,而不仅仅是常量或标识符时,尤其如此。如果参数 A 和 C 是一些共享某些公共子表达式的表达式,例如计算数组中的位置,那么优化器很可能会计算该公共表达式并将参数 A 和 C 存储在其指定的寄存器中,然后再继续处理参数 B。
因此,您看到的效果可能只是代码编写方式的偶然事件,可能受语言解析器编写方式或历史的影响,但在现代实用性中没有意义,并且在启用优化时可能会消失.
,为什么?因为除了个人喜好之外,从右到左与从左到右一样好。并且个人偏好对于不打算由人类阅读的代码没有太大意义。这只是编译器选择的约定。
你看到的是编译器的一个实现细节(显然你有一个编译器决定在寄存器而不是堆栈中传递一些参数,这将是“传统”方式)。 CPU 不会真正关心寄存器加载的指令顺序,因为它们(在您的示例中)根本不相关。
事实上,大多数编译器似乎都是按照从右到左的顺序进行参数传递的,这可能是因为传统上,C 编译器过去常常在堆栈上传递参数,并希望堆栈中最顶部的参数是参数中的第一个列表(但这只是另一种约定)。
以上是大佬教程为你收集整理的为什么函数的参数在 C 中以相反的顺序填充到寄存器中?全部内容,希望文章能够帮你解决为什么函数的参数在 C 中以相反的顺序填充到寄存器中?所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。