我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C/C++函数原理传参示例详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C/C++函数原理传参示例详解

x84-64的寄存器

本文所用gccx86-64 gcc 10.1

wiki.cdot.senecacollege.ca/wiki/X86_64…

rax - register a extended

rbx - register b extended

rcx - register c extended

rdx - register d extended

rbp - register base pointer (start of stack)

rsp - register stack pointer (current location in stack, growing downwards)

rsi - register source index (source for data copies)

rdi - register destination index (destination for data copies)

其他寄存器: r8 r9 r10 r11 r12 r13 r14 r15

函数是个什么东西?

一个简单的函数

int func(){}
int main() {
    int x = 2;
    func();
}
main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        movl    $2, -4(%rbp)
        call    func()
        movl    $0, %eax
        leave
        ret

分配空间动作如下所示:

这里加了个函数调用是因为在有些时候,没有函数调用,就不会使用subq $16, %rsp 这一条指令,我的猜想是既然你都是栈顶的,并且不会再有rbp的变化,那么栈顶以上的元素我都可以随便用。
并且我们观察可以得知,分配栈空间时,他是分配的16个字节,也就是说,有对齐
返回时,弹出栈顶,就可以恢复到上一个栈帧的状态了。

传参姿势

入栈规则

c/c++ 中规定的函数压栈顺序是从右到左,当然,如果你是 Visual C/C++的话,它们有更多的玩法 比如:

template<typename T>
T val(T t) {
  cout << t << endl;
  return t;
}
signed main() {
  printf("%d%d%d", val(1), val(2), val(3));
  return 0;
}

结果

3
2
1
123

看看汇编

int func(int x, int y, int z) {
  return 0;
}
int main() {
  func(1, 2, 3);
}

生成的汇编

func(int, int, int):
        pushq   %rbp
        movq    %rsp, %rbp
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    %edx, -12(%rbp)
        movl    $0, %eax
        popq    %rbp
        ret
main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $3, %edx
        movl    $2, %esi
        movl    $1, %edi
        call    func(int, int, int)
        movl    $0, %eax
        popq    %rbp
        ret

上文中可以看出,也证实了我们所观察到的,首先把3传给了edx,2传给了esi,1传给了edi

全都存寄存器吗?

寄存器毕竟少,当然,还可以存在栈上嘛

int fun() {return 0;}
int func(int x, int y, int z, int a, int b, int c, int d, int e, int f){
    fun();
    return e;
}
int main() {
    func(1, 2, 3, 4, 5, 6, 7, 8, 9);
    return 0;
}
fun():
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $0, %eax
        popq    %rbp
        ret
func(int, int, int, int, int, int, int, int, int):
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $24, %rsp
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        movl    %edx, -12(%rbp)
        movl    %ecx, -16(%rbp)
        movl    %r8d, -20(%rbp)
        movl    %r9d, -24(%rbp)
        call    fun()
        movl    24(%rbp), %eax
        leave
        ret
main:
        pushq   %rbp
        movq    %rsp, %rbp
        pushq   $9  // 24+%rbp
        pushq   $8  // 16+%rbp
        pushq   $7  // 8 +%rbp
        movl    $6, %r9d
        movl    $5, %r8d
        movl    $4, %ecx
        movl    $3, %edx
        movl    $2, %esi
        movl    $1, %edi
        call    func(int, int, int, int, int, int, int, int, int)
        addq    $24, %rsp
        movl    $0, %eax
        leave
        ret

主函数中的这三条语句

pushq   $9
pushq   $8
pushq   $7

说明了,当函数入栈放寄存器放不下时,会放在栈上,放在栈顶之上,等函数调用执行完成后,rbp取出回到当前位置之后,再去addq $24, %rsp 把栈弹出这些元素。

并且func函数中的movl 24(%rbp), %eax也证明了,传的参数是在栈顶的上面(自上向下增长) 24 + %rbp 刚好是 $9, 也就是局部变量f的位置

