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

添加std::vector声明后C++程序出现异常行为的原因

问题分析:C++程序的异常与无限运行根源

注意:以下程序存在未定义行为,运行有破坏性风险,后果自负

代码重现

结构体定义

struct MyStruct {
    int* myarr;
    int size;

    MyStruct(int size) {
        myarr = new int[size];
        for (int i = 0; i < size; i++) {
            myarr[i] = i;
        }
    }

    MyStruct(const MyStruct& other) {
        size = other.size;
        myarr = new int[size];
        for (int i = 0; i < size; i++) {
            myarr[i] = other.myarr[i];
        }
    }

    MyStruct(MyStruct&& other) {
        this->myarr = other.myarr;
        this->size = other.size;

        other.myarr = nullptr;
    }

    MyStruct& operator=(MyStruct&& other) {
        this->myarr = other.myarr;
        this->size = other.size;

        other.myarr = nullptr;
        return *this;
    }
};

函数定义

MyStruct newStruct(MyStruct& temp, int size) { 
    temp = MyStruct{size};
    return temp;
}

主函数

int main() {
    vector<int> myvector {1};
    
    MyStruct tempStruct {2};
    MyStruct myst {newStruct(tempStruct, 5)};
}

运行现象

  • 有时抛出异常:
terminate called after throwing an instance of 'std::bad_array_new_length'
  what():  std::bad_array_new_length
  • 有时无限运行直至磁盘空间耗尽,需通过SIGKILL终止进程
  • 移除std::vector/std::string声明后程序运行正常;替换为std::string会出现相同问题

问题根源:未定义行为与栈布局变化的耦合

你的程序存在两处核心缺陷,结合栈布局变化触发了不可预测的未定义行为:

1. 缺失析构函数导致内存泄漏与堆内存混乱

MyStruct未定义析构函数,编译器生成的默认析构函数不会释放myarr指向的堆内存。内存泄漏本身不会直接触发异常,但会导致堆内存中的旧块被后续分配(如vector的内存操作)重用,可能破坏其他对象的关键数据(比如size成员的值)。

2. 移动赋值运算符未处理原有资源释放

移动赋值运算符直接覆盖目标对象的myarr指针,未释放其原有指向的内存。这不仅加剧了内存泄漏,还可能导致堆内存的旧块被重用时,与其他对象的内存操作产生冲突,篡改size等关键成员的值。

3. 栈布局变化触发未定义行为的异常表现

当引入std::vector或std::string时,栈的内存布局发生改变,原本隐藏的未定义行为被触发:

  • 如果size成员被篡改为负数,new int[size]会将负数转换为极大的无符号整数,触发std::bad_array_new_length异常。
  • 如果内存覆盖导致程序执行流被篡改,可能进入无限循环或持续分配内存,直至磁盘耗尽。

修复方案

遵循C++的三/五法则,补全MyStruct的必要函数,彻底消除未定义行为:

struct MyStruct {
    int* myarr;
    int size;

    // 构造函数
    MyStruct(int size) : size(size), myarr(new int[size]) {
        for (int i = 0; i < size; i++) {
            myarr[i] = i;
        }
    }

    // 拷贝构造函数
    MyStruct(const MyStruct& other) : size(other.size), myarr(new int[other.size]) {
        for (int i = 0; i < size; i++) {
            myarr[i] = other.myarr[i];
        }
    }

    // 移动构造函数
    MyStruct(MyStruct&& other) noexcept : myarr(other.myarr), size(other.size) {
        other.myarr = nullptr;
        other.size = 0;
    }

    // 拷贝赋值运算符
    MyStruct& operator=(const MyStruct& other) {
        if (this != &other) { // 防止自赋值
            delete[] myarr; // 释放原有资源
            size = other.size;
            myarr = new int[size];
            for (int i = 0; i < size; i++) {
                myarr[i] = other.myarr[i];
            }
        }
        return *this;
    }

    // 移动赋值运算符
    MyStruct& operator=(MyStruct&& other) noexcept {
        if (this != &other) { // 防止自赋值
            delete[] myarr; // 释放原有资源
            myarr = other.myarr;
            size = other.size;
            other.myarr = nullptr;
            other.size = 0;
        }
        return *this;
    }

    // 析构函数
    ~MyStruct() {
        delete[] myarr;
    }
};

同时优化newStruct函数,直接返回临时对象以触发移动语义,避免不必要的拷贝:

MyStruct newStruct(int size) { 
    return MyStruct{size};
}

主函数简化为:

int main() {
    vector<int> myvector {1};
    
    MyStruct myst {newStruct(5)};
}

修改后程序将稳定运行,不会出现异常或无限内存分配问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:14:52