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

C++从UDP缓冲区读取结构体中string时出现free()无效指针错误如何解决

核心问题根因

你确实误用了memcpy,且发送端的序列化发送逻辑从根上就存在错误:std::string属于非POD类型,内部仅存储指向堆内存的指针、长度、容量等控制字段,实际字符串内容存在堆上,直接对std::string对象、或包含std::string的结构体做内存拷贝、网络发送,本质上传输的是无意义的野指针,接收端直接覆盖std::string的内存结构后,析构时就会触发free(): invalid pointer崩溃。


1. 发送端错误点&修正

错误点

  • 给hdr.dataSize赋值时用了sizeof(send1.data):这个值是std::string对象本身的固定大小(一般为8/16/24字节,和平台相关),和你实际序列化后的内容长度毫无关系,应该替换为send1.data.size()
  • 直接对包含std::string的SerialMessage结构体强转后发送:结构体内存布局里std::string部分仅存堆指针,你发出去的不是实际序列化数据,接收端拿到完全无效
  • 未考虑结构体内存对齐:Header结构体如果不做对齐约束,不同编译器的对齐规则可能导致头部长度和接收端预期不一致,出现偏移错误

修正后的发送逻辑

// 序列化逻辑保持不变
std::ostringstream outStream;
{
    cereal::BinaryOutputArchive archive_out(outStream);
    archive_out(data1);
}

SerialMessage send1;
send1.hdr.domainId = 6;
send1.hdr.msgId = 1;
send1.data = outStream.str();
send1.hdr.dataSize = send1.data.size(); // 这里改,用实际内容长度
send1.hdr.timeStamp = getTimeStamp();
send1.ftr.temp = 0x1234; // 按需给Footer赋值

// 手动拼接连续的发送缓冲区,不要直接发SerialMessage结构体
size_t totalSendSize = sizeof(Header) + send1.data.size() + sizeof(Footer);
uint8_t* sendBuf = new uint8_t[totalSendSize];
memcpy(sendBuf, &send1.hdr, sizeof(Header)); // 拷头部
memcpy(sendBuf + sizeof(Header), send1.data.data(), send1.data.size()); // 拷实际序列化内容
memcpy(sendBuf + sizeof(Header) + send1.data.size(), &send1.ftr, sizeof(Footer)); // 拷尾部

int nbytes = udp->send(sendBuf, totalSendSize);
delete[] sendBuf; // 用完释放缓冲区

补充:建议给Header结构体加对齐约束,避免偏移错误:

struct __attribute__((packed)) Header // GCC/Clang 下用这个,MSVC用#pragma pack(1)
{
    uint8_t domainId;
    uint8_t msgId;
    int msgCnt;
    uint16_t dataSize;
    uint64_t timeStamp;
};

2. 接收端错误点&修正

错误点

  • memcpy(&serData, buffer, sizeof(inHdr.dataSize))逻辑完全错误:你直接往std::string对象的内存地址写数据,覆盖了它内部的指针、长度等控制字段,造出了完全非法的std::string对象,析构时必然崩溃
  • 未做偏移:实际序列化内容在buffer + sizeof(Header)的位置,不是从buffer开头取
  • 未做长度校验:收到的包长度可能小于预期,直接读取会出现越界访问

修正后的接收逻辑

else{
    std::cout << "\n\tReceived Message, size: " << recvSize << '\n';
    Header inHdr;
    memcpy(&inHdr, buffer, sizeof(Header));
    
    // 先校验包长度是否完整,避免越界
    const size_t expectTotalSize = sizeof(Header) + inHdr.dataSize + sizeof(Footer);
    if (recvSize < expectTotalSize) {
        std::cout << "\tincomplete packet, skip\n";
        continue;
    }

    // 直接用string的长度构造函数取数据,不需要memcpy
    std::string serData(reinterpret_cast<char*>(buffer + sizeof(Header)), inHdr.dataSize);
    // 按需读取Footer
    Footer inFtr;
    memcpy(&inFtr, buffer + sizeof(Header) + inHdr.dataSize, sizeof(Footer));

    std::cout << "\tdID: " << (int)inHdr.domainId << "\n\tmID: " << (int)inHdr.msgId << "\n\tdataLength: " << inHdr.dataSize << "\n\ttimeStamp: " << inHdr.timeStamp << std::endl;
    std::cout << "\nData:\n\t" << serData << std::endl;

    // 直接反序列化即可
    std::istringstream inStream(serData);
    SerialData data;
    {
        cereal::BinaryInputArchive archive_in(inStream);
        archive_in(data);
    }
    printSerializedMessageData(data);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 04:15:06