(void *) 的 realloc 中的内存泄漏以连接字符串
Posted
技术标签:
【中文标题】(void *) 的 realloc 中的内存泄漏以连接字符串【英文标题】:Memory leak in realloc of (void *) to concatenate string 【发布时间】:2020-11-09 03:08:45 【问题描述】:给定一个带有 void 指针 (void *) value
的结构对象,该指针使用 malloc 初始化以保存字符串 "chapt"
。
之后,使用realloc
腾出足够的内存来连接另一个字符串。
/* Standard Imports */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
struct generic_type
void *value;
void (*add)(struct generic_type, int);
;
/* Function Declarations */
static void TestRun();
static void AddNumToString(struct generic_type element, int num);
#define TEST_ARRAY_SIZE 1
int main(int argc, char *argv[])
TestRun();
(void) argc;
(void) *argv;
return 0;
static void TestRun()
struct generic_type element;
element.value = malloc(sizeof(char) * 6);
assert (NULL != element.value);
element.value = strcpy(element.value, "chapt");
element.add = AddNumToString;
element.add(element, 10);
free(element.value);
static void AddNumToString(struct generic_type element, int num)
size_t num_length = snprintf(NULL, 0, "%d", num);
size_t str_length = strlen((char *)(element.value));
size_t new_length = str_length + num_length + 1;
char *num_string = (char *)malloc(sizeof(char) * (num_length + 1));
sprintf(num_string, "%d", num);
element.value = realloc(element.value, sizeof(char) * new_length);
assert (NULL != element.value);
element.value = strcat(((char *)(element.value)), num_string);
free(num_string);
此实现会产生正确的输出,但存在内存泄漏:
==29031== Memcheck, a memory error detector
==29031== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==29031== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==29031== Command: ./a.out
==29031==
==29031== Invalid free() / delete / delete[] / realloc()
==29031== at 0x4C30D3B: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29031== by 0x1088EB: TestRun (teststructs.c:40)
==29031== by 0x108862: main (teststructs.c:22)
==29031== Address 0x522d040 is 0 bytes inside a block of size 6 free'd
==29031== at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29031== by 0x108999: AddNumToString (teststructs.c:52)
==29031== by 0x1088DF: TestRun (teststructs.c:39)
==29031== by 0x108862: main (teststructs.c:22)
==29031== Block was alloc'd at
==29031== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29031== by 0x10887B: TestRun (teststructs.c:34)
==29031== by 0x108862: main (teststructs.c:22)
==29031==
==29031==
==29031== HEAP SUMMARY:
==29031== in use at exit: 8 bytes in 1 blocks
==29031== total heap usage: 3 allocs, 3 frees, 17 bytes allocated
==29031==
==29031== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==29031== at 0x4C31D2F: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29031== by 0x108999: AddNumToString (teststructs.c:52)
==29031== by 0x1088DF: TestRun (teststructs.c:39)
==29031== by 0x108862: main (teststructs.c:22)
==29031==
==29031== LEAK SUMMARY:
==29031== definitely lost: 8 bytes in 1 blocks
==29031== indirectly lost: 0 bytes in 0 blocks
==29031== possibly lost: 0 bytes in 0 blocks
==29031== still reachable: 0 bytes in 0 blocks
==29031== suppressed: 0 bytes in 0 blocks
==29031==
==29031== For counts of detected and suppressed errors, rerun with: -v
==29031== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
似乎问题出在realloc
行,但我似乎看不出它的问题。
在初始化期间分配足够的内存并避免realloc
可以解决问题,但我很清楚为什么此时这不起作用。
【问题讨论】:
请创建一个minimal reproducible example。struct sgeneric
是什么?函数是怎么调用的?你如何初始化所有相关的指针?无效访问发生在哪里?是读还是写?为什么你到处都放(char *)
? has a memory leak:
请展示 valgrind 向您展示的内容。应该有整个堆栈,包括附加信息。您刚刚发布了有关一些地址的信息。作为旁注,只需将snprintf
转换为element.value
,不需要num_string
。
您显示的代码对我来说看起来不错,因此错误可能在其他地方。我同意你应该创建一个最小的例子。
您的问题几乎可以肯定是 FreeStructWithString 中的代码使用 realloc 调用之前的指针值。
这仍然不是一个最小的可重现示例。我们正在寻找一个可以编译和运行的完整程序。
\0"
- \0
之一被忽略,只需删除 \0
。 ws8_main.c:100
看起来像一个相当短的文件 - 您可以考虑将其全部发布。 (我看到您的函数指针按值获取结构。您不会偶然调用 realloc 并修改 void (*add)(struct sgeneric,..)
中的 sgeneric::value
吗?)
【参考方案1】:
AddNumToString
按值传递其element
参数,以便它获得传递给它的对象的副本。这意味着当你这样做时
element.value = realloc(element.value, sizeof(char) * new_length);
element
中包含的原始指针被释放,但新指针存储在副本中。当AddNumToString
返回时副本丢失,因此新分配的空间泄漏。更糟糕的是,调用者中的对象保持不变;特别是,它仍然包含现在已被释放的原始指针。因此,当它最终被释放时(未在您当前的代码中显示),这是双重释放,这很糟糕。
您可能希望AddNumToString
改为使用指向struct generic_type
的指针,以便它可以实际修改对象。
【讨论】:
我尝试在函数调用前后检查element.value
的地址和长度,它返回相同的地址并且长度增加了正确的长度。这真的让我很反感。感谢您的详细回答。以上是关于(void *) 的 realloc 中的内存泄漏以连接字符串的主要内容,如果未能解决你的问题,请参考以下文章