C语言中基于Bitmap动态选择结构体成员写入缓冲区时的内存优化与栈溢出解决方案
嘿,我来帮你搞定这个问题!首先你遇到的栈溢出,核心问题不是用指针声明缓冲区本身,而是代码里所有memcpy调用都错误地在长度参数上加了+1,导致写入超出了内存边界,触发了系统的栈保护机制。另外,直接写char *buffer;却不给它分配内存的话,指针指向的是未定义的垃圾地址,往里面写数据必然会导致内存错误。
下面是既不浪费内存又能安全运行的解决方案,我会一步步解释并给出修正后的完整代码:
第一步:提前算出实际需要的缓冲区大小
我们不需要一开始就分配结构体总大小的内存,而是根据bitmap的位状态,精确计算要写入的所有成员的总字节数(包括bitmap本身):
// 先算出各个位的状态 uint8_t temp4 = isKthBitSet(abitmap.bitmap,4); uint8_t temp3 = isKthBitSet(abitmap.bitmap,3); uint8_t temp2 = isKthBitSet(abitmap.bitmap,2); uint8_t temp1 = isKthBitSet(abitmap.bitmap,1); // 计算总缓冲区大小:从bitmap的大小开始,加上选中成员的大小 size_t buffer_size = sizeof(abitmap.bitmap); if (temp4) buffer_size += sizeof(abitmap.a); if (temp3) buffer_size += sizeof(abitmap.b); if (temp2) buffer_size += sizeof(abitmap.str); if (temp1) buffer_size += sizeof(abitmap.d);
第二步:动态分配内存
用malloc根据计算出的大小分配内存,一定要检查分配是否成功——万一内存不足,malloc会返回NULL,直接用的话会崩溃:
char *buffer = malloc(buffer_size); if (!buffer) { perror("malloc failed"); return 1; // 分配失败就直接退出 }
第三步:修复memcpy的长度错误
你之前写的sizeof(xxx)+1是致命错误!比如abitmap.a是uint32_t,大小是4字节,加1就会复制5字节,直接越界写内存,这才是栈溢出的元凶。正确的做法是直接用成员的实际大小sizeof(xxx),不多不少。
第四步:用完记得释放内存
动态分配的内存不会自动回收,用free(buffer)释放,之后最好把指针设为NULL,避免变成悬空指针。
完整修正后的代码
#include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <time.h> #define RAND(lower,upper) (rand()%(upper-lower+1))+lower int power(int base, int exp) { int result=1; while (exp != 0) { result *= base; --exp; } return result; } int isKthBitSet(int n, int k)//from right, 1<=k<=n { int new_num = n >> (k - 1); return (new_num & 1); } struct Astructure{ uint16_t bitmap; uint32_t a; uint32_t b; char str[10]; uint16_t d; }__attribute__((packed)); int main() { struct Astructure abitmap; abitmap.bitmap = 0; abitmap.a = 52; abitmap.b = 16; char c[10]={"ramya"}; strcpy(abitmap.str, c); abitmap.d = 59; srand(time(0)); for(uint8_t position =1;position<5;position++) { int random10 = RAND(0,1); if(random10==1) { int value = power(2,position-1); abitmap.bitmap = abitmap.bitmap | value; } } // 先确定各个位的状态,计算缓冲区大小 uint8_t temp4 = isKthBitSet(abitmap.bitmap,4); uint8_t temp3 = isKthBitSet(abitmap.bitmap,3); uint8_t temp2 = isKthBitSet(abitmap.bitmap,2); uint8_t temp1 = isKthBitSet(abitmap.bitmap,1); size_t buffer_size = sizeof(abitmap.bitmap); if (temp4) buffer_size += sizeof(abitmap.a); if (temp3) buffer_size += sizeof(abitmap.b); if (temp2) buffer_size += sizeof(abitmap.str); if (temp1) buffer_size += sizeof(abitmap.d); // 动态分配缓冲区 char *buffer = malloc(buffer_size); if (!buffer) { perror("malloc failed"); return 1; } // 复制数据到缓冲区 size_t previousLength = 0; // 先复制bitmap memcpy(buffer + previousLength, &abitmap.bitmap, sizeof(abitmap.bitmap)); previousLength += sizeof(abitmap.bitmap); if(temp4){ memcpy(buffer + previousLength, &abitmap.a, sizeof(abitmap.a)); previousLength += sizeof(abitmap.a); } if(temp3){ memcpy(buffer + previousLength, &abitmap.b, sizeof(abitmap.b)); previousLength += sizeof(abitmap.b); } if(temp2){ memcpy(buffer + previousLength, abitmap.str, sizeof(abitmap.str)); previousLength += sizeof(abitmap.str); } if(temp1){ memcpy(buffer + previousLength, &abitmap.d, sizeof(abitmap.d)); previousLength += sizeof(abitmap.d); } // 解析缓冲区数据 previousLength = sizeof(abitmap.bitmap); // 跳过bitmap,我们已经知道状态了 uint32_t a; uint32_t b; char str[10]; uint16_t d; if(temp4){ memcpy(&a, buffer + previousLength, sizeof(abitmap.a)); printf("a = %d\t",a); previousLength += sizeof(a); } if(temp3){ memcpy(&b, buffer + previousLength, sizeof(abitmap.b)); printf("b = %d\t",b); previousLength += sizeof(b); } if(temp2){ memcpy(str, buffer + previousLength, sizeof(abitmap.str)); printf("string = %s\t",str); previousLength += sizeof(str); } if(temp1){ memcpy(&d, buffer + previousLength, sizeof(abitmap.d)); printf("d = %d\t",d); previousLength += sizeof(d); } printf("\n"); // 释放内存,避免泄漏 free(buffer); buffer = NULL; return 0; }
额外说明
- 我调整了
previousLength的初始逻辑,复制数据时从0开始,更清晰;解析时直接跳过bitmap,因为我们已经提前知道各个位的状态了,没必要再从缓冲区读一遍。 - 动态分配的内存是在堆上,不会像栈内存那样有大小限制,也不会浪费空间——用多少分配多少。
- 一定要检查
malloc的返回值,这是写C代码的良好习惯,避免内存不足导致的崩溃。
内容的提问来源于stack exchange,提问作者rayapati ramya
相关产品推荐
相关产品推荐

