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),直接终止程序并抛出错误。
修复方案
要实现完整的内存复制(深拷贝),需要手动序列化结构体及其指向的数据:
正确的序列化步骤
- 计算正确的buffer大小:
bufferSize = sizeof(Message) + sizeof(Data) * msg.nr; - 先将
Message结构体的内容复制到buffer起始位置; - 再将
msg.data指向的Data数组复制到buffer中结构体之后的地址。
正确的反序列化步骤
- 从buffer起始位置读取
Message的基本信息(id、size、nr); - 为
rcv.data分配足够的内存; - 从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
相关产品推荐
相关产品推荐

