第一步:函数调用
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了,直接拷贝到主调函数的相应对象中。