在C++中,函數壓棧(函數調用)和出棧(函數返回)是函數調用過程中的兩個關鍵步驟。下面將逐步解釋這兩個過程:
一??函數壓棧與出棧過程簡介
函數壓棧(函數調用)的過程如下:
- 調用指令:在函數調用點,會發出一個調用指令(如call指令),將控制權轉移到被調用函數的入口點。
- 保存返回地址:調用指令執行前,當前函數的返回地址會被壓入棧中,以便在函數執行完畢后返回到正確的位置。
- 參數壓棧:函數調用時,將函數的參數按照一定的順序壓入棧中。通常,參數從右至左依次入棧。
- 保存寄存器值:在一些體系結構中,函數調用時需要保存一些寄存器的值,以便在函數執行完畢后能夠恢復原始的寄存器狀態。
- 幀指針與局部變量壓棧:為了支持函數內的局部變量和堆棧的動態分配,通常會在棧上維護一個幀指針(frame pointer),它指向當前函數的棧幀(stack frame)的底部。同時,函數內部定義的局部變量也會在棧上分配空間。
- 執行函數體:一旦函數的參數、局部變量和其他上下文信息都被壓入棧中,函數體中的代碼開始執行。
函數出棧(函數返回)的過程如下:
- 恢復寄存器值:在一些體系結構中,函數返回時需要恢復之前保存的寄存器的值。
- 釋放局部變量和幀指針:函數返回后,會釋放函數內部定義的局部變量所占用的棧空間,并將幀指針恢復到上一層函數的棧幀。
- 彈出參數和返回地址:函數返回后,參數和返回地址會從棧中彈出,將控制權返回到調用函數的正確位置。
二?參數入棧順序
c/c++中規定了函數參數的壓棧順序是從右至左
可以看如下例子:
// person.h
#include<string>class Person
{
public:Person(std::string name);Person(const Person& p);~Person();private:std::string m_name;
};//------------------------// person.cpp#include "person.h"
#include<iostream>
Person::Person(std::string name):m_name(name)
{std::cout << "Person constructor name: " << m_name << std::endl;
}Person::~Person()
{std::cout << "Person destructor name: " << m_name << std::endl;
}Person::Person(const Person& p)
{this->m_name = p.m_name;std::cout << "Person copy constructor name: " << this->m_name << std::endl;
}// -----------------------
// main.cpp
#include<iostream>
#include "person.h"void testFunc1(Person p1, Person p2, Person p3)
{Person pp1("pp1");Person pp2("pp2");
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);Person p1("p1 ----");Person p2("p2 ----");Person p3("p3 ----");std::cout << " --------------" <<std::endl;testFunc1(p1,p2,p3);return a.exec();
}
?從輸出結果可以看出,先執行的最右邊的參數?p3 的拷貝構造函數,再依次執行 p2 、p1 參數?
參考鏈接:c/c++參數入棧順序和參數計算順序 - 知乎 (zhihu.com)
? ? ? ? ? ? ? ?C++ 函數壓棧與出棧 - 知乎