CS50 第4周,“恢复问题”,内存泄漏问题。

huangapple go评论62阅读模式
英文:

CS50 week 4, "Recover problem", issue with memory leak

问题

"your text"I actually, after a lot of sweating, finished the "recover" problem of the cs50 course. The last error I was getting was a memory leak and although I fixed it I still do not understand why I was getting it. So I would be glad if somebody could explain it to me.

The line that gives me the error is the one below (BYTE is a typedef they suggested to create in the problem's specification, I created it like this: typedef uint8_t BYTE; and I used it somewhere else in the code without problems):

//Allocating space for images names
char *filename = malloc(1 *(sizeof(BYTE)));

And this is what valgrind says:

==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== 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 byte 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==
==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 (sup

英文:

your textI actually, after a lot of sweating, finished the "recover" problem of the cs50 course. The last error I was getting was a memory leak and although I fixed it I still do not understand why I was getting it. So I would be glad if somebody could explain it to me.

The line that give me the error is the one below( BYTE is a typedef they suggested to create in the problem's specification, I created it like this: typedef uint8_t BYTE; and I used it somewhere else in the code without problems)

//Allocating space for images names
char *filename = malloc(1 *(sizeof(BYTE)));

And this is what valgrind says:

  ==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==    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==
  ==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)

And these are the check50 results:

:) 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.

But if a change the error line in:

 //Allocating space for images names
 char *filename = malloc(8 *(sizeof(char)));

everything works fine. The space allocated is 1 byte in both case and it is freed in both cases, why do I get an error if I use the typedef BYTE?

答案1

得分: 0

//为图像名称分配空间
char *filename = malloc(1 * (sizeof(BYTE)));

BYTE 别名为 uint8_t,它是一个至少为 8 位的无符号类型,本身别名为 unsigned char。这仅为 1 个 char/字节分配内存。假设它是一个字符串,它只能包含空字节。

//为图像名称分配空间
char *filename = malloc(8 * (sizeof(char)));

> 在这两种情况下,分配的空间为 1 字节。

不,这里分配的空间是 8 字节(sizeof (char) 根据定义为 1),足够容纳 000.jpg + \0。但在这里不需要使用 malloc()。分配的大小在编译时已知且保持不变,因此最好使用一个普通的 char 数组[8]。

> 我在代码的其他地方使用过它而没有问题。

你很幸运。引用超出边界内存是未定义行为(包括读取和写入)。

> 允许的未定义行为范围从完全忽略情况,具有不可预测的结果,到在环境特性的编译或程序执行期间以文档化的方式行为(是否发出诊断消息),或者终止翻译或执行(是否发出诊断消息)。

操作系统按页面分配内存(通常为 4k 左右)。如果你要求 malloc() 分配一个字节,它可能会分配页面大小的内存(如果 malloc() 的调用没有被优化或内存不足)。然后,malloc() 将页面组织成块,进行一些簿记,确保内存对于任何内置类型都具有合适的对齐方式,等等。写入超出块的末尾可能仍然在同一页面上,对于操作系统来说,你没有访问非法内存地址。因此,你的程序可能表现得非常正常,直到你走得太远,它会导致段错误,或者擦除硬盘,或者发射导弹(假设你已安装了正确的硬件),或者更糟糕,使恶魔从鼻子里飞出来。.


请记住这句格言:

"一切可能出错的事情都会在最糟糕的时候出错。"

英文:
//Allocating space for images names
char *filename = malloc(1 *(sizeof(BYTE)));

BYTE is aliasing uint8_t, which is an (at least) 8-bit unsigned type, which itself is aliasing an unsigned char. This allocates memory for just 1 char/ byte. Assuming it's a string, it can only ever hold the null-byte.


 //Allocating space for images names
 char *filename = malloc(8 *(sizeof(char)));

> The space allocated is 1 byte in both case.

No, the space allocated is 8 bytes here (sizeof (char) is by definition 1), which is large enough to hold 000.jpg + \0. But you do not require malloc() here. The size of the allocation is known at compile time and remains constant, so it'd better to just use a plain array[8] of char.

> I used it somewhere else in the code without problems.

You got lucky. It's undefined behaviour to reference out of bounds memory (and that includes reading and writing).

> Permissible undefined behavior ranges from ignoring the situation
> completely with unpredictable results, to behaving during translation
> or program execution in a documented manner characteristic of the
> environment (with or without the issuance of a diagnostic message), to
> terminating a translation or execution (with the issuance of a
> diagnostic message).

The OS gives out memory in pages (usually 4k or so). If you ask malloc() for one byte, it might allocate a pagesize worth of memory instead (if the call to malloc() doesn't get optimized or it's out of memory). malloc() then organizes the page into chunks, do some book keeping, ensures the memory is suitably-aligned for any built-in type, and more. Writing past the end of the chunk may stay on the same page, and as far as the OS is concerned, you didn't access any illegal memory address. So your program might appear to work just fine until you go too far, and it segfaults, or erase your harddrive, or launch a missile (assuming you have the correct hardware installed), or worse, make demons fly out of your nose..


Remember the adage:

"Anything that can go wrong will go wrong, and at the worst possible time."

huangapple
  • 本文由 发表于 2023年2月19日 10:39:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75497671.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定