`
caozuiba
  • 浏览: 901915 次
文章分类
社区版块
存档分类
最新评论

C/C++ 函数参数和返回值传递机制

 
阅读更多

原文:C/C++ 函数参数和返回值传递机制
作者:Breaker <breaker.zy_AT_gmail>


说明 C/C++ 函数调用中,参数和返回值传递的机制,包括低级汇编指令和高级 C++ 对象拷贝构造

关键字:参数传递,返回值传递,按值传递 (passed by value),按引用传递 (passed by reference),拷贝构造

相关参考

目录

函数调用栈示意图^

调用顺序 func_1->func_2,调用时栈操作顺序从 高地址 到 低地址

示意图如下:

栈示意图后面的 1、2 表示哪个函数会访问这些存储

基本过程如此,但编译器之间略有差别,如 VC 调试方式编译,用 sub esp, XXh 预留栈空间等

C++ 中参数和返回值传递都是初始化语义

prolog 和 epilog
  • prolog: 进入 func_2 时的准备工作,保存 func_1 的环境,如 EBP 和其它寄存器值,预留栈空间等
  • epilog: 离开 func_2 时的恢复工作,恢复 func_1 的环境

prolog 和 epilog 由编译器产生,但可使用 VC 的 __declspec(naked) 裸函数,手工编写 prolog 和 epilog,参考 MSDNConsiderations for Writing Prolog/Epilog Code

参数按值传递^

以按值传递一个 POD 结构 Student 为例,说明汇编指令

编译器 Linux GCC 4
编译命令 g++ -O0 -g3 -Wall

测试程序:

IDA 调试 POD 参数传递^

call print_student 指令及栈操作

call print_student 指令及栈操作

print_student prolog 及栈操作

print_student prolog 及栈操作

IDA 中两个函数参数相关汇编符号:

  • var_XX: caller 访问 callee 的参数使用的符号,表示为:相对于 caller ESP+XXh 的偏移量 [ESP+XXh+var_XX]
  • arg_XX: callee 访问自己参数使用的符号,表示为:相对于 callee EBP 的偏移量 [EBP+arg_XX]

在一次函数调用中,var_XX 和 arg_XX 是同一存储的不同名称,在 caller 中用 var_XX 访问,在 callee 中用 arg_XX 访问

C++ 对象参数传递^

传递 C++ 类对象时的拷贝构造,以及按引用传递、按地址(指针)传递参数时的汇编指令

编译器 VC 2010
编译命令 cl /Od /MDd /Zi /EHsc (Debug)

测试程序:

VC 调试 C++ 对象参数传递^

下面是 caller 中调用 some_func() 的汇编指令:

  1. 按引用传递 和 按地址(指针)传递

    两者的汇编指令相似:

  2. 按值传递 double 字面量

  3. 按值传递 Complex 对象

    调用 Complex copy ctor 拷贝构造对象:

  4. 调用 some_func()

堆栈数据

返回值按值传递^

测试程序:

返回值传递步骤^

  1. callee 用返回对象 ret_obj 初始化 class TestClass 的 返回值临时对象

    临时对象的销毁时机

    参考 "The C++ Programming Language"

    临时对象在维持它的那条语句之后被销毁,除非临时对象被约束到其它名字,此时由这个名字控制临时对象的生存期,约束不产生初始化或赋值语义,没有拷贝

  2. callee 返回时,由 ret_obj 的存储方式,决定是否销毁

    如果 ret_obj 是局部对象或 callee 参数,则在返回时销毁

  3. caller 中根据对 callee 返回值的使用,会有不同的情况,常见如下:

    • 用返回值赋值

    • 用返回值初始化

      上面 3 者效果相同,均将返回值临时对象约束到 obj 或 obj_ref,期间只有一个对象本体,就是返回值临时对象,没有拷贝,之后由约束名字控制其生存期

    • 即时使用返回值而不保存

返回值传递测试结果^

对上面程序的测试结果

  • 编译器 VC 2010
    编译命令 cl /Od /MDd /Zi /EHsc (Debug)

    运行结果:

  • 编译器 MinGW GCC 4
    编译命令 g++ -O0 -g3 -Wall

    运行结果:

    GCC 返回值传递优化和 VC 的区别
    • GCC 不创建额外的返回值临时对象(即使 -O0 关闭优化),而直接将 callee 的局部对象作为返回值临时对象,被调函数返回后,将其栈交给调用函数控制
    • 初始化返回值到非 const 引用 TestClass& obj = get_test_obj() 时,编译报错,而 VC 不报错,应初始化到 const 引用 const TestClass& obj_ref = get_test_obj()

返回值传递效率^

  • 因为拷贝开销,一般 不建议返回对象,除非:

    • 返回值是内部类型,如 整数、浮点数、枚举、指针、数组名等
    • 返回值是小 size 类型的对象,如 Point、Rect 等 POD,或 smart pointer 等小型封装类
  • 返回引用类型实际是 caller 直接访问 callee 中返回值对象的别名,没有拷贝开销

  • 返回局部变量时,可使用 返回时构造 技巧,如:

    return 语句中的 Complex(2.3, 1.2) 即是返回值临时对象,不调用 copy ctor 创建第 2 个临时对象,返回时没有销毁局部对象的开销

分享到:
评论

相关推荐

    C/C++ 函数调用与函数返回值

    函数参数与函数调用  1、函数调用的作用:  ·用实参数向形式参数传递数据;  ·为获得数据参数及在函数体中声明的变量分配存储空间;  ·中断现行(调用)函数,把流程向被调用函数的入口处,开始执行被调...

    EDA/PLD中的C/C++ 函数调用与函数返回值

    函数参数与函数调用  1、函数调用的作用:  ·用实参数向形式参数传递数据;  ·为获得数据参数及在函数体中声明的变量分配存储空间;  ·中断现行(调用)函数,把流程向被调用函数的入口处,开始执行被调...

    C++函数调用传参与返回值深度分析

    也许你从书上了解到了C++的函数参数和返回值类型有类对象,引用,指针。 但是却不知道在内存中到底是怎么回事。本文档从内存堆栈分别揭示了这6种情况下到底在这个过程中发生了什么事情。

    NDKC调用Java函数传参或获取变量

    NDK开发时,C/C++调用Java的函数的一些案例; 传递int类型参数: https://blog.csdn.net/niuba123456/article/details/80978500 传递String类型参数: https://blog.csdn.net/niuba123456/article/details/80978916 ...

    C#调用C++动态库,执行回调函数并传递结构体参数

    c#调用C++动态库、执行回调函数,并回传结构体参数数据。vs2017环境编写C#和C++动态库,这个为完整工程例子,可供相关人员学习参考。

    C++调用Python脚本

    完整的C++调用Python脚本源码,包含参数传递和函数返回值处理等,且解决传递中文参数出现乱码等问题

    golang调用c++DLL传递char*型参数读取返回char*字符串内容

    golang调用c++DLL返回值为char*参数为const char*,读取返回DLL返回值,传递char*型参数

    高质量C/C++编程指南(PDF)

    第8 章 C++函数的高级特性 8.1 函数重载的概念. 8.2 成员函数的重载、覆盖与隐藏. 8.3 参数的缺省值. 8.4 运算符重载. 8.5 函数内联. 8.6 一些心得体会. 第9 章 类的构造函数、析构函数与赋值函数 9.1 构造函数与析...

    进程间函数调用

    remote-function底层使用命令管道进行通讯,内置的流程完成了函数参数和返回值的序列化过程。且大部分的参数和返回值都可以使用内置的(反)序列化方法,对于一些复杂的数据类型,需要自己(反)序列化。 项目首页...

    lowkeyway#Embedded#参数传递和返回值传递1

    1. 函数参数,默认调用惯例情况下从右向左的顺序依次把参数压入栈中 2. 函数的返回地址,即调用方调用此函数(如call func1)的下一条指令的地址 3.

    高质量C++编程指南.PDF

    第8章 C++函数的高级特性 8.1 函数重载的概念 8.2 成员函数的重载、覆盖与隐藏 8.3 参数的缺省值 8.4 运算符重载 8.5 函数内联 8.6 一些心得体会 第9章 类的构造函数、析构函数与赋值函数 9.1 构造函数与析构函数的...

    计算机语言中c/c++的基础入门学习代码记录(适应于计算机二级)

    06函数返回值为指针类型;07数组和指针;08指针数组;09多级指针) 9.结构体(01typedef语句;02结构体定义和使用;03结构体数组;04结构体数组排序;05结构体赋值;06结构体嵌套;07共用体) 10.位运算(01位运算;...

    经典C/C++面试题目大汇总(全附答案).doc

    将“引用”作为函数返回值类型的格式、好处和需要遵守的规则? 格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 } 好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个...

    c/c++ 学习总结 初学者必备

    如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,由于“指向指针的指针”这个概念不容易理解,我们也可以用函数返回值来传递动态内存。 常常有人把return语句用错了。这里强调不要用return语句...

    c++语言介绍.ppt

    C++源于C语言。 1970年,两位程序员Brian Kernighan和Dennis Ritchie首创了一种新的程序设计语言,取名...函数、参数传递与函数返回值 函数名重载与操作符重载 动态存储分配 友元函数与内联函数 结构、联合与类

    高质量C/C++编程指南.pdf

    《高质量C/C++编程指南》,作者:林锐,pdf 格式,大小 327KB。 目录: 前 言 ..................................................................................................................................

    免费下载:C语言难点分析整理.doc

    7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. ...

    c语言难点分析整理,C语言

    7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. ...

    C/C++回调函数介绍

    如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。 为什么要使用回调函数? 因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它...

    高级C语言 C 语言编程要点

    7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. ...

Global site tag (gtag.js) - Google Analytics