C++11新特性中的匿名函数Lambda表达式的汇编实现分析(二)

简介:

首先,让我们来看看以&方式进行变量捕获,同样没有参数和返回。

int main()
{
    int a = 0xB;
    auto lambda = [&]{
        a = 0xA;
    };
    lambda();
    return 0;
}

闭包中将main中a变量改写为0xA。

main中的关键汇编代码:

int a = 0xB;
 mov         dword ptr [ebp-8],0Bh  
    auto lambda = [&]{
        a = 0xA;
    };
 lea         eax,[ebp-8]  
 push        eax  
 lea         ecx,[ebp-14h]  
 call        002D1BE0  
    lambda();
 lea         ecx,[ebp-14h]  
 call        002D1C20  
    return 0;

同样的,进入闭包前要调用一个拷贝函数。

002D1BE0 内:

pop         ecx  
 mov         dword ptr [ebp-8],ecx  
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [ebp+8]  
 mov         dword ptr [eax],ecx  
 mov         eax,dword ptr [ebp-8]  
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret         4

和前面一篇文章中的代码基本一致,但是有两个地方不同,

上文写到:

pop         ecx  

 mov         dword ptr [ebp-8],ecx  

 mov         eax,dword ptr [ebp-8]  

 mov         ecx,dword ptr [ebp+8]  

 mov         edx,dword ptr [ecx]  

 mov         dword ptr [eax],edx  

 mov         eax,dword ptr [ebp-8]

注意黑体部分,若采用[=]的捕获方式,那么将通过寄存器edx拷贝原变量的值;

若采用[&]方式,则直接通过ecx拷贝原变量的地址,而不取出值。

闭包内:

pop         ecx  
 mov         dword ptr [ebp-8],ecx  
        a = 0xA;
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [eax]  
 mov         dword ptr [ecx],0Ah  
    };
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret

对a进行赋值,直接是:

*this = 0xA;

因为事先this就取到了a的地址。

可以发现,按引用捕获实际上就如同函数参数传递引用,传递的是一个地址值,而不创建对象副本(可以和上一篇文中的内容比较)。

C++11标准中对Lambda表达式的捕获内容还有一些特定支持,比如可以以指定的方式捕获指定的变量:

int main()
{
    int a = 0xB;
    bool b = true;
    auto lambda = [&a,b]{
        a = b;
    };
    lambda();
    return 0;
}

上面的代码对a进行引用捕获,对b按值捕获。根据前面分析的结果,可以预见,a的地址和b的值将被拷贝以供闭包函数使用。

int a = 0xB;
 mov         dword ptr [ebp-8],0Bh  
    bool b = true;
 mov         byte ptr [ebp-11h],1  
    auto lambda = [&a,b]{
        a = b;
    };
 lea         eax,[ebp-11h]  
 push        eax  
 lea         ecx,[ebp-8]  
 push        ecx  
 lea         ecx,[ebp-24h]  
 call        00222060  
    lambda();
 lea         ecx,[ebp-24h]  
    lambda();
 call        00221C20  
    return 0;

调用Lambda之前,先调用复制函数,传入两个参数,&a和&b,而this被放在main的[ebp-24h]中。

复制函数或者叫准备函数:

pop         ecx  
 mov         dword ptr [ebp-8],ecx  
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [ebp+8]  
 mov         dword ptr [eax],ecx  
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [ebp+0Ch]  
 mov         dl,byte ptr [ecx]  
 mov         byte ptr [eax+4],dl  
 mov         eax,dword ptr [ebp-8]  
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret         8

[eax] 就是 *this,也即a

[eax+4] 就是 *(this+4),也即b

从内存图可以清楚的看到,a的地址被记录,b的值被复制

image

闭包函数内:

pop         ecx  
 mov         dword ptr [ebp-8],ecx  
        a = b;
 mov         eax,dword ptr [ebp-8]  
 movzx       ecx,byte ptr [eax+4]  
 
 mov         edx,dword ptr [ebp-8]  
 mov         eax,dword ptr [edx]  
 mov         dword ptr [eax],ecx

b的值是从[eax+4]也即this+4中取出,而a在this中,其实就是:

*(this) = *(this+4);

可以看到闭包内通过this作为基址,对闭包外的变量进行偏移访问。

相关文章
|
2月前
|
安全 Linux iOS开发
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
391 53
Binary Ninja 5.1.8104 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
2月前
|
Linux API iOS开发
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
250 14
Binary Ninja 4.2.6455 (macOS, Linux, Windows) - 反编译器、反汇编器、调试器和二进制分析平台
|
5月前
|
程序员 编译器 C++
【实战指南】C++ lambda表达式使用总结
Lambda表达式是C++11引入的特性,简洁灵活,可作为匿名函数使用,支持捕获变量,提升代码可读性与开发效率。本文详解其基本用法与捕获机制。
212 47
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
204 2
|
8月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
349 12
|
11月前
|
自然语言处理 编译器 C语言
为什么C/C++编译腰要先完成汇编
C/C++ 编译过程中先生成汇编语言是历史、技术和实践的共同选择。历史上,汇编语言作为成熟的中间表示方式,简化了工具链;技术上,分阶段编译更高效,汇编便于调试和移植;实践中,保留汇编阶段降低了复杂度,增强了可移植性和优化能力。即使在现代编译器中,汇编仍作为重要桥梁,帮助开发者更好地理解和优化代码。
为什么C/C++编译腰要先完成汇编
|
11月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
算法 编译器 C++
【C++11】lambda表达式
C++11 引入了 Lambda 表达式,这是一种定义匿名函数的方式,极大提升了代码的简洁性和可维护性。本文详细介绍了 Lambda 表达式的语法、捕获机制及应用场景,包括在标准算法、排序和事件回调中的使用,以及高级特性如捕获 `this` 指针和可变 Lambda 表达式。通过这些内容,读者可以全面掌握 Lambda 表达式,提升 C++ 编程技能。
571 3
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
【11月更文挑战第6天】在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。
490 5
|
存储 算法 搜索推荐
对二叉堆的简单分析,c和c++的简单实现
这篇文章提供了对二叉堆数据结构的简单分析,并展示了如何在C和C++中实现最小堆,包括初始化、插入元素、删除最小元素和打印堆的函数,以及一个示例程序来演示这些操作。
177 19