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

时间:2017-08-21 18:17:10

标签: 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;
}

1 个答案:

答案 0 :(得分:0)

如果您要创建一些明确命名的结构和一些小辅助函数,您的生活会更容易。

首先,您的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可能会略微简化这段代码,因为你不需要分别分配struct和内部数组。

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