You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的定义无关:

  1. 文件名实际需要8字节内存
    你生成的文件名格式是XXX.jpg(比如000.jpg),这个字符串包含:3位数字 + 1个点 + 3个字母 + 1个字符串结束符\0,总共8个字符。每个char占1字节,因此需要8字节内存才能完整存储。

  2. 第一种分配方式内存严重不足
    malloc(1 * sizeof(BYTE))确实只分配了1字节内存,但你用sprintf往里面写入8个字符,会直接写到分配内存的边界之外,触发Valgrind的无效写入错误。这种情况属于未定义行为,程序可能看似运行,但随时可能崩溃或产生错误结果。

  3. 第二种分配方式刚好满足需求
    malloc(8 * sizeof(char))分配了8字节内存,刚好能容纳完整的文件名字符串(包括结束符),不会出现内存越界,因此Valgrind和check50都能通过检测。

  4. BYTE和char的大小一致
    BYTE是uint8_t的别名,本身就是1字节,和大多数系统中char的大小一致。问题出在你计算内存大小的逻辑,而非使用BYTE还是char。

内容的提问来源于stack exchange,提问作者Nico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:31:06