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

C++结构体数组越界访问问题:如何规避野指针引发的程序崩溃

问题分析与解决方案

这个问题的核心是手动管理动态数组时没有设置可靠的终止标记,再加上C++动态数组分配后,超出有效元素范围的内存是未初始化的随机值——刚好你的环境里这个位置不是空指针,导致while循环越界访问到非法内存,最终崩溃。另外还有一个隐藏的坑:你的结构体成员name是const char*,但直接赋值exec_ssh_command返回的std::string会导致指针悬空,这也是潜在的崩溃隐患。

下面给你几种针对性的解决办法,按推荐程度排序:

1. 改用std::vector管理数组(最推荐的C++方案)

既然是C++代码,完全没必要手动折腾动态数组,std::vector会自动帮你管理内存、避免越界,还能彻底解决指针问题。

首先修改结构体(把易出问题的const char*换成std::string):

struct nicInfo {
    std::string name;
    std::string ipAddr;
};

然后重写nicList函数:

std::vector<nicInfo> nicList() {
    std::vector<nicInfo> nicList;
    nicList.reserve(nic_numbers); // 预分配空间,提升效率
    std::string nic_name_command;

    for (int i = 0; i < nic_numbers; i++) {
        nic_name_command = "ls /sys/class/net | sed -n -s " + std::to_string(i+1) + "p";
        nicInfo info;
        info.name = sshObj->exec_ssh_command(nic_name_command);
        nicList.push_back(info);
    }
    return nicList;
}

打印的时候直接用数组长度控制循环,不用依赖空指针:

// C++风格打印
auto nic_list = nicList();
for (const auto& nic : nic_list) {
    std::cout << "nic name : " << nic.name << std::endl;
}

// 如果非要用C风格循环
for (int i = 0; i < nic_list.size(); i++) {
    printf("nic name : %s\n", nic_list[i].name.c_str());
}

这种方式彻底避免了手动内存管理和越界问题,是C++开发的最佳实践。

2. 手动添加终止标记(兼容原C风格循环)

如果你一定要保留原有的动态数组和C风格打印逻辑,可以在分配数组时多申请一个元素,把最后一个元素的name设为nullptr,作为循环终止的信号:

nicInfo* nicList() {
    std::string num;
    std::string nic_name_command;
    // 多分配1个元素用于存放终止标记
    nicInfo* nic = new nicInfo[nic_numbers + 1];

    for (int i = 0; i < nic_numbers; i++) {
        nic_name_command = "ls /sys/class/net | sed -n -s " + std::to_string(i+1) + "p";
        // 解决指针悬空问题:复制字符串到堆内存
        std::string nameStr = sshObj->exec_ssh_command(nic_name_command);
        nic[i].name = strdup(nameStr.c_str());
    }
    // 设置终止标记
    nic[nic_numbers].name = nullptr;
    return nic;
}

这样你的原while循环就能正常工作了,但要记得释放内存时:

nicInfo* nic = nicList();
int i = 0 ;
while ((nic + i)->name ) {
    printf("nic name : %s\n", (nic + i)->name);
    free((void*)(nic + i)->name); // 先释放每个name的堆内存
    i++;
}
delete[] nic; // 最后释放数组本身

3. 传递数组长度控制循环(最直接的兼容方案)

如果不想修改数组分配逻辑,也可以在返回数组的同时返回有效元素的长度,打印时用长度来控制循环,完全不用依赖空指针:

// 修改函数返回值为数组+长度的配对
std::pair<nicInfo*, int> nicList() {
    std::string num;
    std::string nic_name_command;
    nicInfo* nic = new nicInfo[nic_numbers];

    for (int i = 0; i < nic_numbers; i++) {
        nic_name_command = "ls /sys/class/net | sed -n -s " + std::to_string(i+1) + "p";
        std::string nameStr = sshObj->exec_ssh_command(nic_name_command);
        nic[i].name = strdup(nameStr.c_str());
    }
    return {nic, nic_numbers};
}

打印时:

auto result = nicList();
nicInfo* nic = result.first;
int count = result.second;

for (int i = 0; i < count; i++) {
    printf("nic name : %s\n", nic[i].name);
    free((void*)nic[i].name);
}
delete[] nic;

关键注意点

你原来的代码里有个致命的指针悬空问题:nic[i].name = sshObj->exec_ssh_command(nic_name_command);如果exec_ssh_command返回的是std::string,那么这条语句会把临时std::string的c_str()指针赋值给name,但临时对象在语句结束后会立刻销毁,导致name变成指向已释放内存的悬空指针,后续访问大概率会崩溃或出现乱码。上面的方案里都用strdup复制了字符串到堆内存,或者改用std::string,就是为了修复这个问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 07:28:14