C中动态分配的字符串矩阵

Dynamically allocated string matrix in C

我试图在 C 中创建一个字符串矩阵来存储 sql 回调的结果。由于某种原因,它总是在 "data" 的第 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;
}

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

首先,您的 db_select 函数 returns 分配了一个 data,但设置了一个全局变量 row_index。列数永远丢失。但这已经表明您需要一个 struct - 您想要将此函数需要给您的所有信息打包到一个 "coherent" 块中。

因此,您可能会说一行是一堆列:

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

而 table 是一堆行:

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 稍微简化,因为您不需要分别分配结构和内部数组。

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