按值传递
如下所示
printI 将捕获变量 i , 所以可以直接在函数体中使用变量 i
int i = 100;
/*
* 输出 i 的地址:
* 刚好是下面 rdi 寄存器中的值
*/
std::cout << "address of i is:" << &i << std::endl;
auto printI = [=]() -> int {
int temp = i;
temp = temp*2;
std::cout << "printI:" << temp << std::endl;
return i;
};
打印下 变量 i 的值:
/*
* i 的值
*/
i
$1 = 100
/*
* i 的地址
*/
&i
$2 = (int *) 0x7ffffffee300
值变量的捕获
printI 反汇编代码如下所示:
Dump of assembler code for function <lambda()>::operator()(void) const:
=> 0x00000000080009ca <+0>: push %rbp
0x00000000080009cb <+1>: mov %rsp,%rbp
0x00000000080009ce <+4>: sub $0x20,%rsp
0x00000000080009d2 <+8>: mov %rdi,-0x18(%rbp)
0x00000000080009d6 <+12>: mov -0x18(%rbp),%rax
0x00000000080009da <+16>: mov (%rax),%eax
0x00000000080009dc <+18>: mov %eax,-0x4(%rbp)
0x00000000080009df <+21>: shll -0x4(%rbp)
0x00000000080009e2 <+24>: lea 0x1ac(%rip),%rsi # 0x8000b95
0x00000000080009e9 <+31>: lea 0x201630(%rip),%rdi # 0x8202020 <_ZSt4cout@@GLIBCXX_3.4> 0x00000000080009f0 <+38>: callq 0x8000860 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x00000000080009f5 <+43>: mov %rax,%rdx
0x00000000080009f8 <+46>: mov -0x4(%rbp),%eax
0x00000000080009fb <+49>: mov %eax,%esi
0x00000000080009fd <+51>: mov %rdx,%rdi
0x0000000008000a00 <+54>: callq 0x80008a0 <_ZNSolsEi@plt>
0x0000000008000a05 <+59>: mov %rax,%rdx
0x0000000008000a08 <+62>: mov 0x2015c1(%rip),%rax # 0x8201fd0
0x0000000008000a0f <+69>: mov %rax,%rsi
0x0000000008000a12 <+72>: mov %rdx,%rdi
0x0000000008000a15 <+75>: callq 0x8000870 <_ZNSolsEPFRSoS_E@plt>
0x0000000008000a1a <+80>: mov -0x18(%rbp),%rax
0x0000000008000a1e <+84>: mov (%rax),%eax
0x0000000008000a20 <+86>: leaveq
0x0000000008000a21 <+87>: retq
将 变量 i 保存到本地堆栈中,如下所示:
- (rdi%) 寄存器指向的是第一个参数的地址
- 将第一个参数的地址 (rdi%) 保存到新开辟的 栈帧(Frame) 中
- 将 (rdi%) 地址中的 值(value) 保存到 eax% 寄存器中
- 按值传递:(rdi%) 中的值就是 100 (详见下述分析)
0x00000000080009d2 <+8>: mov %rdi,-0x18(%rbp)
0x00000000080009d6 <+12>: mov -0x18(%rbp),%rax
0x00000000080009da <+16>: mov (%rax),%eax
此时各个寄存器中的值,如下所示:
rax 0x7ffffffee304 140737488282372
rbx 0x0 0
rcx 0xb40 2880
rdx 0x0 0
rsi 0x7fffff05d8c0 140737471961280
rdi 0x7ffffffee304 140737488282372
rbp 0x7ffffffee310 0x7ffffffee310
rsp 0x7ffffffee2f8 0x7ffffffee2f8
r8 0x7fffff05d8c0 140737471961280
r9 0x7fffff7d0d80 140737479773568
r10 0x6 6
r11 0x7ffffecee7e0 140737468360672
r12 0x80008c0 134219968
r13 0x7ffffffee3f0 140737488282608
r14 0x0 0
r15 0x0 0
rip 0x80009ca 0x80009ca <<lambda()>::operator()(void) const>
eflags 0x213 [ CF AF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
可以看到 rdi 寄存器中的值和代码 (&i) 中值
rdi => 0x7ffffffee304
&i => 0x7ffffffee300
查看内存中的值
0x7ffffffee300: 100 100 -2040769536 1449909546
0x7ffffffee310: 134220560 0 -20374633 32767
0x7ffffffee320: -112 -1 -72712 32767
0x7ffffffee330: -112 1 134220322 0
重点是 0x7ffffffee300 和 0x7ffffffee304 两个地址中的值
可以看到都是 100,这就是 按值传递
0x7ffffffee300 :: 100
0x7ffffffee304 :: 100
按引用传递
如下所示
变量 i 会在 Lambda 表达式中,被修改成为 3
int i = 100;
std::cout << "address of i is:" << &i << std::endl;
auto printI = [&]() -> void {
i = 3;
};
std::cout << "i is :"<< i << std::endl;
引用变量捕获
如下所示:
- %rdi 寄存器,指向的是 变量 i 的内存地址的地址 (指针的指针的含义) ;
- 经过一次解引用,获取 变量 i 的地址,存储到 %rax 寄存器中;
- 再将立即数,送入到 变量 i 的地址中 (再次解引用)
Dump of assembler code for function <lambda()>::operator()(void) const:
=> 0x00000000080009ca <+0>: push %rbp
0x00000000080009cb <+1>: mov %rsp,%rbp
0x00000000080009ce <+4>: mov %rdi,-0x8(%rbp)
0x00000000080009d2 <+8>: mov -0x8(%rbp),%rax
0x00000000080009d6 <+12>: mov (%rax),%rax
0x00000000080009d9 <+15>: movl $0x3,(%rax)
0x00000000080009df <+21>: nop
0x00000000080009e0 <+22>: pop %rbp
0x00000000080009e1 <+23>: retq
End of assembler dump.
此时各个寄存器中值为
rax 0x7ffffffee300 140737488282368
rbx 0x0 0id) const (__closure=0x7fffff182d7e <std::ostream::flush()+30>)
rcx 0xb40 2880onProjects/mytest/main.cpp:16
rdx 0x0 0 [&]() -> void {
rsi 0x7fffff05d8c0 140737471961280
rdi 0x7ffffffee300 140737488282368:operator()(void) const:
rbp 0x7ffffffee310 0x7ffffffee310
rsp 0x7ffffffee2e8 0x7ffffffee2e8bp
r8 0x7fffff05d8c0 1407374719612808(%rbp)
r9 0x7fffff7d0d80 140737479773568p),%rax
r10 0x6 62>: mov (%rax),%rax
r11 0x7ffffecee7e0 140737468360672ax)
r12 0x80008c0 134219968
r13 0x7ffffffee3f0 140737488282608
r14 0x0 03>: retq
r15 0x0 0
rip 0x80009ca 0x80009ca <<lambda()>::operator()(void) const>
eflags 0x213 [ CF AF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(rdi%) 和 指针输出地址关系如下所示
rdi => 0x7ffffffee300 // 变量 i 的地址
0x7ffffffee2ff ---
0x7ffffffee2fe * // 变量 i 的值
0x7ffffffee2fd *
&i => 0x7ffffffee2fc ---
内存中的数据如下所示:
0x7ffffffee2fc: 0x00000064 0xfffee2fc 0x00007fff 0xe6c3a300
0x7ffffffee30c: 0x45b53a39 0x08000b00 0x00000000 0xfec91b97
0x7ffffffee31c: 0x00007fff 0xffffff90 0xffffffff 0xfffee3f8
0x7ffffffee32c: 0x00007fff 0xffffff90 0x00000001 0x080009e2
重点是 0x7ffffffee2fc 和 0x7ffffffee300 两个内存位置的数据,含义如下所示
0x7ffffffee2fc: 0x00000064 // 0x64 = 100 变量 i 的值
0x7ffffffee300: 0xfffee2fc // 存储了地址 (0x7ffffffee2fc)
总结
- 按值传递
- (rdi%) 中保存的是 变量副本值(value)
- 按引用传递
- (rdi%) 中保存的是 变量的地址的地址(address of address)