函数调用时发生了什么

第一步:函数调用

1、对实参表从右向左,一次计算出实参的值,并且将值压栈。

2、将函数调用语句下一条语句的地址保存到在栈中,以便函数调用完成后返回。(压栈)

3、跳转到函数体处。

第二步:函数体执行

4、如果函数体中定义了变量,将变量压栈

5、将每一个形参以栈中对应的实参值取代,执行函数体的功能体。

6、将函数体中的变量、保存到栈中的实参值,依次从栈中取出,释放栈空间(出栈)。

第三步:返回

7、返回过程执行的是函数体中的return语句。其过程是从栈中取出刚开始调用函数时压入的地址,跳转到函数的下一条语句。当return语句不带有表达式时,按照保存的地址返回,当return语句带有表达式时,将计算出的return表达式的值保存起来,然后再返回。


函数的调用方和被调用方对于函数如何调用需要遵守同样的约定,函数才能被正确地调用,这样的约定称为**调用惯例**。

* 函数参数的传递顺序和方式
调用惯例要规定参数压栈的顺序:是从左至右,还是从右至左。有些调用惯例还允许使用寄存器传递参数,以提高性能。
*
栈的维护方式
在被调函数返回时,需要将被压入栈中的参数全部弹出,以使得栈在函数调用前后保持一致。这个弹出的工作可以由函数的调用方完成,也也可以由被函数完成。
*
名字修饰规则
为了链接的时候对调用惯例进行区分,调用惯例要对函数本身的名字进行修饰,不同的调用惯例有不同的名字修饰策略。

函数返回值传递

一般情况下,寄存器eax是传递返回值的通道,函数将返回值存储在eax中,返回后函数的调用方再读取eax。但是eax本身只有4字节,那么大于4字节的返回值是如何传递的呢?

对于返回5~8 字节数据的情况,一般采用eax和edx联合返回的方式进行的。其中eax存储返回值的低4字节,edx存储返回值的高4字节。 那么实际上,编译器是怎么设计大尺寸返回值传递的呢?

【总结】
函数返回值的传递:小于8字节的返回值,以寄存器为中转。大于8字节的,以主调函数中新开辟的同样大小的中间变量temp为中转。C语言对于尺寸太大的返回值类型,会使用一个临时的栈上内存区域作为中转,结果返回值对象会被拷贝两次。故不到万不得已,不要轻易返回大尺寸对象。 

C++函数的返回值传递

C++ 处理大返回值略有不同,其可能是像C那样,1次拷贝到栈上的临时对象里,然后把临时对象拷贝到存储返回值的对象里。但,有些编译器会进行返回值优化RVO(Return Value Optimization),这样,对象拷贝会减少一次,即没有临时对象temp了,直接拷贝到主调函数的相应对象中。

猜你喜欢

转载自blog.csdn.net/E_ROAD_BY_U/article/details/75914477