使用智能指针与vector构建默克尔树时持续崩溃的问题
默克尔树构建代码的问题分析与修复
核心问题排查
你的代码触发0xC0000005(空指针访问)错误,同时存在死循环、内存双重释放问题,具体原因如下:
1. 初始vector初始化错误
std::vector< std::shared_ptr<merkle::MerkleNode> > nodes(values.size());
这行代码创建了包含values.size()个空shared_ptr的vector,后续push_back添加有效节点后,vector前半部分全是空指针。循环中从nodes.begin()开始访问时,第一个元素就是空指针,调用(*it)->hash()直接触发空指针解引用崩溃。
2. 死循环问题
while (nodes.size() > 1)循环内未将nodes更新为新生成的new_nodes,导致nodes.size()永远不会减小,陷入无限循环。
3. 返回值的内存错误
return std::move(std::shared_ptr<MerkleNode>(nodes[0].get()));
用get()获取原始指针构造新shared_ptr,会让两个shared_ptr管理同一块内存,最终触发双重释放。
修复后的代码
#include <vector> #include <memory> #include <string> namespace merkle { struct MerkleNode { std::string hash_; std::shared_ptr<MerkleNode> left_; std::shared_ptr<MerkleNode> right_; // 叶子节点构造函数 MerkleNode(const std::string& value) : hash_(value), left_(nullptr), right_(nullptr) {} // 中间节点构造函数 MerkleNode(std::shared_ptr<MerkleNode> left, std::shared_ptr<MerkleNode> right) : left_(std::move(left)), right_(std::move(right)) { // 此处需替换为实际哈希逻辑,示例仅做字符串拼接 hash_ = left_->hash() + right_->hash(); } const std::string& hash() const { return hash_; } }; } // namespace merkle inline std::shared_ptr<merkle::MerkleNode> AddLeaf(const std::string &vleft, const std::string &vright) { auto left = std::make_shared<merkle::MerkleNode>(vleft); auto right = std::make_shared<merkle::MerkleNode>(vright); return std::make_shared<merkle::MerkleNode>(std::move(left), std::move(right)); } std::shared_ptr<merkle::MerkleNode> Build(const std::vector<std::string> &values) { if (values.empty()) { return nullptr; } // 初始化空vector,避免空指针占位 std::vector<std::shared_ptr<merkle::MerkleNode>> nodes; nodes.reserve(values.size()); for (const auto &value : values) { nodes.push_back(std::make_shared<merkle::MerkleNode>(value)); } while (nodes.size() > 1) { // 奇数节点数时复制最后一个节点凑偶数 if (nodes.size() % 2 != 0) { nodes.push_back(nodes.back()); } std::vector<std::shared_ptr<merkle::MerkleNode>> new_nodes; new_nodes.reserve(nodes.size() / 2); // 用索引遍历更直观,避免迭代器移动的潜在问题 for (size_t i = 0; i < nodes.size(); i += 2) { auto parent = std::make_shared<merkle::MerkleNode>(nodes[i], nodes[i+1]); new_nodes.push_back(std::move(parent)); } // 更新节点层,推进循环 nodes.swap(new_nodes); } // 直接返回根节点,无需额外构造shared_ptr return nodes[0]; }
额外优化点
- 用
reserve()提前分配vector内存,减少扩容开销 - 替换迭代器为索引遍历,降低操作复杂度
- 补充
MerkleNode结构体定义(适配修复逻辑) - 增加空输入的边界处理
内容的提问来源于stack exchange,提问作者Mauricio Alvarez
相关产品推荐
相关产品推荐

