gpt4 book ai didi

c - 无法从结构数组中释放内存,valgrind 显示内存泄漏

转载 作者:太空宇宙 更新时间:2023-11-04 08:23:53 25 4
gpt4 key购买 nike

我有一个嵌套结构,我正在为其动态分配内存并创建数组。所以,它就像一个结构有行,行有列,两者都是结构。

首先,我为主结构分配内存,然后根据输入行为行结构*(行数)分配内存,然后遍历行并为列数分配内存。

赋值代码:

    int parse_json_to_result(char *json, db_res_t** result) {
printf("received json: %s\n", json);
cJSON *root, *record;
int recordCount = 0;
int colCount = 0;
int i = 0;
int j = 0;
int int_val = 0;
char *str_val = '\0';

root = cJSON_Parse(json);
recordCount = cJSON_GetArraySize(root);
*result = calloc(1,sizeof(db_res_t));
(*result)->n = recordCount;

// malloc for number of rows.
(*result)->rows = calloc(recordCount,sizeof(db_row_t) );

//this is done to get the count of columns only once.
record = cJSON_GetArrayItem(root, i);
colCount = cJSON_GetArraySize(record);

for (i = 0; i < recordCount; i++) {
j = 0;
record = cJSON_GetArrayItem(root, i);
(*result)->rows->n = colCount;
// malloc for number of coulmns in each row.
(*result)->rows[i].values = calloc(colCount, sizeof(db_val_t) );
cJSON *subitem = record->child;
while (subitem) {
if (subitem->type == cJSON_Number) {
int_val =
cJSON_GetObjectItem(record, subitem->string)->valueint;
(*result)->rows[i].values[j].type = DB_INT;
(*result)->rows[i].values[j].nul = 0;
(*result)->rows[i].values[j++].val.int_val = int_val;
// printf("%d\n", int_val);
} else {
str_val =
cJSON_GetObjectItem(record, subitem->string)->valuestring;
// printf("%s\n", str_val);
(*result)->rows[i].values[j].type = DB_STRING;
if (strcmp(str_val, "") == 0) {
(*result)->rows[i].values[j].nul = 1;
(*result)->rows[i].values[j++].free = 0;
} else {
(*result)->rows[i].values[j].nul = 0;
(*result)->rows[i].values[j].free = 1;
(*result)->rows[i].values[j++].val.string_val = strdup(str_val);
}
}
subitem = subitem->next;
}
}
cJSON_Delete(root);
return 1;
}

结构:

struct _str{
char* s; /**< string as char array */
int len; /**< string length, not including null-termination */
};

typedef struct _str str;
typedef str* db_key_t;

typedef enum {
DB_INT, /**< represents an 32 bit integer number */
DB_BIGINT, /**< represents an 64 bit integer number */
DB_DOUBLE, /**< represents a floating point number */
DB_STRING, /**< represents a zero terminated const char* */
DB_STR, /**< represents a string of 'str' type */
DB_DATETIME, /**< represents date and time */
DB_BLOB, /**< represents a large binary object */
DB_BITMAP /**< an one-dimensional array of 32 flags */
} db_type_t;


typedef struct {
db_type_t type; /**< Type of the value */
int nul; /**< Means that the column in database has no value */
int free; /**< Means that the value should be freed */
/** Column value structure that holds the actual data in a union. */
union {
int int_val; /**< integer value */
long long bigint_val; /**< big integer value */
double double_val; /**< double value */
time_t time_val; /**< unix time_t value */
const char* string_val; /**< zero terminated string */
str str_val; /**< str type string value */
str blob_val; /**< binary object data */
unsigned int bitmap_val; /**< Bitmap data type */
} val;
} db_val_t;
typedef struct db_row {
db_val_t* values; /**< Columns in the row */
int n; /**< Number of columns in the row */
} db_row_t;
struct db_row;
typedef struct db_res {
struct {
db_key_t* names; /**< Column names */
db_type_t* types; /**< Column types */
int n; /**< Number of columns */
} col;
struct db_row* rows; /**< Rows */
int n; /**< Number of rows in current fetch */
int res_rows; /**< Number of total rows in query */
int last_row; /**< Last row */
} db_res_t;

释放内存的代码:

