c - valgrind 检测到内存泄漏但应用程序工作

标签 c linux sqlite valgrind

由于这个函数,我写了一个能够读取 sqlite 数据库的应用程序:

struct query_res excucute_sql_statement(char *database, char *zSQL){
    sqlite3 *conn;
    sqlite3_stmt    *res;
    const char      *tail, *buf, *zErrMsg;
    struct query_res q_res;
    char table[MAXSTMTNUM][MAXCOLNUM][MAXSTRINGLEN];
    q_res.table = table;
    q_res.num = 0;
    int maxtry = 5, try = 0;

    while (sqlite3_open(database, &conn)) {
        if (try > maxtry)
            break;
        printf("Can not open database \'%s\'. %s\n", database, sqlite3_errmsg(conn));
        usleep(50000);
        try ++;
    }

    if (sqlite3_exec(conn, zSQL, callback, &q_res, &zErrMsg)){
        printf("Excecuting %s\n", zSQL);
        printf("We did not get any data! error %s\n",zErrMsg);
        if(sqlite3_finalize(conn))
            printf("Can not finalize database. %s\n", sqlite3_errmsg(conn));
        if(sqlite3_close(conn))
            printf("Can not close database. %s\n", sqlite3_errmsg(conn));
        return q_res;
    }

    sqlite3_free(zSQL);

    if(sqlite3_close(conn))
        printf("Can not close database. %s\n", sqlite3_errmsg(conn));

    return q_res;
}

对于返回的每一行,调用函数回调:

static int callback(void *buf, int argc, char **argv, char **azColName){
    int i;
    struct query_res *q_res;
    q_res = (struct query_res *)buf;
    if (q_res->num >= MAXSTMTNUM)
        return 0;

    q_res->table[q_res->num] = calloc(argc, sizeof(char *));

    for(i=0; i<argc; i++){
        if (i >= MAXCOLNUM)
            break;  
        q_res->table[q_res->num][i] = calloc(((strlen(argv[i]) < MAXSTRINGLEN) ? strlen(argv[i]) : MAXSTRINGLEN), sizeof(char));
        strncpy(q_res->table[q_res->num][i], argv[i], ((strlen(argv[i]) < MAXSTRINGLEN) ? strlen(argv[i]) : MAXSTRINGLEN));
    }
    q_res->num ++;

    return 0;
}

以下是调用 excucute_sql_statement 的代码摘录:

struct query_res    res;
res = excucute_sql_statement(database, zSQL);


directions = malloc(sizeof (struct direction_list));
directions->directions = calloc(5,  sizeof (struct direction));

double cur_dist, min_dist = 30;
float s_lat, s_lon, e_lat, e_lon;
directions->direction_num = 0;

//printf("Res table num %d\n", res.num);

//printf("First elem %s\n", res.table[0][0]);

for (i = 0 ; i < res.num ; i++){
    //printf("%d. %s|%s|%s|%s|%s|%s\n", i, res.table[i][0], res.table[i][1], res.table[i][2], res.table[i][3], res.table[i][4], res.table[i][5]);
    sscanf(res.table[i][1], "%g", &s_lat);
    sscanf(res.table[i][2], "%g", &s_lon);
    sscanf(res.table[i][3], "%g", &e_lat);
    sscanf(res.table[i][4], "%g", &e_lon);
    sscanf(res.table[i][0], "%d", &rs);
    sscanf(res.table[i][5], "%d", &rp);
    //printf("New seg start: %g,%g end %g,%g rs %d rp %d\n", s_lat, s_lon, e_lat, e_lon, rs, rp);
    cur_dist = (gps_distance(location.lat, location.lon, s_lat, s_lon)
        + gps_distance(location.lat, location.lon, e_lat, e_lon)) / 2;
    //printf("Current direction num %d \n", directions->direction_num);
    //printf("cur_dist %f\n", cur_dist);
    if (cur_dist < min_dist){   
        directions->directions[0] = fill_direction(rs, rp, database);
        directions->direction_num = 1;
        min_dist = cur_dist;
    }
    else if (cur_dist == min_dist){
        directions->directions[directions->direction_num] = fill_direction(rs, rp, database);
        directions->direction_num ++;
    }
}

