c++,从汇编角度看lambda

本篇作为c++,从汇编底层角度深入理解带捕获的lambda如何转化为std::function的开胃小菜

复制代码
#include <iostream>
#include <functional>

int main(int args, char* argv[]) {
    [](){
        std::cout << "lambda 0" << std::endl;
    }();

    []() -> int {
        std::cout << "lambda 1" << std::endl;
        return 0;
    }();

    [](int a = 1) -> int {
        std::cout << "lambda 1" << std::endl;
        return a;
    }();

    int a = 1, b = 2;

    [a, b]() -> int {
        std::cout << b << std::endl;
        return a;
    }();
}

编译后看一下符号,lambda相关的只有4个,对应main函数中的4个lambda表达式

复制代码
# g++ -std=c++20 -fno-elide-constructors  -O0 -g -o main main.cpp
# readelf -s -W main |c++filt |grep lambda
    13: 0000000000401176    43 FUNC    LOCAL  DEFAULT   14 main::{lambda()#1}::operator()() const
    14: 00000000004011a2    47 FUNC    LOCAL  DEFAULT   14 main::{lambda()#2}::operator()() const
    15: 00000000004011d2    48 FUNC    LOCAL  DEFAULT   14 main::{lambda(int)#3}::operator()(int) const
    16: 0000000000401202    48 FUNC    LOCAL  DEFAULT   14 main::{lambda()#4}::operator()() const

看一下main如何执行这些lambda表达式的:

复制代码
0000000000401232 <main>:
int main(int args, char* argv[]) {

  # 申请了 0x20 大小的栈空间
  401236:	48 83 ec 20          	sub    $0x20,%rsp

  # lea 是加载地址,调用表达式时`rdi`寄存器传入了一个地址
  401241:	48 8d 45 f5          	lea    -0xb(%rbp),%rax
  401245:	48 89 c7             	mov    %rax,%rdi
  401248:	e8 29 ff ff ff       	call   401176 <main::{lambda()#1}::operator()() const>
    }();

  # 看这里和上面的lea加载地址,0xb和0xa,只差1,很奇怪
  40124d:	48 8d 45 f6          	lea    -0xa(%rbp),%rax
  401251:	48 89 c7             	mov    %rax,%rdi
  401254:	e8 49 ff ff ff       	call   4011a2 <main::{lambda()#2}::operator()() const>
    }();
    
  # 第三个 lambda,多传入了 立即数1 作为第二个参数
  401259:	48 8d 45 f7          	lea    -0x9(%rbp),%rax
  40125d:	be 01 00 00 00       	mov    $0x1,%esi
  401262:	48 89 c7             	mov    %rax,%rdi
  401265:	e8 68 ff ff ff       	call   4011d2 <main::{lambda(int)#3}::operator()(int) const>

  # 第四个,出现差异了
  int a = 1, b = 2;
  40127e:	c7 45 fc 01 00 00 00 	movl   $0x1,-0x4(%rbp)
  401285:	c7 45 f8 02 00 00 00 	movl   $0x2,-0x8(%rbp)

  # rdi 是 -0x10(%rbp) 地址,这个地址是(a和b共同空间)的地址,只传递了一个地址!
    [a, b]() -> int {
  40128c:	8b 45 fc             	mov    -0x4(%rbp),%eax
  40128f:	89 45 f0             	mov    %eax,-0x10(%rbp)
  401292:	8b 45 f8             	mov    -0x8(%rbp),%eax
  401295:	89 45 f4             	mov    %eax,-0xc(%rbp)
    }();
  401298:	48 8d 45 f0          	lea    -0x10(%rbp),%rax
  40129c:	48 89 c7             	mov    %rax,%rdi
  40129f:	e8 6e ff ff ff       	call   401212 <main::{lambda()#4}::operator()() const>

总结:

1、写一个 lambda,就会生成一个对应的函数

2、lambda 的传参,和常规函数传参一致,调用时候传递参数

3、不带捕获的 lambda,第一个参数是一个 无意义的 地址值,类似于类的this,但大小是1,无意义

4、带捕获的lambda,第一个参数是参数列表地址(所有参数排序作为整个空间,传递空间地址,就像传递结构体指针)

相关推荐
HugoStudio_SWAN1 小时前
洛谷 P1420 / P1179 / B4262 最长连号、数字统计与词频统计——统计的三种面孔
c++·学习·程序人生·算法
raindayinrain2 小时前
c++并发
c++
j7~2 小时前
【C++】智能指针的使用及其原理--详解
c++·c++11·内存泄漏·智能指针·raii·boost智能指针
Persistent的粽子!4 小时前
C++:类与对象(一)
开发语言·c++·经验分享·笔记
库玛西4 小时前
深入浅出Linux select网络模型:从底层位图原理到C++面向对象高级封装
linux·服务器·网络·c++·ubuntu
2402_882893864 小时前
深入浅出 unordered_map 与 unordered_set——从使用到底层差异
c++·哈希·unordered_map·unordered_set
今天要早睡_5 小时前
C++ 核心语法速过:命名空间、引用、函数重载与 nullptr 深度解析
android·java·c++
汉克老师5 小时前
CSP-J 初赛(以满分为目标):第二十七课 《图的遍历——BFS广度优先搜索——像“水波纹”一样,一层一层地搜索》
c++·csp-j·小学生·学c++编程
闻缺陷则喜何志丹5 小时前
【动态规划】P3609 [USACO17JAN] Hoof, Paper, Scissor G
c++·算法·动态规划·洛谷
汉克老师6 小时前
CSP-J 初赛(以满分为目标):第二十六课 《图的遍历——DFS深度优先搜索——从“树的先序遍历”走进真正的图世界》
c++·csp-j·小学生·学c++编程