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

memcpy未正确复制内存?C语言代码报错及重复释放问题咨询

C语言内存复制问题:栈溢出与重复释放的原因分析

问题现象

运行提供的C语言代码时,出现*** stack smashing detected ***: terminated错误,且排查发现msg.data与rcv.data指向同一内存地址,释放rcv.data时实际是释放了之前已释放的内存。

核心原因分析

1. 浅拷贝导致重复内存释放

Message结构体中的data是指针类型,memcpy执行的是浅拷贝:它只会复制指针变量本身的数值(即内存地址),而不会复制指针指向的实际Data数组内容。

  • 你先通过memcpy(buffer, &msg, bufferSize)把msg的内容(包括data指针的地址)复制到了buffer里;
  • 之后执行memcpy(&rcv, buffer, p->size),把buffer里的指针地址复制给了rcv.data,导致rcv.data和原来的msg.data指向同一块堆内存;
  • 当你先free(msg.data)释放这块内存,后续再free(rcv.data)时,就属于重复释放已释放的内存,这是C语言中的未定义行为,会触发内存错误。

2. 越界内存复制触发栈溢出检测

你计算bufferSize的逻辑存在错误:

  • sizeof(msg)已经包含了Message结构体所有成员的大小(包括data指针的大小,通常是4或8字节);
  • 你额外加上sizeof(Data)*msg.nr后,执行memcpy(buffer, &msg, bufferSize)时,会从msg的栈地址开始,读取远超msg结构体本身大小的内存(因为msg是栈上的局部变量),这触发了gcc的栈保护机制(Stack Smashing Protection),直接终止程序并抛出错误。

修复方案

要实现完整的内存复制(深拷贝),需要手动序列化结构体及其指向的数据:

正确的序列化步骤

  1. 计算正确的buffer大小:bufferSize = sizeof(Message) + sizeof(Data) * msg.nr;
  2. 先将Message结构体的内容复制到buffer起始位置;
  3. 再将msg.data指向的Data数组复制到buffer中结构体之后的地址。

正确的反序列化步骤

  1. 从buffer起始位置读取Message的基本信息(id、size、nr);
  2. 为rcv.data分配足够的内存;
  3. 从buffer中结构体之后的地址复制Data数组到rcv.data。

修复后的代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char BYTE;

typedef struct Data {
    int valid;
    double value;
} Data;

typedef struct Message {
    int id;
    int size;
    int nr;
    Data *data;
} Message;

int main() {
    int sz = 5;
    int id = 1;
    int i;

    Message msg;
    msg.id = id;
    msg.size = 0;
    msg.nr = sz;
    msg.data = malloc(sizeof(Data) * msg.nr);
    for (i = 0; i < msg.nr; i++) {
        msg.data[i].valid = 1;
        msg.data[i].value = (double)i;
    }

    printf("Input data\nid: %d\nsize: %d\nnr: %d\n",
           msg.id, msg.size, msg.nr);
    
    for (i = 0; i < sz; i++)
        printf("msg.data[%d].valid: %d\nmsg.data[%d].value: %lf\n",
               i, msg.data[i].valid, i, msg.data[i].value);

    // 计算正确的buffer大小
    int bufferSize = sizeof(Message) + (sizeof(Data) * msg.nr);
    msg.size = bufferSize;
    printf("bufferSize: %d\n", bufferSize);

    BYTE *buffer = malloc(sizeof(BYTE) * bufferSize);
    // 先复制Message结构体
    memcpy(buffer, &msg, sizeof(Message));
    // 再复制Data数组到结构体之后的位置
    memcpy(buffer + sizeof(Message), msg.data, sizeof(Data)*msg.nr);

    // 释放原msg的data内存
    if (msg.data != NULL)
        free(msg.data);
     
    // 反序列化
    Message *p = (Message *)buffer;
    Message rcv;
    // 先读取基本信息
    memcpy(&rcv, buffer, sizeof(Message));
    // 为rcv.data分配内存
    rcv.data = malloc(sizeof(Data) * rcv.nr);
    // 复制Data数组
    memcpy(rcv.data, buffer + sizeof(Message), sizeof(Data)*rcv.nr);
    
    printf("Output data\nid: %d\nsize: %d\nnr: %d\n",
           rcv.id, rcv.size, rcv.nr);
    
    for (i = 0; i < sz; i++)
        printf("rcv.data[%d].valid: %d\nrcv.data[%d].value: %lf\n",
               i, rcv.data[i].valid, i, rcv.data[i].value);

    // 释放资源
    if (rcv.data != NULL)
        free(rcv.data);
    if (buffer != NULL)
        free(buffer);

    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 18:55:12