传对象呢?

在这里,暂且不谈内存布局,把一个对象看成一块内存对于的位置
这里用一个结构体做示例

struct E {int x, y, z;};
E func(E e){
    e.x = 2;
    return e;
}
int main() {
    E e = {.x = 1, .y = 2, .z = 3};
    e = func(e);
    return 0;
}
func(E):
        pushq   %rbp
        movq    %rsp, %rbp
        // 将rdi 和 esi 取出来 放到 rdx 和 eax 中
        movq    %rdi, %rdx
        movl    %esi, %eax
        // 存放到开辟好的空间中 {x = rbp - 32, y = rbp - 28, z = rbp - 24}
        movq    %rdx, -32(%rbp)
        movl    %eax, -24(%rbp)
        // 更改 x
        movl    $2, -32(%rbp)
        // 将值移动到寄存器上,从返回寄存器上移动到局部返回出去的变量
        movq    -32(%rbp), %rax
        movq    %rax, -12(%rbp)
        movl    -24(%rbp), %eax
        movl    %eax, -4(%rbp)
        // 将返回值值移动到寄存器上 rax rdx 上
        movq    -12(%rbp), %rax
        movl    -4(%rbp), %ecx
        movq    %rcx, %rdx
        popq    %rbp
        ret
main:
        // 压栈保存现场 没什么好说的
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        // 内存布局
        rbp
        | z  rbp - 4  
        | y  rbp - 8
        | x  rbp - 12
        movl    $1, -12(%rbp)
        movl    $2, -8(%rbp)
        movl    $3, -4(%rbp)
        // 移动 x 和 y 到 rdx 寄存器中
        movq    -12(%rbp), %rdx
        // 移动 z 到 eax中
        movl    -4(%rbp), %eax
        // 再将 rdx 和 eax 分别移动到rdi 和 esi中
        movq    %rdx, %rdi
        movl    %eax, %esi
        call    func(E)
        // 从rax 中取出x y
        movq    %rax, -12(%rbp)
        // 从rdx中取出z
        movl    -4(%rbp), %eax
        andl    $0, %eax
        orl     %edx, %eax //
        movl    %eax, -4(%rbp)
        movl    $0, %eax
        leave
        ret

以上就是C/C++ 函数原理传参示例详解的详细内容,更多关于C/C++ 函数原理传参的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C/C++函数原理传参示例详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C/C++函数原理传参示例详解

这篇文章主要为大家介绍了C/C++函数原理传参示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-08

C++ 函数参数详解:高阶函数中参数传递的范例

c++++ 中有两种参数传递机制:传值传递和传址传递。传址传递将对象的内存地址传递给函数,而传值传递将值的副本传递给函数。高阶函数是接受函数作为参数的函数,其参数传递需要特别注意,因为传递的函数可能具有不同的参数签名和返回类型。示例中,st
C++ 函数参数详解:高阶函数中参数传递的范例
2024-04-27

C++ 函数默认参数传递的实现原理

c++++ 函数默认参数传递的实现原理:编译时解析函数声明,分配内存单元存储默认参数值。函数调用时检查实参存在性:若存在,使用传入值;否则,使用默认参数值。x86 架构中,默认参数通常存储在寄存器中,并在函数调用时压入栈中;实参值会覆盖默认
C++ 函数默认参数传递的实现原理
2024-04-19

C++ 函数参数详解:传入机制的底层原理与应用场景

c++++ 函数参数传递机制:值传递:创建传递值副本,原始变量不受影响。引用传递:直接操作原始变量,允许修改。指针传递:通过地址访问变量,可修改指向变量或指向其他变量。应用场景:值传递:不修改数据的函数,如打印、计算。引用传递:修改数据的函
C++ 函数参数详解:传入机制的底层原理与应用场景
2024-04-27

C++ 函数参数详解:函数重载中参数传递的影响

