为何按引用捕获的Lambda中指针地址与调用方不一致?
Lambda捕获方式导致指针地址异常的原因分析
问题代码
Bootstrap* APIServer::bs = nullptr; std::thread* APIServer::start_srv(Bootstrap* bootstrap) { auto sv_th = new std::thread([&] { APIServer::bs = bootstrap; // 此处调试发现bootstrap指针地址与调用方不同 Environment::init(); }); return sv_th; } Bootstrap* bootstrap = new Bootstrap(); // 地址为0x5555556a3c50 // 调试发现线程Lambda内bootstrap指针值为0x5555556835c0
调用APIServer::start_srv(bootstrap)时,Lambda内读取到的bootstrap指针地址与调用方的0x5555556a3c50不符,改为[=]捕获后问题消失,原因如下:
[&]捕获栈变量引用,存在悬空引用风险start_srv的形参bootstrap是栈上的指针变量(存储着0x5555556a3c50地址),[&]按引用捕获这个栈变量。当start_srv执行完毕返回后,函数栈帧被销毁,形参bootstrap占用的内存会被系统回收并可能重新分配给其他数据。而新创建的线程大概率在函数返回后才执行Lambda代码,此时Lambda里访问的bootstrap是指向已销毁栈内存的悬空引用,读取到的是被重新分配后的内存值(即0x5555556835c0),这属于C++未定义行为。[=]按值捕获,复制了指针的有效值[=]会把形参bootstrap里存储的地址值(0x5555556a3c50)复制一份到Lambda对象内部。线程执行Lambda时,访问的是这份独立复制的值,不受原栈变量销毁的影响,因此能得到正确的指针地址。
内容的提问来源于stack exchange,提问作者Michaelzh
相关产品推荐
相关产品推荐

