关于malloc实际分配内存的探讨
来源:互联网 发布:java面试视频 编辑:程序博客网 时间:2024/05/17 00:58
在最近的项目中,发现了一个比较奇怪的bug,bug的最终结果引发了free(): invalid next size (fast)错误,导致程序崩溃。 通常这个问题很常见,就是由于实际写入的内存大小超过了由malloc获取的内存块的大小导致的覆盖,解决方案也很简单,不管是stackoverflow还是其他论坛上对这个问题的解决方案通常都是在实际的大小上+1即可。
在博主正在做的项目中,原先的代码写法是通过strlen获取字符串长度,然后调用malloc来获得这个长度对应的内存块,于是就产生了上述的问题。当然这个问题目前已经解决了,只要strlen + 1 即可,但是奇怪的地方在于在项目中只有特定的字符串会引发这个问题,而并非所有通过这种方式分配的地址都会报错,这是为什么呢?
先说明场景:项目中采用UTF-8编码,并且字符串含有汉字与各类其他字符的组合。首先,utf-8属于变长编码,汉字通常占3字节,但是英文或其他的字符占一字节。因此,在博主的机器上,如下的代码会引发上述的错误:
typedef struct{ char* name; char* address; char* auth; char* birthday;}TEST;int main(){ TEST* ptest = (TEST*)calloc(1 , sizeof(TEST)); char* address = "一二三四五六七八九十一二三a"; char* name = "测试名"; char* auth = "一二三四五六七八九十一二三"; char* birthday = "12345678"; FILE* fp = NULL; fp = fopen("/home/kylin/testcode/addr.log" , "w"); fprintf(fp , "sizeof TEST : %zd\nsizeof char* : %zd\nsizeof int : %zd\n" , sizeof(TEST) , sizeof(char*) , sizeof(int)); ptest->name = calloc(strlen(name) , sizeof(char)); ptest->address = calloc(strlen(address) , sizeof(char)); ptest->auth = calloc(strlen(auth) , sizeof(char)); ptest->birthday = calloc(strlen(birthday) , sizeof(char)); strcpy(ptest->name , name); strcpy(ptest->address , address); strcpy(ptest->auth , auth); strcpy(ptest->birthday , birthday); fprintf(fp , "&ptest : %10p\n&Name : %10p\n&Addr : %10p\n&Auth : %10p\n&Birth : %10p\n" , ptest , ptest->name , ptest->address , ptest->auth , ptest->birthday); fprintf(fp , "NameLen : %zd\nAddrLen : %zd\nAuthLen : %zd\nBirthLen : %zd\n" , strlen(ptest->name) , strlen(ptest->address) , strlen(ptest->auth) , strlen(ptest->birthday)); fprintf(fp , "Name : %s\nAddr : %s\nAuth : %s\nBirth : %s\n" , ptest->name , ptest->address , ptest->auth , ptest->birthday); fclose(fp); free(ptest->name); free(ptest->address); free(ptest->auth); free(ptest); return 0;}
编译运行后,在free时会报出错误:
*** Error in `./main': free(): invalid next size (fast): 0x0000000001ac52a0 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f9183da77e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x7fe0a)[0x7f9183dafe0a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f9183db398c]
./main[0x400df5]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f9183d50830]
./main[0x4006f9]
该错误仅仅在执行free(ptest->auth)时出现,那么为什么在之前的free不会产生呢?通过多次测试发现,在address的最后的a是引发这个错误的原因,如果删掉这个a,或者多加一个a都不会出错。在测试过程中我将内存分布情况打印出来:
无错误的内存分布图:
错误的内存分布图:
由于malloc分配的实际内存大小总是大于我们申请的内存大小,其中有多出来的8个字节用于存放struct mem_control_block,这个结构体通常位于我们申请获得的首地址-8的位置上,用于存放对于该内存块的管理信息。我们注意到,在出错的内存分布中,ptest->auth地址块中,mem_control_block字段中的size(地址为0x220f2c8)值变成了0。那么这个0就应当是address中的结束符了。而其他的内存块中,mem_control_block.size的大小也并非实际我们申请的大小。这是为什么呢?
实际上这是因为堆中的内存块总是成块分配的,并不是申请多少字节,就拿出多少个字节的内存来提供使用。堆中内存块的大小通常与内存对齐有关,在我的机器上,通过测试发现内存块的分配总是按照16字节的大小来进行分配的。比如下面的测试代码:
#define MLEN 25int main(){ char* ptr = NULL; unsigned long lastaddr = 0; int i = 0; while (i < 10) { ptr = malloc(sizeof(char) * MLEN); printf("%d : %10p , Get Size : %zd\n" , i ++ , ptr , (int)ptr - lastaddr); lastaddr = (int)ptr; } return 0;}
当MLEN值为25的时候,输出如下:当MLEN的值调整到24的时候,输出如下:
之所以使用24和25,是因为内存块分配的实际大小是要包括mem_control_block结构体的,将这个结构体的大小(8字节)算进去后,两次申请的大小是32与33,但是实际分配后的大小却是32与48字节。因此可以发现,当:申请大小+sizeof(struct mem_control_block) % 16 == 0的时候,那么刚好可以完成一次满额的分配,但是当其!=0的时候,就会多分配一个内存块,而这个内存块的大小在我的机器上就是16字节。
现在回到最开始的问题,为什么ptest->address会造成越界而其他变量的不会造成越界?原因就在于使用strlen(address)的时候不会将\0计算进长度里面,同时很巧的是strlen(address) + sizeof(mem_control_block) = 48,刚好满足了3块内存块的大小,因此整个字符串刚好填充满所有获得的内存块。在这个情况下,使用strcpy拷贝字符串,在结尾会多出一个未计算长度的结束符,这个结束符将会覆盖掉下一个指针(即ptest->auth)所指向的内存块的mem_control_block中.size的值。这种情况下就在引发了在free(ptest->auth)时由于.size大小的不对而产生的free(): invalid next size (fast)错误。
因此,对于这种内存错误,在strlen的基础上+1就是最高效有用的解决方案了。
- 关于malloc实际分配内存的探讨
- 关于malloc内存分配及查询实际内存方法
- malloc()分配的堆内存为什么比实际的大
- C中 malloc()分配堆内存实际的大小
- C中 malloc()分配堆内存实际的大小
- 关于Malloc内存分配函数的解析
- 关于String内存分配的深入探讨
- 关于String内存分配的深入探讨
- 关于String内存分配的深入探讨
- 关于String内存分配的深入探讨
- 关于String内存分配的深入探讨
- C语言动态内存分配:(一)malloc/free的实现及malloc实际分配/释放的内存
- 内存(RAM)结构[物理级] malloc()实际分配给用户的内存
- 关于全局变量指针直接 malloc分配内存的一些问题
- 关于内存分配时malloc()和calloc()的区别
- 关于malloc分配内存的“堆被损坏“Bug
- 关于String内存分配的深入探讨 (转)
- 关于工作分配的探讨
- 我的阅读史(1)
- tomcat闪退
- H5播放视频插件
- java操作word生成水印升级版
- oracle数据类型varchar2、varchar、nvarchar2的区别
- 关于malloc实际分配内存的探讨
- Delaunay三角剖分
- 在 1080P 的显示器上,4K 的视频是否看起来比 1080P 的视频更清晰?
- 用iDesigner对HR7P275开发板进行程序的仿真和代码烧录过程详解
- 编译程序依赖于 ffmpeg和x264 的链接顺序
- Spring MVC静态资源处理
- HDU 3401 + CDOJ 880 Trade + 生日礼物
- Mysql创建用户账号
- mysql忘记密码