int free_result(db_res_t* _r)
{
if (!_r)
{
return -1;
}
int i,row_count=0;
int col_count=0;
printf("freeing result set at %p\n", _r);
row_count = _r->n;
printf("RowCount %d .\n",row_count);
for(i=0;i<row_count;i++)
{
printf("Freeing %d row.\n",i);
col_count= _r->rows[i].n;
printf("col_count %d .\n",col_count);
int j=0;
for(j=0;j<col_count;j++)
{
if(_r->rows[i].values[j].type == DB_STRING && _r->rows[i].values[j].nul==0)
{
printf("Freeing %d col.\n",j);
free(_r->rows[i].values[j].val.string_val);
_r->rows[i].values[j].val.string_val =NULL;
}
else if(_r->rows[i].values[j].type == DB_STR && _r->rows[i].values[j].nul==0)
{
printf("Freeing %d col.",j);
free(_r->rows[i].values[j].val.str_val.s);
_r->rows[i].values[j].val.str_val.s =NULL;
}
}
//free all value colums for each row.
free(_r->rows[i].values);
_r->rows[i].values = NULL;
}
//free all rows
free(_r->rows);
_r->rows =NULL;
//free resultset
free(_r);
_r = NULL;
//this will print nil.
printf("freed result set a %p\n", _r);
return 0;
}

我的示例输入是 2 行,每行 10 列,其中只有几列是 char*。所以在免费的情况下,我希望输出有点像:

正在释放 0 行。col_count 10 。释放 1 列。释放 2 列。释放 3 列。释放 4 列。释放 1 行。col_count 10 。释放 1 列。释放 2 列。释放 3 列。释放 4 列。释放结果集 a (nil)

但我实际得到的是:

释放位于 0x18e13e0 的结果集行数 3。释放 0 行。col_count 10 。释放 1 列。释放 2 列。释放 3 列。释放 4 列。释放 1 行。col_count 0 。释放 2 行。col_count 0 .释放结果集 a (nil)

循环不会超出第一行。我认为所有的值都在第一次通过时被取消引用。但是怎么办?我无法理解。我能够访问每一行和每一列的所有值。所以,可能在 free_result 中循环有一些问题。

主要

    int main(void) {
db_res_t *result = NULL;
char* json = "[{\"id\":11,\"username\":\"microsip\",\"domain\":\"192.168.254.128\",\"event\":\"presence\",\"etag\":\"a.1437194656.2922.1.0\",\"expires\":1437200355,\"received_time\":-1,\"body\":\"\",\"extra_hdrs\":\"\",\"sender\":\"\"},{\"id\":12,\"username\":\"microsip\",\"domain\":\"92.168.254.128\",\"event\":\"presence\",\"etag\":\"a.1437194656.2922.1.0\",\"expires\":1437200355,\"received_time\":-1,\"body\":\"\",\"extra_hdrs\":\"\",\"sender\":\"\"}]";
parse_json_to_result(json,&result);
free_result(result);
}

valgrind 日志:

HEAP SUMMARY:
in use at exit: 119 bytes in 6 blocks
total heap usage: 3,336 allocs, 3,330 frees, 195,040 bytes allocated

55 bytes in 4 blocks are definitely lost in loss record 3 of 3
at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
by 0x542C839: strdup (strdup.c:42)
by 0x4023D4: parse_json_to_result (Util.c:64)
by 0x4035CE: getResource (RepositoryHandler.c:116)
by 0x400FC5: test_util_get (Test.c:33)
by 0x400F7A: main (Test.c:25)

LEAK SUMMARY:
definitely lost: 55 bytes in 4 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 64 bytes in 2 blocks
suppressed: 0 bytes in 0 blocks
Reachable blocks (those to which a pointer was found) are not shown.
To see them, rerun with: --leak-check=full --show-leak-kinds=all

For counts of detected and suppressed errors, rerun with: -v
ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

抱歉发了这么长的帖子,但我已尽力提供所有信息。我坚持了很长时间,似乎没有找到问题。

编辑

对于外部库,我很抱歉,但它是一个单独的 c 文件,必须与 -lm 选项一起编译。链接https://github.com/kbranigan/cJSON

gcc -o Test.c cJSON.c -lm

最佳答案

这个错误非常微妙。在分配代码中,您有:

(*result)->rows->n = colCount;

在发布代码中,您使用:

col_count= _r->rows[i].n;

您需要将分配代码更改为:

(*result)->rows[i].n = colCount;

然后所有的内存都被正确地释放了。请注意,原文等同于:

(*result)->rows[0].n = colCount;

以便重复设置 rows[0].n 中的值,但 rows[1].n 中的值保留为零,如调用()


这是我最终得到的代码。它仍然有我用来帮助我缩小问题范围的调试打印语句。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "cJSON.h"

struct _str
{
char *s; /**< string as char array */
int len; /**< string length, not including null-termination */
};

typedef struct _str str;
typedef str *db_key_t;

