使用new数组时触发Valgrind非法读取错误,请求排查
Invalid read of size 8(__strlen_sse42触发)的问题 我来帮你拆解下这个问题——你说分配了11字节内存,用来存10字节内容加空终止符,逻辑看似没问题,但Valgrind却在strlen的SSE优化实现里报了无效读。咱们一步步来分析:
先搞懂错误本身
Invalid read of size 8意味着程序尝试读取8字节的内存块,但这块内存要么没被你分配,要么超出了你申请的范围。而触发点__strlen_sse42是libc针对64位系统做的优化版strlen:它不会逐字节找空终止符,而是按8字节的块批量读取内存,直到找到包含\0的块,再在块里定位具体位置。
你的场景哪里出了问题?
你分配了11字节,存10字节内容+1字节\0,总长度刚好11字节,看似刚好填满内存。但问题就出在strlen的批量读取上:
假设你的内存起始地址是0x4c2d040,11字节的内存会到0x4c2d04a结束。当strlen处理到0x4c2d048这个地址时,它会尝试读取从这里开始的8字节(到0x4c2d050),但0x4c2d04b到0x4c2d050是你没分配的内存区域,Valgrind自然会检测到这个越界读,抛出错误。
给你几个可行的解决方案
方案1:给内存多留几个“缓冲字节”
既然strlen是按8字节块读取,那你可以把分配的内存长度凑到8的倍数(比如16字节),这样即使strlen批量读取,也不会超出你申请的内存范围。比如把malloc(11)改成malloc(16),既不会浪费太多内存,又能避免Valgrind的报错。方案2:确保空终止符确实被正确写入
有时候看似加了\0,但复制操作可能出错:比如用memcpy(dest, src, 10)只复制了10字节内容,却忘了手动加dest[10] = '\0';,导致strlen会一直读下去直到找到随机的\0,必然越界。一定要检查你的复制逻辑:
错误示例:char *buf = malloc(11); memcpy(buf, "0123456789", 10); // 只复制了内容,没加终止符 strlen(buf); // 越界风险极高正确示例:
char *buf = malloc(11); memcpy(buf, "0123456789", 10); buf[10] = '\0'; // 手动补全终止符 // 或者直接用strcpy(前提是源字符串本身是终止的) strcpy(buf, "0123456789");方案3:确认内存对齐(一般不用手动处理)
64位系统下,malloc默认会把内存对齐到16字节,所以这个问题大概率不存在。但如果你用了自定义的内存分配函数,要确保它能做到至少8字节对齐,否则也可能触发类似的读取错误。
总结
这个问题本质是strlen的优化实现和你刚好卡边界的内存分配之间的冲突——不是你的逻辑完全错误,而是Valgrind严格检测到了strlen为了效率而做的“超范围读取”。给内存多分配几个字节,或者确保字符串终止符正确,就能解决这个问题。
内容的提问来源于stack exchange,提问作者imreal

