解决C代码内存写超问题
作者:wallace-lai 发布:2023-08-22 更新:2023-08-22
在运行程序时发现一个基本的getchar()调用竟然报了assertion failed错误。非常奇怪,因为getchar()是非常基本的接口,几乎不可能出错。
main: malloc.c:2379: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Aborted (core dumped)
经过搜索后发现原因可能是有内存错误。函数的调用逻辑如下所示,在getchar()调用之前确实有内存申请的操作并且对申请的内存做了写操作。很有可能是内存申请或者申请后的写操作导致后续的getchar()调用失败的。
- CreateNewGame
- CreateNewFrame
- malloc
- GameRun
- getchar
使用valgrind工具进行内存读写的检测,果然发现了非法写入操作,某些写操作超出了内存申请的范围。
$ valgrind --leak-check=full ./main
==12468== Memcheck, a memory error detector
==12468== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12468== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==12468== Command: ./main
==12468==
==12468== Invalid write of size 4
==12468== at 0x109DAB: CreateNewFrame (frame.c:87)
==12468== by 0x109740: CreateNewGame (game.c:102)
==12468== by 0x1093A6: main (main.c:18)
==12468== Address 0x4a476b0 is 0 bytes after a block of size 304 alloc'd
==12468== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==12468== by 0x109C90: CreateNewFrame (frame.c:68)
==12468== by 0x109740: CreateNewGame (game.c:102)
==12468== by 0x1093A6: main (main.c:18)
回到代码,一开始真的没发现有什么问题,但又确实存在写超的情况。于是我只好手动计算结构体大小,发现申请的大小远远小于了所需的大小。再仔细一看代码,发现问题所在了————额外的mapSize大小内存是用来存放BlockType类型数据的,但是mapSize只计算了个数,还应该乘上BlockType类型的大小才对!
Frame *CreateNewFrame(int w, int h, int thick, int x, int y)
{
size_t mapSize = (w + thick * 2) * (h + thick * 2); // 内存写超了
size_t totalSize = sizeof(Frame) + mapSize;
Frame *frame = malloc(totalSize);
HANDLE_ON_ERROR(frame == NULL);
memset(frame, 0, totalSize);
// 对frame进行写操作
// ...
frame->map[j + i * frame->mapCol] = // ...
// ...
return frame;
}
将代码修改成以下的样子,问题解决!
size_t mapSize = (w + thick * 2) * (h + thick * 2) * sizeof(BlockType);
总结
valgrind是一个内存检测的好工具,合理运用它可以提升代码质量