开发者

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

目录
  • x84-64的寄存器
  • 函数是个什么东西?
    • 一个简单的函数
  • 传参姿势
    • 入栈规则
    • 看看汇编
  • 全都存寄存器吗?
    • 传对象呢?

      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
      

      分配空间动作如下所示:

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

      这里加了个函数调用是因为在有些时候,没有函数调用,就不会使用subq $16, %rsp 这一条指令,我的猜想是既然你都是栈顶的,并且不会再开发者_C学习有rbp的变化,那么栈顶以上的元素我都可以随便用。

      并且我们观察可以得知,分配栈空间时,他是分配的16个字节,也就是说,有对齐

      返回时,弹出栈顶,就可以恢复到上一个栈帧的状态了。

      传参姿势

      入栈规则

      c/c+www.devze.com+ 中规定的函数压栈顺序是从右到左,当然,如果你是 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 0php;
      }
      

      结果

      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;javascript
          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 http://www.devze.com   %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)
        android      // 从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++ 函数原理传参的资料请关注我们其它相关文章!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