C 中嵌套结构类型和复制函数的内存泄漏

Memory leaks with nested struct types and copy functions in C

我对嵌套结构及其专用复制函数有疑问。类型结构如下:

  1. 程序中有一个Data实例
  2. 每个 Problem 实例都包含一个指向 Data 实例的指针
  3. 每个 Bigproblem 个实例都有自己的 Problem 个实例。

因此,当复制 BigProblem 实例时,也会生成 Problem 实例的副本。代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// types

struct Data {
    int n;
    int *x;
};
typedef struct Data data_t;

struct Problem {
    data_t *data;
    int m;
    int *y;
};
typedef struct Problem problem_t;

struct BigProblem {
    problem_t *prob;
    int p;
    int *z;
};
typedef struct BigProblem big_t;

// functions

data_t init_data(int s)
{
    data_t dat;
    dat.n = s;
    dat.x = calloc(s, sizeof(int));
    return dat;
}

problem_t init_problem(data_t *dat)
{
    problem_t prob;
    prob.data = dat;
    prob.m = 5;
    prob.y = calloc(prob.data->n, sizeof(int));
    return prob;
}

problem_t copy_problem(problem_t *prob)
{
    int i;
    problem_t probc;
    probc.data = prob->data;
    probc.m = prob->m;
    probc.y = calloc(prob->data->n, sizeof(int));
    for (i=0; i<prob->data->n; i++)
        probc.y[i] = prob->y[i];
    return probc;
}

big_t init_bigproblem(problem_t *prob)
{
    big_t bigprob;
    bigprob.prob = prob;
    bigprob.p = 3;
    bigprob.z = calloc(prob->m, sizeof(int));
    return bigprob;
}

big_t copy_bigproblem(big_t *bigprob)
{
    int i;
    big_t bigprobc;
    problem_t probc = copy_problem(bigprob->prob);
    bigprobc.prob = &probc;
    bigprobc.p = bigprob->p;
    bigprobc.z = calloc(bigprob->prob->m, sizeof(int));
    for (i=0; i<bigprob->prob->m; i++)
        bigprobc.z[i] = bigprob->z[i];
    return bigprobc;
}

void free_data(data_t *dat)
{
    free(dat->x);
}

void free_problem(problem_t *prob)
{
    free(prob->y);
}

void free_bigproblem(big_t *bigprob)
{
    free(bigprob->z);
    free_problem(bigprob->prob);
}

int main(int argc, char **argv)
{
    data_t mydata = init_data(10);
    problem_t myproblem = init_problem(&mydata);
    big_t mybigprob = init_bigproblem(&myproblem);

    big_t big_copy = copy_bigproblem(&mybigprob);

    free_bigproblem(&mybigprob);
    free_bigproblem(&big_copy);
    free_data(&mydata);

    return 0;
}

我怀疑问题与 copy_bigproblem 中的这些行有关:

    problem_t probc = copy_problem(bigprob->prob);
    bigprobc.prob = &probc;

但我无法准确指出这里出了什么问题。 Valgrind 建议问题发生在释放 free_bigproblem 中的 prob 变量时,但如果正确复制了 Problem 实例,则不会发生这种情况。 Valgrind 输出在这里:

