良许Linux教程网 干货合集 c/c++参数入栈顺序和参数计算顺序

c/c++参数入栈顺序和参数计算顺序

如果大家留意的话,应该会知道C/C++语言中函数参数的入栈顺序是从右至左的。那么为什么会是这样呢?我们来看看两个相关的知识点:参数的计算顺序和压栈顺序。

参数入栈顺序

在C/C++中,规定了函数参数的压栈顺序是从右至左的。函数调用的协议会影响函数参数入栈的方式、栈内数据的清除方式以及编译器对函数名的修饰规则等方面。

参数传递和命名约定

Visual C/C++编译器支持以下调用约定。

关键字 堆栈清理 参数传递
__cdecl 调用者 将参数以相反的顺序(从右到左)压入堆栈
__clrcall N/A 将参数按顺序(从左到右)加载到CLR表达式堆栈
__stdcall 被调用者 将参数以相反的顺序(从右到左)压入堆栈
__fastcall 被调用者 将参数存储在寄存器中,然后压入堆栈
__thiscall 被调用者 将参数压入堆栈;此指针存储在ECX中
__vectorcall 被调用者 将参数存储在寄存器中,然后以相反的顺序(从右到左)压入堆栈

官方文档详细解释可参考:

https://msdn.microsoft.com/en-us/library/984x0h58(v=vs.120).aspx

通常情况下,C/C++默认使用__cdecl的入栈方式,也就是从右到左将参数压入堆栈。Windows API使用的是__stdcall方式,而__fastcall适用于对性能要求较高的场合。

自定义参数入栈方式

当然,我们也可以自定义函数的参数入栈顺序。常见的形式如下所示:

//函数返回值  入栈规则  函数名(参数类型 参数名);
int __cdecl get_name_index(const std::string& str_name);

为什么要从右往左入栈?

每个参数都有自己的地址,但不定长参数无法确认地址,并且函数参数的个数也不确定,C/C++中规定了函数参数的压栈顺序是从右至左,对于含有不定参数的printf函数,其原型是printf(const char* format,…);其中format确定了printf的参数(通过format的%个数判断)。

假设是从左至右压栈,那么先入栈的是format,然后依次入栈未知参数,此时想要知道参数个数,就必须找到format,而要找到format,就必须知道参数个数,这样就会陷入一个死胡同里面了。

而c/c++中规定参数压栈为从右至左的顺序,这种方式对于不定参数,最后入栈的是参数个数,只需要取栈顶就可以得到。

我们举一个了例子如下:

//win10+vs2019
//来源:技术让梦想更伟大
//作者:李肖遥
#include 
using namespace std;

void fun(int x, int y, int z)
{
    cout "pause");
    return 0;
}

输出结果如下:

image-20240126203223639
image-20240126203223639

我们知道先入栈的占高地址,从结果看出入栈的顺序依次为z->y->x,即压栈顺序从右至左。

参数计算顺序

先执行哪个参数和参数的计算顺序有关,而c/c++中没有规定函数参数的计算顺序,这个和编译器有关,代码参数的计算顺序决定了实际输出。

//来源:技术让梦想更伟大
//作者:李肖遥
#include 

int main () {
 int a = 2;
 printf("%d, %d, %d", a, (a = (a + 2)), (a = (a + 3)));
 system("pause");
 return 0;
}
//win10 + VS2019 输出: 7, 7, 7
//clang输出结果 2 4 7 

vs的计算顺序是从右至左,clang的计算顺序是从左至右,具体的计算流程分析就很简单了。

对于c/c++函数参数的读取顺序,参数入栈时顺序从右向左入栈,但是在入栈前会先把参数列表里的表达式从右向左算一遍得到表达式的结果,最后再把这些运算结果统一入栈。

在参数入栈前,编译器会先把参数的表达式都处理掉,对于一般的操作来说,参数入栈时取值是直接从变量的内存地址里取的,但是对于a++操作,编译器会开辟一个缓冲区来保存当前a的值,然后再对a继续操作,最后参数入栈时的取值是从缓冲区取,而不是直接从a的内存地址里取。

结论

因为函数参数的计算顺序依照编译器的实现,所以在编码中避免编写诸如 fun(++x, x+y)这种的程序,其在不同的平台得到的结果可能不一样,但是在面试中可能遇到这样的问题,所以我们需要知其然更要知所以然。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部