这些函数工作正常并给出了预期的结果,但是在运行 valgrind 时,我得到以下输出:

==22808== Thread 2:
==22808== Invalid read of size 4
==22808==    at 0x804946B: get_all_possible_directions (util.c:240)
==22808==    by 0x8049D73: start_direction_detection (direction_detection.c:293)
==22808==    by 0x40C41C88: ???
==22808==  Address 0x4f03690 is not stack'd, malloc'd or (recently) free'd
==22808== 
==22808== Invalid read of size 1
==22808==    at 0x402F5C3: __GI___rawmemchr (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==22808==    by 0x40C246E: _IO_str_init_static_internal (strops.c:44)
==22808==    by 0x8049D73: start_direction_detection (direction_detection.c:293)
==22808==    by 0x40C41C88: ???
==22808==  Address 0x45aa01b is 0 bytes after a block of size 11 alloc'd
==22808==    at 0x402B965: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==22808==    by 0x8048F59: callback (util.c:57)
==22808== 
==22808== Invalid read of size 4
==22808==    at 0x8049487: get_all_possible_directions (util.c:241)
==22808==    by 0x8049D73: start_direction_detection (direction_detection.c:293)
==22808==    by 0x40C41C88: ???
==22808==  Address 0x4f03690 is not stack'd, malloc'd or (recently) free'd

等等……

请注意,第 240 行对应于第一个 scanf 语句。

我认为我的表初始化有问题。也许在这里:

q_res->table[q_res->num] = calloc(argc, sizeof(char *));

您知道为什么 valgrind 会触发此错误吗?

谢谢


[根据评论更新:]

struct query_reschar ***tableint num组成。

最佳答案

excucute_sql_statement()中,这里

q_res.table = table;

您正在将对堆栈本地存储()的引用复制到函数返回的结构中。

一旦函数返回,堆栈本地存储就变得无效,因此结构的成员 table 在函数返回后引用无效(未分配)的内存。

要修复此修改 excucute_sql_statement()

struct query_res excucute_sql_statement(char *database, char *zSQL)
{
  [...]

  /* char table[MAXSTMTNUM][MAXCOLNUM][MAXSTRINGLEN]; */ /* Delete this line. */
  q_res.table = NULL;
  q_res.num = 0;

回调()

static int callback(void *buf, int argc, char **argv, char **azColName)
{
  size_t i;
  struct query_res * q_res = (struct query_res *) buf;

  /* Resize statement table, adding one new entry. */
  q_res->table = realloc(q_res->table, (q_res->num + 1) * sizeof(*q_res->table));

  /* Allocate new argument table. */
  /* (Allocate +1 for a stopper element which stays NULL to be able to detect the end of the table.) */
  q_res->table[q_res->num] = calloc(argc + 1, sizeof(*q_res->table[q_res->num]));

  for(i=0; i<argc; ++i)
  {
    /* Allocate entry for argument, that is characters for argument. */
    q_res->table[q_res->num][i] = malloc(strlen(argv[i]) + 1);
    /* Copy argument. */
    strcpy(q_res->table[q_res->num][i], argv[i]);
  }

  q_res->num++;

  return 0;
}

(未经测试)

另请注意,整个代码(您的和我的)都缺少正确的错误检查。在这里,特别是对 malooc/calloc/realloc 的分配调用的重新调整值应针对 NULL 进行测试!

关于c - valgrind 检测到内存泄漏但应用程序工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21822832/

相关文章:

c - 在linux中创建socket和FD_SET后select总是返回1

node.js - 这个在 linux 中将环境变量传递给 node 的例子是如何工作的?

java - Android -- SQLite + SharedPreferences,2 个线程同时读/写?

linux - 在内核模块中创建指向文件的符号链接(symbolic link)

c++ - SCons:如何让库获取 CPPPATH 选项

数据库模式更新

java - SQLite 中的 Select 语句不返回结果

c++ - CLOCK_MONOTONIC 和 pthread_mutex_timedlock/pthread_cond_timedwait

仅使用奇数除数计算素数速度较慢

c - 套接字编程-setsockopt : Protocol not available?