在 c++++ 函数重载中,函数参数的传递方式会影响函数重载的行为。影响如下:1. 传值:只关心参数数量和顺序;2. 传引用:考虑参数类型;3. 传指针:不考虑参数类型。实战中,理解传递方式对于正确重载函数至关重要,如上述 print()
C++ 函数参数详解:函数重载中参数传递的影响
2024-04-27

C++ 函数参数详解:异常处理中参数传递的特殊性

在异常处理中,c++++ 函数参数传递方式会影响异常行为:值传递:局部变量及值传递参数会被销毁,不影响原有数据。引用传递:异常会影响原有数据,导致未定义行为。指针传递:不会影响原有数据,但指针所指数据可能被修改。C++ 函数参数详解:异常处
C++ 函数参数详解:异常处理中参数传递的特殊性
2024-04-26

C++ 函数参数详解:函数式编程中参数传递的思想

c++++ 函数中参数传递有五种方式:引用传递、值传递、隐式类型转换、const 参数、默认参数。引用传递提高效率,值传递更安全;隐式类型转换自动将其他类型转换为函数期望的类型;const 参数防止意外修改;默认参数允许省略某些参数。在函数
C++ 函数参数详解:函数式编程中参数传递的思想
2024-04-28

C++存储链接性原理示例详解

这篇文章主要为大家介绍了C++存储链接性原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-03

C++ 函数参数详解:协程中参数传递的异步机制

协程中参数传递采用异步机制,不会在执行前复制参数值,而是动态获取。这种机制提供了灵活性,允许协程在运行时调整参数或延迟传递参数。具体步骤如下:传统函数参数传递:同步传递,函数执行前将实参复制到局部变量。协程参数传递:异步传递,协程使用协程指
C++ 函数参数详解:协程中参数传递的异步机制
2024-04-26

C++ 函数参数详解:lambda表达式中参数传递的规则

lambda 表达式中的参数传递规则:by value:按值传递,对原始值的修改不影响外部作用域。by reference:使用 [&] 或 [=] 捕获列表按引用传递参数,允许修改外部作用域。by move:对于可移动类型,使用 && 捕
C++ 函数参数详解:lambda表达式中参数传递的规则
2024-04-26

C++ 函数参数的传递方式详解:值传递和引用传递

c++++ 参数传递方式分为值传递和引用传递。值传递创建函数参数副本,不影响原变量;引用传递直接操作原变量。选择方式取决于需求:保护原变量使用值传递,修改原变量或提高效率使用引用传递。C++ 函数参数的传递方式详解:值传递和引用传递在 C
C++ 函数参数的传递方式详解:值传递和引用传递
2024-04-12

C语言函数声明以及函数原型超详细讲解示例

这篇文章主要介绍了C语言函数声明以及函数原型超详细讲解,C语言代码由上到下依次执行,原则上函数定义要出现在函数调用之前,否则就会报错。但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明
2023-02-11

C++ 函数参数详解:泛型编程中参数传递的多态性

泛型编程中 c++++ 函数参数的多态性泛型函数的参数可以采用不同类型(参数传递的多态性),实现针对不同数据类型工作的灵活代码。参数传递方式有三种:值传递:副本传递,不会影响原始参数引用传递:引用传递,反映原始参数的更改指针传递:指针传递,
C++ 函数参数详解:泛型编程中参数传递的多态性
2024-04-26

C#中参数的传递方式详解

本文详细讲解了C#中参数的传递方式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2022-11-13

C++ 函数调用详解:参数传递机制深入分析

c++++ 函数调用有三种参数传递机制:传值调用(复制参数值),传引用调用(传递参数引用,可修改原始变量),指针传递(传递参数指针)。选择机制需考虑参数大小、是否需修改原始变量和效率。C++ 函数调用详解:参数传递机制深入分析在 C++
C++ 函数调用详解:参数传递机制深入分析
2024-05-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录