【问题标题】:Dynamically allocated string matrix in CC中动态分配的字符串矩阵
【发布时间】:2018-01-29 21:33:05
【问题描述】:

我正在尝试在 C 中创建一个字符串矩阵来存储 sql 回调的结果。由于某种原因,即使数据的内存地址相同,它总是在“数据”的第 12 次重新分配时崩溃。 谢谢。

int row_index;

static int db_select_cb(void *p_data ,int argc, char **argv, char **azColName){
    char ***data = (char ***)p_data;
    data = (char ***)realloc(data,sizeof(char **)*(row_index+1));
    data[row_index] = (char **)malloc(sizeof(char *)*(argc));
    for(int col_index = 0;col_index < argc;col_index++){
        data[row_index][col_index] = (char *)malloc(sizeof(char)*(strlen(argv[col_index])+1));
        strcpy(data[row_index][col_index],argv[col_index]);
    }
    row_index++;
    return 0;
}

char ***db_select(sqlite3 *conn,unsigned char *zSQL){
    row_index = 0;
    char ***data = (char ***)malloc(sizeof(char ***)*(row_index+1));
    char *err = 0;
    int cerr = sqlite3_exec(conn,zSQL,db_select_cb,(void*)data,&err);
    if(cerr){
        printf(":: SQL ERROR IN \"db_select\" || %s ||\n", err);
        sqlite3_free(err);
        return 0;
    }
    return data;
}

感谢你们的帮助。问题是我需要将对矩阵的引用传递给回调,因为 realloc 正在修改数据。这就是最终的工作。

int row_index;

static int db_select_cb(void *p_data ,int argc, char **argv, char **azColName){
    char ****data = (char ****)p_data;
    *data = realloc(*data,sizeof(char **)*(row_index+1));
    (*data)[row_index] = malloc(sizeof(char *)*(argc));
    for(int col_index = 0;col_index < argc;col_index++){
        (*data)[row_index][col_index] = malloc(sizeof(char)*(strlen(argv[col_index])+1));
        strcpy((*data)[row_index][col_index],argv[col_index]);
    }
    row_index++;
    return 0;
}

char ***db_select(sqlite3 *conn,unsigned char *zSQL){
    row_index = 0;
    char ***data = malloc(sizeof(char **)*(row_index+1));
    char *err = 0;
    int cerr = sqlite3_exec(conn,zSQL,db_select_cb,(void*)&data,&err);
    if(cerr){
        printf(":: SQL ERROR IN \"db_select\" || %s ||\n", err);
        sqlite3_free(err);
        return 0;
    }
    return data;
}

这是一个使用结构的更新解决方案,正如 Groo 指出的那样,这是跟踪行和列大小的唯一方法。

typedef struct{
    char ***data;
    int row_size;
    int *col_size;
}Table;

static int db_select_cb(void *p_table ,int argc, char **argv, char **azColName){
    Table **table = (Table **)p_table;

    (*table)->data = realloc((*table)->data,sizeof(char **)*((*table)->row_size+1));
    (*table)->data[(*table)->row_size] = malloc(sizeof(char *)*(argc));
    (*table)->col_size = realloc((*table)->col_size,sizeof(int)*((*table)->row_size+1));

    int col_index;
    for(col_index = 0;col_index < argc;col_index++){
        (*table)->data[(*table)->row_size][col_index] = malloc(sizeof(char)*(strlen(argv[col_index])+1));
        strcpy((*table)->data[(*table)->row_size][col_index],argv[col_index]);
    }
    (*table)->col_size[(*table)->row_size] = col_index;
    (*table)->row_size++;
    return 0;
}

Table *db_select(sqlite3 *conn,unsigned char *zSQL){
    Table *table = malloc(sizeof(Table));
    table->row_size = 0;
    table->data = NULL;
    table->col_size = NULL;

    char *err = 0;
    int cerr = sqlite3_exec(conn,zSQL,db_select_cb,(void*)&table,&err);
    if(cerr){
        printf(":: SQL ERROR IN \"db_select\" || %s ||\n", err);
        sqlite3_free(err);
        return 0;
    }
    return table;
}

【问题讨论】:

  • 我不记得以前在一个问题中看到过这么多星星:(
  • 一个三星级程序员应该知道如何使用调试器。
  • XD 我知道如何使用 valgrind,但是 Windows 上的所有调试器都在付费墙后面,或者并没有真正告诉我太多。
  • @TheAschr 您的代码是一个巨大的多级 UB。我的建议 - 重新考虑再写一遍
  • I always thought those were discouraged in C你从哪里得到这些信息的?

标签: c arrays matrix dynamic allocation


【解决方案1】:

如果你能创建几个命名合理的结构和一些小的辅助函数,你的生活会容易得多。

首先,您的db_select 函数返回一个已分配的data,但设置了一个全局变量row_index。列数永远丢失。但这已经表明您需要一个struct - 您希望将此函数需要提供给您的所有信息打包到一个“连贯”的块中。

所以,你可能会说一行是一堆列:

typedef struct {
    char *cols;
    int cols_count;
} Row;

而一个表是一堆行:

typedef struct {
    Row * rows;
    int rows_count;
} Table;

现在你分别处理分配和内务处理(注意:我在浏览器中写这个,甚至没有检查它是否会编译):

// allocates a new table
Table * Table_create(void) {
    Table * table = calloc(1, sizeof *table);
    return table;
}

// creates a new child row in the table, with the specified number of cols
Row * Row_create(Table *table, int numCols) {

    table = realloc(table, table->rows_count * sizeof *table);
    table->rows_count++;

    Row * newRow = &table->rows[table->rows_count - 1];
    newRow->cols = calloc(numCols * sizeof *newRow->cols);
    newRow->cols_count = numCols;
    return newRow;
}

Sqlite 功能现在看起来非常简单:

// this obviously allocates a new table, so somebody will have to 
// free it at some point
Table * table_fetch_from_db(sqlite3 * conn, unsigned char * sql) {

    Table * table = Table_create();

    if (sqlite3_exec(conn, sql, load_single_row, table, NULL)) {
        // handle error
    }

    return table;
}

int load_single_row(void *args, int numCols, char **cols, char **colNames) {

    // we passed a Table* as args
    Table * table = (Table*)args;

    // allocate a new row inside table
    Row * row = Row_create(table, numCols);

    for (int i = 0; i < numCols; i++) {
        int single_col_len = strlen(cols[col_index]);
        row->cols[i] = malloc(single_col_len * sizeof *row->cols[i]);
        strcpy(row->cols[i], cols[i]);            
    }

    return 0;
}

如果您使用的是 C99,则使用 flexible array members 可能会稍微简化此代码,因为您不需要分别分配结构和内部数组。

请注意,我没有对此进行任何测试,它缺少释放表的功能,并且可能无法解决您的实际问题。 :)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 2018-04-24
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    • 1970-01-01
    • 2021-10-13
    相关资源
    最近更新 更多