==10384== Memcheck, a memory error detector
==10384== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==10384== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==10384== Command: ./typedeftest
==10384== 
==10384== Conditional jump or move depends on uninitialised value(s)
==10384==    at 0x4C2B1B6: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10384==    by 0x4007D4: free_problem (typedeftest.c:97)
==10384==    by 0x400801: free_bigproblem (typedeftest.c:103)
==10384==    by 0x400879: main (typedeftest.c:115)
==10384==  Uninitialised value was created by a stack allocation
==10384==    at 0x4007B9: free_problem (typedeftest.c:96)
==10384== 
==10384== Invalid free() / delete / delete[] / realloc()
==10384==    at 0x4C2B200: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10384==    by 0x4007D4: free_problem (typedeftest.c:97)
==10384==    by 0x400801: free_bigproblem (typedeftest.c:103)
==10384==    by 0x400879: main (typedeftest.c:115)
==10384==  Address 0x400f610 is in the Text segment of /usr/lib/ld-2.21.so
==10384==    at 0x400F610: _dl_fini (in /usr/lib/ld-2.21.so)
==10384== 
==10384== 
==10384== HEAP SUMMARY:
==10384==     in use at exit: 40 bytes in 1 blocks
==10384==   total heap usage: 5 allocs, 5 frees, 160 bytes allocated
==10384== 
==10384== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==10384==    at 0x4C2C080: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==10384==    by 0x40061A: copy_problem (typedeftest.c:58)
==10384==    by 0x400704: copy_bigproblem (typedeftest.c:81)
==10384==    by 0x400861: main (typedeftest.c:112)
==10384== 
==10384== LEAK SUMMARY:
==10384==    definitely lost: 40 bytes in 1 blocks
==10384==    indirectly lost: 0 bytes in 0 blocks
==10384==      possibly lost: 0 bytes in 0 blocks
==10384==    still reachable: 0 bytes in 0 blocks
==10384==         suppressed: 0 bytes in 0 blocks
==10384== 
==10384== For counts of detected and suppressed errors, rerun with: -v
==10384== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

如有任何帮助,我们将不胜感激。

我做了一些小改动 考虑 memcpy 而不是循环

#include <stdio.h>                                                             
#include <stdlib.h>                                                            
#include <string.h>                                                            

// types                                                                       

struct Data {                                                                  
    int n;                                                                     
    int *x;                                                                    
};                                                                             
typedef struct Data data_t;                                                    

struct Problem {                                                               
    data_t *data;                                                              
    int m;                                                                     
    int *y;                                                                    
};                                                                             
typedef struct Problem problem_t;                                              

struct BigProblem {                                                            
    problem_t prob;                                                            
    int p;                                                                     
    int *z;                                                                    
};                                                                             
typedef struct BigProblem big_t;                                               

// functions                                                                   

void init_data(data_t *dat, int s)                                             
{                                                                              
    dat->n = s;                                                                
    dat->x = calloc(s, sizeof(int));                                           
}                                                                              

void init_problem(problem_t *prob, data_t *dat)                                
{                                                                              
    prob->data = dat;                                                          
    prob->m = 5;                                                               
    prob->y = calloc(prob->data->n, sizeof(int));                              
}                                                                              

void init_bigproblem(big_t *bigprob, problem_t *prob)                          
{                                                                              
    bigprob->prob = *prob;                                                     
    bigprob->p = 3;                                                            
    bigprob->z = calloc(prob->m, sizeof(int));                                 
}

copy_problem(problem_t *src, problem_t *cpy)                                   
{                                                                              
    int i;                                                                     
    cpy->data = src->data;                                                     
    cpy->m = src->m;                                                           
    cpy->y = calloc(src->data->n, sizeof(int));                                
    for (i=0; i<src->data->n; i++)                                             
        cpy->y[i] = src->y[i];                                                 
}                                                                              

void copy_bigproblem(big_t *src, big_t *cpy)                                   
{                                                                              
    int i;                                                                     

    copy_problem(&src->prob, &cpy->prob);                                      
    cpy->p = src->p;                                                           
    cpy->z = calloc(src->prob.m, sizeof(int));                                 
    for (i=0; i<src->prob.m; i++)                                              
        cpy->z[i] = src->z[i];                                                 
}                                                                              

void free_data(data_t *dat)                                                    
{                                                                              
    free(dat->x);                                                              
}                                                                              

void free_problem(problem_t *prob)                                             
{                                                                              
    free(prob->y);                                                             
}                                                                              

void free_bigproblem(big_t *bigprob)                                           
{                                                                              
    free(bigprob->z);                                                          
    free_problem(&bigprob->prob);                                              
}                                                                              

int main(int argc, char **argv)                                                
{                                                                              
    data_t mydata;                                                             
    problem_t myproblem;                                                       
    big_t mybigprob, big_copy;                                                 

    init_data(&mydata, 10);                                                    
    init_problem(&myproblem, &mydata);                                         
    init_bigproblem(&mybigprob, &myproblem);                                   

    copy_bigproblem(&mybigprob, &big_copy);                                    


    free_bigproblem(&mybigprob);                                               
    free_bigproblem(&big_copy);                                                
    free_data(&mydata);                                                        

    return 0;                                                                  
}