typedef enum {
DB_INT, /**< represents an 32 bit integer number */
DB_BIGINT, /**< represents an 64 bit integer number */
DB_DOUBLE, /**< represents a floating point number */
DB_STRING, /**< represents a zero terminated const char* */
DB_STR, /**< represents a string of 'str' type */
DB_DATETIME, /**< represents date and time */
DB_BLOB, /**< represents a large binary object */
DB_BITMAP /**< an one-dimensional array of 32 flags */
} db_type_t;

typedef struct
{
db_type_t type; /**< Type of the value */
int nul; /**< Means that the column in database has no value */
int free; /**< Means that the value should be freed */
/** Column value structure that holds the actual data in a union. */
union
{
int int_val; /**< integer value */
long long bigint_val; /**< big integer value */
double double_val; /**< double value */
time_t time_val; /**< unix time_t value */
/*const*/ char *string_val; /**< zero terminated string */
str str_val; /**< str type string value */
str blob_val; /**< binary object data */
unsigned int bitmap_val; /**< Bitmap data type */
} val;
} db_val_t;
typedef struct db_row
{
db_val_t *values; /**< Columns in the row */
int n; /**< Number of columns in the row */
} db_row_t;

typedef struct db_res
{
struct
{
db_key_t *names; /**< Column names */
db_type_t *types; /**< Column types */
int n; /**< Number of columns */
} col;
struct db_row *rows; /**< Rows */
int n; /**< Number of rows in current fetch */
int res_rows; /**< Number of total rows in query */
int last_row; /**< Last row */
} db_res_t;

static
int parse_json_to_result(char *json, db_res_t **result)
{
printf("received json: %s\n", json);
cJSON *root, *record;
int recordCount = 0;
int colCount = 0;
int i = 0;
int j = 0;
int int_val = 0;
char *str_val = '\0';

root = cJSON_Parse(json);
recordCount = cJSON_GetArraySize(root);
*result = calloc(1, sizeof(db_res_t));
(*result)->n = recordCount;

// malloc for number of rows.
(*result)->rows = calloc(recordCount, sizeof(db_row_t) );

// this is done to get the count of columns only once.
record = cJSON_GetArrayItem(root, i);
colCount = cJSON_GetArraySize(record);
printf("Record count = %d\n", recordCount);
printf("colCount-1 = %d\n", colCount);

for (i = 0; i < recordCount; i++)
{
printf("Allocating record %d\n", i);
j = 0;
record = cJSON_GetArrayItem(root, i);
(*result)->rows[i].n = colCount;
printf("colCount-2 = %d\n", colCount);
// malloc for number of columns in each row.
(*result)->rows[i].values = calloc(colCount, sizeof(db_val_t) );
cJSON *subitem = record->child;
while (subitem)
{
if (subitem->type == cJSON_Number)
{
int_val =
cJSON_GetObjectItem(record, subitem->string)->valueint;
(*result)->rows[i].values[j].type = DB_INT;
(*result)->rows[i].values[j].nul = 0;
(*result)->rows[i].values[j++].val.int_val = int_val;
// printf("%d\n", int_val);
}
else
{
str_val =
cJSON_GetObjectItem(record, subitem->string)->valuestring;
// printf("%s\n", str_val);
(*result)->rows[i].values[j].type = DB_STRING;
if (strcmp(str_val, "") == 0)
{
(*result)->rows[i].values[j].nul = 1;
(*result)->rows[i].values[j].free = 0;
(*result)->rows[i].values[j++].val.string_val = NULL;
}
else
{
static int count = 0;
printf("Allocate %d: %s\n", ++count, str_val);
(*result)->rows[i].values[j].nul = 0;
(*result)->rows[i].values[j].free = 1;
(*result)->rows[i].values[j++].val.string_val = strdup(str_val);
}
}
subitem = subitem->next;
}
}
cJSON_Delete(root);
return 1;
}

static
int free_result(db_res_t *_r)
{
if (!_r)
{
return -1;
}
int i, row_count = 0;
int col_count = 0;
printf("freeing result set at %p\n", _r);
row_count = _r->n;
printf("RowCount %d .\n", row_count);
for (i = 0; i < row_count; i++)
{
printf("Freeing %d row.\n", i);
col_count = _r->rows[i].n;
printf("col_count %d.\n", col_count);
int j = 0;
for (j = 0; j < col_count; j++)
{
if (_r->rows[i].values[j].type == DB_STRING && _r->rows[i].values[j].nul == 0)
{
printf("Freeing-1 %d col [%s]\n", j, _r->rows[i].values[j].val.string_val);
free(_r->rows[i].values[j].val.string_val);
_r->rows[i].values[j].val.string_val = NULL;
}
else if (_r->rows[i].values[j].type == DB_STR && _r->rows[i].values[j].nul == 0)
{
printf("Freeing-2 %d col [%s]\n", j, _r->rows[i].values[j].val.string_val);
free(_r->rows[i].values[j].val.str_val.s);
_r->rows[i].values[j].val.str_val.s = NULL;
}
}
// free all value colums for each row.
free(_r->rows[i].values);
_r->rows[i].values = NULL;
}
// free all rows
free(_r->rows);
_r->rows = NULL;
// free resultset
free(_r);
_r = NULL;
// this will print nil.
printf("freed result set a %p\n", _r);
return 0;
}

