原文:C/C++ 函数参数和返回值传递机制 说明 C/C++ 函数调用中,参数和返回值传递的机制,包括低级汇编指令和高级 C++ 对象拷贝构造 关键字:参数传递,返回值传递,按值传递 (passed by value),按引用传递 (passed by reference),拷贝构造 相关参考
函数调用栈示意图^调用顺序 func_1->func_2,调用时栈操作顺序从 高地址 到 低地址 示意图如下: ***低地址*** [ ... ] 2 [func_2 局部变量 2 ] 2 [func_2 局部变量 1 ] 2 [func_1 寄存器值: ECX, EBX, EDI, ESI 等 ] 2 保存 func_1 的运行环境 [func_2 预留栈空间 ] 2 用 sub esp, XXh 预留栈空间 [func_1 EBP ] 2 保存 func_1 EBP,然后 EBP <= ESP [func_1 中下一条 EIP ] 2 保存 func_2 调用完成后,func_1 的下一条指令地址 [func_2 返回值 ] 2 1 func_1 使用 func_2 的返回值,size 小的返回值通过寄存器传递 [func_2 第 1 个参数 ] 2 1 [func_2 第 2 个参数 ] 2 1 [... ] 2 1 IDA 中用 func_2 arg_XX 表示 [func_2 第 N 个参数 ] 2 1 C/C++ 参数入栈方式:从后到前 ***高地址*** 栈示意图后面的 1、2 表示哪个函数会访问这些存储 基本过程如此,但编译器之间略有差别,如 VC 调试方式编译,用 sub esp, XXh 预留栈空间等 C++ 中参数和返回值传递都是初始化语义 prolog 和 epilog
prolog 和 epilog 由编译器产生,但可使用 VC 的 __declspec(naked) 裸函数,手工编写 prolog 和 epilog,参考 MSDNConsiderations for Writing Prolog/Epilog Code 参数按值传递^以按值传递一个 POD 结构 Student 为例,说明汇编指令 编译器 Linux GCC 4 测试程序: struct Student { int id; // 学号 char* name; // 姓名 int age; // 年龄 int sex; // 性别 }; // 被调函数 int print_student(Student stu); // 调用函数 int main() { Student stu_1 = {12345, "LiMing", 21, 0}; print_student(stu); } IDA 调试 POD 参数传递^call print_student 指令及栈操作 ![]() print_student prolog 及栈操作 ![]() IDA 中两个函数参数相关汇编符号:
在一次函数调用中,var_XX 和 arg_XX 是同一存储的不同名称,在 caller 中用 var_XX 访问,在 callee 中用 arg_XX 访问 C++ 对象参数传递^传递 C++ 类对象时的拷贝构造,以及按引用传递、按地址(指针)传递参数时的汇编指令 编译器 VC 2010 测试程序: // 复数类模板 template<class Type> class Complex { // 省略无关代码 // copy ctor Complex(const Complex& right) : m_real(right.m_real), m_image(right.m_image) {} } // 被调函数 void some_func(Complex<double> complex_v1, Complex<double>& complex_v2, Complex<double>* complex_v3, double v1, double& v2, double* v3); // 调用函数 void caller() { Complex<double> complex_v1(1.2, 2.3); double v1 = 1.3; some_func(complex_v1, complex_v1, &complex_v1, 1.1, v1, &v1); } VC 调试 C++ 对象参数传递^下面是 caller 中调用 some_func() 的汇编指令:
0x00000000 0x00000004 0x00000008 0x0000000C . ... . 0x0012FB7C 98 fd 12 00 caller edi 0x0012FB80 78 fc 12 00 caller esi 0x0012FB84 00 60 fd 7f caller ebx . ... sub esp, 0C0h 预留 192 byte 栈空间 (some_func) . 0x0012FC48 98 fd 12 00 caller ebp 0x0012FC4C b1 25 41 00 调用 Complex copy ctor 时是 Complex copy ctor 的参数 right 调用 some_func() 时是 caller eip 以下是 some_func() 的参数 0x0012FC50 33 33 33 33 Complex copy ctor 构造的对象,this 指针 = 起始地址 0x0012FC50 0x0012FC54 33 33 f3 3f 浮点数 1.2 (64bit: 3ff3333333333333) 0x0012FC58 66 66 66 66 浮点数 2.3 (64bit: 4002666666666666) 0x0012FC5C 66 66 02 40 0x0012FC60 84 fd 12 00 Complex& 0x0012FC64 84 fd 12 00 Complex* 0x0012FC68 9a 99 99 99 double 64bit 0x0012FC6C 99 99 f1 3f 0x0012FC70 74 fd 12 00 double& 0x0012FC74 74 fd 12 00 double* 返回值按值传递^测试程序: class TestClass { public: // ctor TestClass(int d = 0) : m_data(d) { cerr << "TestClass ctor: " << this << endl; } // copy ctor TestClass(const TestClass& right) : m_data(right.m_data) { cerr << "TestClass copy ctor: " << this << " <= " << &right << endl; } // dtor ~TestClass() { cerr << "TestClass dtor: " << this << endl; } // assign TestClass& operator=(const TestClass& right) { cerr << "TestClass assign: " << this << "<=" << &right << endl; m_data = right.m_data; return *this; } template<class CharT> std::basic_ostream<CharT>& output(std::basic_ostream<CharT>& os) const { os << m_data; return os; } private: int m_data; }; // 被调函数 TestClass get_test_obj() { TestClass ret_obj(200); return ret_obj; } // 调用函数 int main() { TestClass obj = get_test_obj(); return 0; } 返回值传递步骤^
返回值传递测试结果^对上面程序的测试结果
返回值传递效率^
原文链接:http://blog.csdn.net/breakerzy/article/details/6196232
标签:
<无>
|
|