C语言动态字符串拼接中realloc使用不当引发valgrind内存错误
Markdown 格式化规范(翻译自英文要求)
动态字符串拼接函数的内存泄漏问题
我四处查找适配的解决方案,但始终没有找到。我清楚自己无法解决这个问题,根源是对C语言不够熟悉。
我正在编写一个可将一个动态分配字符串拼接到另一个动态分配字符串上的函数,从运行表象来看该函数工作正常,没有暴露内存泄漏问题;但当我使用valgrind检测时,能明显看到存在一些找不到根源的严重内存泄漏,问题似乎出在我使用realloc的方式上。
相关代码
#include <stdio.h> #include <string.h> #include <stdlib.h> void dynamCat(char *dest, const char *src) { size_t len = strlen(dest) + strlen(src) + 1; printf("strLen: %ld\n", len); char *tmp = realloc(dest, len); if(tmp == NULL){ fprintf(stderr, "strAppend: realloc messed up\n"); free(tmp); return; } else { dest = tmp; } strcat(dest, src); } int main (int argc, char *argv[]) { char *one = malloc(4); strcpy(one, "tee"); char *two = malloc(4); strcpy(two, "hee"); printf("one: %s\n", one); printf("two: %s\n", two); dynamCat(one, two); printf("one: %s\n", one); printf("two: %s\n", two); //freeee free(one); free(two); return 0; }
程序正常运行输出
$ ./dynamCat one: tee two: hee strLen: 7 one: teehee two: hee
Valgrind 检测输出
==28804== Memcheck, a memory error detector ==28804== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==28804== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info ==28804== Command: ./dynamCat ==28804== ==28804== Invalid read of size 1 ==28804== at 0x4841C72: strlen (vg_replace_strmem.c:459) ==28804== by 0x48D9407: __vfprintf_internal (in /usr/lib/libc-2.33.so) ==28804== by 0x48C463E: printf (in /usr/lib/libc-2.33.so) ==28804== by 0x1092EE: main (dynamCat.c:37) ==28804== Address 0x4a3c040 is 0 bytes inside a block of size 4 free'd ==28804== at 0x4840D7B: realloc (vg_replace_malloc.c:834) ==28804== by 0x1091FA: dynamCat (dynamCat.c:11) ==28804== by 0x1092D6: main (dynamCat.c:35) ==28804== Block was alloc'd at ==28804== at 0x483E77F: malloc (vg_replace_malloc.c:307) ==28804== by 0x10926D: main (dynamCat.c:26) ==28804== ==28804== Invalid read of size 1 ==28804== at 0x4841C84: strlen (vg_replace_strmem.c:459) ==28804== by 0x48D9407: __vfprintf_internal (in /usr/lib/libc-2.33.so) ==28804== by 0x48C463E: printf (in /usr/lib/libc-2.33.so) ==28804== by 0x1092EE: main (dynamCat.c:37) ==28804== Address 0x4a3c041 is 1 bytes inside a block of size 4 free'd ==28804== at 0x4840D7B: realloc (vg_replace_malloc.c:834) ==28804== by 0x1091FA: dynamCat (dynamCat.c:11) ==28804== by 0x1092D6: main (dynamCat.c:35) ==28804== Block was alloc'd at ==28804== at 0x483E77F: malloc (vg_replace_malloc.c:307) ==28804== by 0x10926D: main (dynamCat.c:26) ==28804== ==28804== Invalid read of size 1 ==28804== at 0x48460D0: mempcpy (vg_replace_strmem.c:1536) ==28804== by 0x48ED211: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib/libc-2.33.so) ==28804== by 0x48D915A: __vfprintf_internal (in /usr/lib/libc-2.33.so) ==28804== by 0x48C463E: printf (in /usr/lib/libc-2.33.so) ==28804== by 0x1092EE: main (dynamCat.c:37) ==28804== Address 0x4a3c042 is 2 bytes inside a block of size 4 free'd ==28804== at 0x4840D7B: realloc (vg_replace_malloc.c:834) ==28804== by 0x1091FA: dynamCat (dynamCat.c:11) ==28804== by 0x1092D6: main (dynamCat.c:35) ==28804== Block was alloc'd at ==28804== at 0x483E77F: malloc (vg_replace_malloc.c:307) ==28804== by 0x10926D: main (dynamCat.c:26) ==28804== ==28804== Invalid read of size 1 ==28804== at 0x48460DE: mempcpy (vg_replace_strmem.c:1536) ==28804== by 0x48ED211: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib/libc-2.33.so) ==28804== by 0x48D915A: __vfprintf_internal (in /usr/lib/libc-2.33.so) ==28804== by 0x48C463E: printf (in /usr/lib/libc-2.33.so) ==28804== by 0x1092EE: main (dynamCat.c:37) ==28804== Address 0x4a3c040 is 0 bytes inside a block of size 4 free'd ==28804== at 0x4840D7B: realloc (vg_replace_malloc.c:834) ==28804== by 0x1091FA: dynamCat (dynamCat.c:11) ==28804== by 0x1092D6: main (dynamCat.c:35) ==28804== Block was alloc'd at ==28804== at 0x483E77F: malloc (vg_replace_malloc.c:307) ==28804== by 0x10926D: main (dynamCat.c:26) ==28804== ==28804== Invalid free() / delete / delete[] / realloc() ==28804== at 0x483F9AB: free (vg_replace_malloc.c:538) ==28804== by 0x109312: main (dynamCat.c:41) ==28804== Address 0x4a3c040 is 0 bytes inside a block of size 4 free'd ==28804== at 0x4840D7B: realloc (vg_replace_malloc.c:834) ==28804== by 0x1091FA: dynamCat (dynamCat.c:11) ==28804== by 0x1092D6: main (dynamCat.c:35) ==28804== Block was alloc'd at ==28804== at 0x483E77F: malloc (vg_replace_malloc.c:307) ==28804== by 0x10926D: main (dynamCat.c:26) ==28804== ==28804== ==28804== HEAP SUMMARY: ==28804== in use at exit: 7 bytes in 1 blocks ==28804== total heap usage: 4 allocs, 4 frees, 4,111 bytes allocated ==28804== ==28804== 7 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==28804== at 0x4840D7B: realloc (vg_replace_malloc.c:834) ==28804== by 0x1091FA: dynamCat (dynamCat.c:11) ==28804== by 0x1092D6: main (dynamCat.c:35) ==28804== ==28804== LEAK SUMMARY: ==28804== definitely lost: 7 bytes in 1 blocks ==28804== indirectly lost: 0 bytes in 0 blocks ==28804== possibly lost: 0 bytes in 0 blocks ==28804== still reachable: 0 bytes in 0 blocks ==28804== suppressed: 0 bytes in 0 blocks ==28804== ==28804== For lists of detected and suppressed errors, rerun with: -s ==28804== ERROR SUMMARY: 9 errors from 6 contexts (suppressed: 0 from 0)
我刚接触C语言,非常感谢各位的帮助。
问题根源
C语言函数参数为值传递,dynamCat的形参dest是main中one指针的临时拷贝,属于函数局部变量。
当realloc执行成功后,会释放原内存块、返回新的内存地址,你把新地址赋值给局部变量dest,这个修改只会在dynamCat函数内部生效,main中的one仍然保存着已经被realloc释放的旧地址,成为悬空指针。
由此引发的连锁问题:
- 后续
printf读取one指向的已释放内存,触发Invalid read错误 - 最后
free(one)释放已被释放的地址,触发Invalid free错误 realloc返回的新内存块没有任何全局指针指向它,函数结束后局部变量dest销毁,这块内存完全泄漏
另外还有一处错误:realloc失败时(返回NULL),原dest指向的内存块不会被释放,你写的free(tmp)(即free(NULL))无意义,且没有告知调用方分配失败的状态。
修正方案
两种可行思路,核心都是把realloc返回的新地址回传给调用方:
方案1:函数返回新指针
// 修改 dynamCat 返回新的地址,分配失败返回NULL char* dynamCat(char *dest, const char *src) { size_t len = strlen(dest) + strlen(src) + 1; char *tmp = realloc(dest, len); if (tmp == NULL) { fprintf(stderr, "strAppend: realloc failed\n"); return NULL; // 返回失败状态,原内存仍有效 } strcat(tmp, src); return tmp; // 把新地址返回给调用方 } // main 中调用时接收返回值 one = dynamCat(one, two); if (one == NULL) { // 自行处理分配失败逻辑 }
方案2:传入指针的地址(二级指针)
// 形参改为二级指针,直接修改调用方的指针变量 void dynamCat(char **dest, const char *src) { size_t len = strlen(*dest) + strlen(src) + 1; char *tmp = realloc(*dest, len); if (tmp == NULL) { fprintf(stderr, "strAppend: realloc failed\n"); return; } *dest = tmp; // 直接修改main中的one指针 strcat(*dest, src); } // main 中调用时传入指针的地址 dynamCat(&one, two);
修正后main中one会指向realloc返回的新内存,后续读写、释放都不会再触发内存错误。
内容的提问来源于stack exchange,提问作者salesman
相关产品推荐
相关产品推荐

