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

标签 c arrays matrix dynamic allocation

我正在尝试用 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;
}

最佳答案

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

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

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

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

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

关于c - C 中动态分配的字符串矩阵,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45803368/

相关文章:

java - 将顶点着色器计算转换/移植到 Java

c++ - 类模板 cpp、hpp、cpp

c - 匿名结构 + 复合文字导致 lint 错误

c - 关于 C 列表的练习

c++ - 理解这个递归函数

javascript - 状态数组不断重新排序

java - 不知道如何将字符串添加到数组列表并使其粘住

Python 矩阵表示为 (40000,)

c++ - 寻找格式HTTP响应的c/c++库

javascript - 通过 Javascript 中的任何字段提取条目