CS50 Week4 Recover问题:BYTE类型malloc引发内存错误的疑问
问题
完成CS50的"Recover"习题时,为文件名分配内存遭遇内存错误:
- 最初使用题目定义的
typedef uint8_t BYTE;,执行char *filename = malloc(1 *(sizeof(BYTE)));,Valgrind检测到大量无效写入错误,check50也提示内存错误不通过 - 改为
char *filename = malloc(8 *(sizeof(char)));后,所有检测正常通过
我误以为两种方式分配的都是1字节内存且都已释放,不解为何前者报错,求解释。
Valgrind检测日志
==2963== Memcheck, a memory error detector ==2963== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==2963== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==2963== Command: ./recover card.raw ==2963== ==2963== Invalid write of size 1 ==2963== at 0x4A17034: _IO_default_xsputn (genops.c:394) ==2963== by 0x4A17034: _IO_default_xsputn (genops.c:370) ==2963== by 0x4A09822: _IO_padn (iopadn.c:64) ==2963== by 0x49FF817: pad_func (vfprintf-internal.c:196) ==2963== by 0x49FF817: __vfprintf_internal (vfprintf-internal.c:1516) ==2963== by 0x4A0AA08: __vsprintf_internal (iovsprintf.c:95) ==2963== by 0x49E99A7: sprintf (sprintf.c:30) ==2963== by 0x1092FD: main (recover.c:52) ==2963== Address 0x4bb5262 is 1 bytes after a block of size 1 alloc'd ==2963== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2963== by 0x109241: main (recover.c:29) ==2963== ==2963== Invalid write of size 1 ==2963== at 0x4A17034: _IO_default_xsputn (genops.c:394) ==2963== by 0x4A17034: _IO_default_xsputn (genops.c:370) ==2963== by 0x49FED28: outstring_func (vfprintf-internal.c:239) ==2963== by 0x49FED28: __vfprintf_internal (vfprintf-internal.c:1516) ==2963== by 0x4A0AA08: __vsprintf_internal (iovsprintf.c:95) ==2963== by 0x49E99A7: sprintf (sprintf.c:30) ==2963== by 0x1092FD: main (recover.c:52) ==2963== Address 0x4bb5262 is 1 bytes after a block of size 1 alloc'd ==2963== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2963== by 0x109241: main (recover.c:29) ==2963== ==2963== Invalid write of size 1 ==2963== at 0x4A17034: _IO_default_xsputn (genops.c:394) ==2963== by 0x4A17034: _IO_default_xsputn (genops.c:370) ==2963== by 0x49FF049: outstring_func (vfprintf-internal.c:239) ==2963== by 0x49FF049: __vfprintf_internal (vfprintf-internal.c:1593) ==2963== by 0x4A0AA08: __vsprintf_internal (iovsprintf.c:95) ==2963== by 0x49E99A7: sprintf (sprintf.c:30) ==2963== by 0x1092FD: main (recover.c:52) ==2963== Address 0x4bb5263 is 2 bytes after a block of size 1 alloc'd ==2963== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2963== by 0x109241: main (recover.c:29) ==2963== ==2963== Invalid write of size 1 ==2963== at 0x4A0AA0E: __vsprintf_internal (iovsprintf.c:97) ==2963== by 0x49E99A7: sprintf (sprintf.c:30) ==2963== by 0x1092FD: main (recover.c:52) ==2963== Address 0x4bb5267 is 6 bytes after a block of size 1 alloc'd ==2963== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2963== by 0x109241: main (recover.c:29) ==2963== ==2963== Syscall param openat(filename) points to unaddressable byte(s) ==2963== at 0x4A9D6EB: open (open64.c:41) ==2963== by 0x4A15135: _IO_file_open (fileops.c:188) ==2963== by 0x4A15491: _IO_file_fopen@@GLIBC_2.2.5 (fileops.c:280) ==2963== by 0x4A0872D: __fopen_internal (iofopen.c:75) ==2963== by 0x4A0872D: fopen@@GLIBC_2.2.5 (iofopen.c:86) ==2963== by 0x109316: main (recover.c:56) ==2963== Address 0x4bb5261 is 0 bytes after a block of size 1 alloc'd ==2963== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2963== by 0x109241: main (recover.c:29) ==2963== ==2963== HEAP SUMMARY: ==2963== in use at exit: 0 bytes in 0 blocks ==2963== total heap usage: 104 allocs, 104 frees, 233,481 bytes allocated ==2963== ==2963== All heap blocks were freed -- no leaks are possible ==2963== ==2963== For lists of detected and suppressed errors, rerun with: -s ==2963== ERROR SUMMARY: 400 errors from 5 contexts (suppressed: 0 from 0)
check50检测结果
:) recover.c exists. :) recover.c compiles. :) handles lack of forensic image :) recovers 000.jpg correctly :) recovers middle images correctly :) recovers 049.jpg correctly :( program is free of memory errors valgrind tests failed; see log for more information.
解答
核心问题是你对文件名所需内存的计算完全错误,和BYTE的定义无关:
文件名实际需要8字节内存
你生成的文件名格式是XXX.jpg(比如000.jpg),这个字符串包含:3位数字 + 1个点 + 3个字母 + 1个字符串结束符\0,总共8个字符。每个char占1字节,因此需要8字节内存才能完整存储。第一种分配方式内存严重不足
malloc(1 * sizeof(BYTE))确实只分配了1字节内存,但你用sprintf往里面写入8个字符,会直接写到分配内存的边界之外,触发Valgrind的无效写入错误。这种情况属于未定义行为,程序可能看似运行,但随时可能崩溃或产生错误结果。第二种分配方式刚好满足需求
malloc(8 * sizeof(char))分配了8字节内存,刚好能容纳完整的文件名字符串(包括结束符),不会出现内存越界,因此Valgrind和check50都能通过检测。BYTE和char的大小一致
BYTE是uint8_t的别名,本身就是1字节,和大多数系统中char的大小一致。问题出在你计算内存大小的逻辑,而非使用BYTE还是char。
内容的提问来源于stack exchange,提问作者Nico
相关产品推荐
相关产品推荐

