(void *) 的 realloc 中的内存泄漏以连接字符串

Memory leak in realloc of (void *) to concatenate string

给定一个带有空指针 (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 解决了问题,但我更清楚为什么这在此时不起作用。

AddNumToString 按值传递其 element 参数,因此它获得传递给它的对象的 copy。这意味着当你做

element.value = realloc(element.value, sizeof(char) * new_length);

element中包含的原始指针被释放,但新的指针存储在副本中。 AddNumToStringreturns时副本丢失,所以新分配的space被泄露。更糟糕的是,调用者中的对象保持不变;特别是,它仍然包含现在已被释放的原始指针。因此,当它最终被释放时(未显示在您当前的代码中),这是一个双重释放,这很糟糕。

您可能希望 AddNumToString 取一个指向 struct generic_type 的指针,这样它就可以实际修改对象。