递归读取二叉树时delete触发SIGTRAP错误,如何避免内存泄漏?
二叉树文件读取时delete触发SIGTRAP异常的解决
问题背景
我编写了从文件读取二叉树的方法,代码如下:
BinaryTree * FileService::read(BinaryTree * node) { basic_string<char> np = "nullptr"; char * val = new char (); if (!fscanf(this->filePtr, "%s ", val)) { delete val; return node; } string str(val); if (str == np) { delete val; return node; } node = new BinaryTree(); if (Helper::isFloatNumber(val)) { node->setNumericValue(stof(val)); } else { node->setStrValue(val); } delete val; <----HERE SIGTRAP (Trace/breakpoint trap) node->setLeft(this->read(&node->getLeftReference())); node->setRight(this->read(&node->getRightReference())); return node; }
执行时出现错误:Process finished with exit code -1073740940 (0xC0000374),调试时在标注的delete val;处触发SIGTRAP (Trace/breakpoint trap)陷阱。注释该行后代码可正常运行,但担心内存泄漏。我尝试过将delete val;移至return之前,也尝试用非空判断包裹delete操作:
if (val != nullptr) { delete val; val = nullptr; }
但问题均未解决。请问哪里出错了?如何修改才能同时避免内存泄漏和异常?
问题根源
核心问题出在char * val = new char ();这一行:你只分配了1个字节的内存,但fscanf("%s ", val)会读取整个空白分隔的字符串,当输入的字符串长度大于0时,必然会越界写入,破坏堆内存结构。后续的delete val操作访问了被破坏的堆空间,就会触发SIGTRAP或堆损坏异常。
解决方法
方案1:使用固定大小的字符数组(简单直接)
把动态分配的单个char换成足够大的字符数组,避免越界:
BinaryTree * FileService::read(BinaryTree * node) { const basic_string<char> np = "nullptr"; char val[256]; // 假设输入的单个节点值不超过255字节 if (!fscanf(this->filePtr, "%255s ", val)) { // 限制读取长度,防止越界 return node; } const string str(val); if (str == np) { return node; } node = new BinaryTree(); if (Helper::isFloatNumber(val)) { node->setNumericValue(stof(val)); } else { node->setStrValue(val); } // 数组在栈上,无需手动释放,不存在内存泄漏 node->setLeft(this->read(&node->getLeftReference())); node->setRight(this->read(&node->getRightReference())); return node; }
方案2:使用C++风格的字符串读取(更安全)
完全抛弃C风格的字符数组和fscanf,用C++的std::string和流操作来读取,从根源避免内存管理问题:
#include <string> #include <fstream> BinaryTree * FileService::read(BinaryTree * node) { const std::string np = "nullptr"; std::string val; // 假设fileStream是std::ifstream类型,若原filePtr是FILE*可转为ifstream使用 if (!(this->fileStream >> val)) { // >>自动按空白分隔读取字符串 return node; } if (val == np) { return node; } node = new BinaryTree(); if (Helper::isFloatNumber(val)) { node->setNumericValue(std::stof(val)); } else { node->setStrValue(val); } // std::string自动管理内存,无需手动释放 node->setLeft(this->read(&node->getLeftReference())); node->setRight(this->read(&node->getRightReference())); return node; }
关键注意点
- 绝对不要用
new char()分配单个字符来存储字符串,%s需要足够空间容纳整个字符串(包括末尾的'\0')。 - 用C风格函数读取字符串时,必须限制读取长度(比如
%255s),防止缓冲区溢出。 - 优先使用C++标准库的字符串和流操作,它们自带内存管理,能大幅减少内存泄漏和越界问题。
内容的提问来源于stack exchange,提问作者Deadpool
相关产品推荐
相关产品推荐