int main(void)
{
db_res_t *result = NULL;
char json[] =
"[{\"id\":11,\"username\":\"microsip\",\"domain\":\"192.168.254.128\","
"\"event\":\"presence\",\"etag\":\"a.1437194656.2922.1.0\",\"expires\":1437200355,"
"\"received_time\":-1,\"body\":\"\",\"extra_hdrs\":\"\",\"sender\":\"\"},"
"{\"id\":12,\"username\":\"microsip\",\"domain\":\"92.168.254.128\",\"event\":\"presence\","
"\"etag\":\"a.1437194656.2922.1.0\",\"expires\":1437200355,\"received_time\":-1,"
"\"body\":\"\",\"extra_hdrs\":\"\",\"sender\":\"\"}]";

parse_json_to_result(json, &result);
free_result(result);
return 0;
}

代码的泄漏版本产生如下输出:

received json: [{"id":11,"username":"microsip","domain":"192.168.254.128","event":"presence","etag":"a.1437194656.2922.1.0","expires":1437200355,"received_time":-1,"body":"","extra_hdrs":"","sender":""},{"id":12,"username":"microsip","domain":"92.168.254.128","event":"presence","etag":"a.1437194656.2922.1.0","expires":1437200355,"received_time":-1,"body":"","extra_hdrs":"","sender":""}]
Record count = 2
colCount-1 = 10
Allocating record 0
colCount-2 = 10
Allocate 1: microsip
Allocate 2: 192.168.254.128
Allocate 3: presence
Allocate 4: a.1437194656.2922.1.0
Allocating record 1
colCount-2 = 10
Allocate 5: microsip
Allocate 6: 92.168.254.128
Allocate 7: presence
Allocate 8: a.1437194656.2922.1.0
freeing result set at 0x10082b0a0
RowCount 2 .
Freeing 0 row.
col_count 10.
Freeing-1 1 col [microsip]
Freeing-1 2 col [192.168.254.128]
Freeing-1 3 col [presence]
Freeing-1 4 col [a.1437194656.2922.1.0]
Freeing 1 row.
col_count 0.
freed result set a 0x0

固定版本产生的输出如下:

received json: [{"id":11,"username":"microsip","domain":"192.168.254.128","event":"presence","etag":"a.1437194656.2922.1.0","expires":1437200355,"received_time":-1,"body":"","extra_hdrs":"","sender":""},{"id":12,"username":"microsip","domain":"92.168.254.128","event":"presence","etag":"a.1437194656.2922.1.0","expires":1437200355,"received_time":-1,"body":"","extra_hdrs":"","sender":""}]
Record count = 2
colCount-1 = 10
Allocating record 0
colCount-2 = 10
Allocate 1: microsip
Allocate 2: 192.168.254.128
Allocate 3: presence
Allocate 4: a.1437194656.2922.1.0
Allocating record 1
colCount-2 = 10
Allocate 5: microsip
Allocate 6: 92.168.254.128
Allocate 7: presence
Allocate 8: a.1437194656.2922.1.0
freeing result set at 0x10082b0a0
RowCount 2 .
Freeing 0 row.
col_count 10.
Freeing-1 1 col [microsip]
Freeing-1 2 col [192.168.254.128]
Freeing-1 3 col [presence]
Freeing-1 4 col [a.1437194656.2922.1.0]
Freeing 1 row.
col_count 10.
Freeing-1 1 col [microsip]
Freeing-1 2 col [92.168.254.128]
Freeing-1 3 col [presence]
Freeing-1 4 col [a.1437194656.2922.1.0]
freed result set a 0x0

如您所见,第 2 行的 colCount 在错误输出中是错误的(0 而不是 10)。诀窍是从那开始倒退,找出为什么值被破坏,或者没有像原来那样设置。


顺便说一句,您应该小心使用以下划线开头的名称,如 _r。它们基本上保留供实现使用。 C 的规则在§7.1.3 保留标识符中定义:

  • All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.
  • All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

关于c - 无法从结构数组中释放内存,valgrind 显示内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31868661/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com