You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用erase函数时Valgrind检测到大小为4的无效读取错误

自定义Vector类erase操作触发越界读的问题排查与解决

我正在编写作业,用自行实现的、模仿C++标准Vector的Vector类过滤重复区间。程序能输出预期结果,但会触发RTE错误,Valgrind检测信息如下:

==672367== Invalid read of size 4
==672367==    at 0x109D0B: Vector::erase(int*) (Vector.cpp:357)
==672367==    by 0x10A4C8: main (pa13a.cpp:79)
==672367==  Address 0x4dc00a0 is 0 bytes after a block of size 16 alloc'd
==672367==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==672367==    by 0x10985C: Vector::operator=(Vector const&) (Vector.cpp:220)
==672367==    by 0x10A49F: main (pa13a.cpp:76)
==672367== 
Unique Timestamps:  [100, 200, 320, 480, 700]
Intervals:          [100, 120, 160, 220]
Filtered Intervals: [120, 160, 220]
Reversed Intervals: [220, 160, 120]
Combined Data:      [100, 200, 320, 480, 700, 220, 160, 120]
==672367== 
==672367== HEAP SUMMARY:
==672367==     in use at exit: 0 bytes in 0 blocks
==672367==   total heap usage: 27 allocs, 27 frees, 78,152 bytes allocated
==672367== 
==672367== All heap blocks were freed -- no leaks are possible
==672367== 
==672367== For lists of detected and suppressed errors, rerun with: -s
==672367== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

相关代码段:

fInt = tInt; // Filtered interval = to Original Interval vector
for (auto x = fInt.begin(); x != fInt.end(); ++x) { // pointer iteration
    while (*x == minInterval) { // value of position == the filter number
        fInt.erase(x); // erase duplicates until new number
    }
}

我试过用索引迭代、单独存储再反转等方法,都出现段错误。单独测试Vector类的erase函数时Valgrind没报异常,求问题原因及解决方法。


问题原因

  1. 迭代器失效:调用erase(x)后,x指向的元素被删除,该迭代器直接失效。如果自定义Vector的erase没有返回下一个有效迭代器,后续继续使用x(包括*x和++x)会访问已释放或移位的内存,触发越界读。
  2. 循环逻辑漏洞:外层for循环的++x会在erase后跳过元素,内层while循环在erase后未更新x,当所有匹配元素被删除后,x可能指向容器末尾之外的位置,导致*x越界访问。
  3. 连续erase场景下的边界问题:单独测试erase没问题,但连续删除时,自定义Vector可能未正确维护end()指针或内存边界,导致迭代时越界。

解决方法

方法1:修正迭代器使用逻辑(匹配标准vector行为)

如果自定义Vector::erase已实现返回下一个有效迭代器,修改代码如下:

fInt = tInt;
auto x = fInt.begin();
while (x != fInt.end()) {
    if (*x == minInterval) {
        x = fInt.erase(x); // 用erase返回的新迭代器更新x
    } else {
        ++x; // 不匹配时才移动迭代器
    }
}

若erase未返回值,先修改erase函数:

int* Vector::erase(int* pos) {
    size_t idx = pos - data_;
    // 移动pos后的元素覆盖被删除位置
    for (size_t i = idx; i < size_ - 1; ++i) {
        data_[i] = data_[i+1];
    }
    --size_;
    return pos; // 元素前移后,pos指向原位置的下一个元素
}

再使用上述循环逻辑。

方法2:从后往前遍历

从容器末尾开始遍历,删除元素不会影响前面的迭代器有效性:

fInt = tInt;
for (auto x = fInt.end() - 1; x >= fInt.begin(); --x) {
    if (*x == minInterval) {
        fInt.erase(x);
    }
}

注意确保x不会小于begin(),避免越界。

方法3:使用erase-remove惯用法(若允许用标准算法)

如果作业允许使用标准库算法,用std::remove_if配合erase简化逻辑:

#include <algorithm>

fInt = tInt;
fInt.erase(std::remove_if(fInt.begin(), fInt.end(),
    [minInterval](int val) { return val == minInterval; }),
    fInt.end());

需确保自定义Vector的begin()、end()返回的迭代器兼容标准算法。

额外检查点

  • 确认operator=的深拷贝实现正确,Valgrind提到operator=分配内存,需保证拷贝后的容器内存边界无越界风险。
  • 检查erase删除最后一个元素时,是否正确更新end()指针,避免后续迭代访问已释放区域。

内容的提问来源于stack exchange,提问作者Howlkipsure139

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 15:26:28