在sqlite的回调函数中动态重新分配2个dim数组

时间:2016-10-17 21:52:02

标签: c arrays sqlite multidimensional-array realloc

我正在研究sqlite-.dll用于教育目的。 我试图在每次使用数据库中的新行调用回调函数时动态地在我的2维数组中添加一行。 (例如SELECT * FROM CUSTOMER)。 然后,应将此数组中存储的数据作为C接口返回。

SQLCONTROL_API char** sql_execQuery(char *dbName, char *sqlStatement)
{
    char **a = 0;
    /*Some sqlite stuff*/
    int rc = sqlite3_exec(db, sqlStatement, callback, &a, &zErrMsg);
    return a;
}

使用回调函数:

static int callback(void *data, int argc, char **argv, char **azColName)
{
    char **old = (char **)data;
    int num_rows = sizeof(old) / sizeof(old[0]);
    int num_cols = sizeof(old[0]) / sizeof(old[0][0]);

    old = (char **)realloc(old, (num_rows + 1) * sizeof(char *));
    for (int i = 0; i < (num_rows + 1); i++)
        old[i] = (char *)realloc(old[i], argc * sizeof(char *));

    /*I am trying to create a 2 dim array that looks like a table, 
    so the column names are in the first row, 
    then the data from the table is stored in each row*/
    for (int i = 0; i < argc; i++)
    {
        if (num_rows == 1)
            old[0][i] = *azColName[i];

        old[num_rows][i] = *argv[i];
    }
    data = old;
    return 0;
}

将数据插入数据库时​​,一切正常。但是当我尝试检索数据时,我得到了读取访问权限。 现在我的问题是,我采用我的方法是正确的,还是我错过了一些重要的要求?

1 个答案:

答案 0 :(得分:0)

sql_execQuery()中,您将a声明为char **,并将其地址&a作为第四个参数传递给sqlite3_exec() char ***。因此,该参数具有类型callback(),并且它指向程序堆栈中的某个位置。这没有什么本质上的错误。

然后我们到了data,其中存在严重的问题,主要是:

  • 它将char **指针视为char ***类型,而不是正确的类型char **old = *(char ***)data; // ... *(char ***)data = old; 。如果这是你唯一的问题,你可以这样解决:
sizeof
  • 它试图通过old运算符计算分配空间的维数,如果char实际上是一个2D数组,那么这是合理的,但 它根本不是一个数组 。它是指向sizeof(old)的指针,因此char是指针的大小(指向sizeof(old[0])的指针),char是指针的大小({ {1}})和sizeof(old[0][0])char的大小。这并没有告诉您有关已分配多少空间的任何信息。

  • 在为old分配内存后,它会将已分配内存的各部分取消引用而不进行初始化,方法是将它们传递给realloc()。通常,除了其中一个之外的所有部分都已初始化,但未初始化的部分会导致realloc()表现出未定义的行为。

  • 您未能检查分配错误。

看起来您需要将更复杂的数据结构传递给回调,以便您可以跟踪分配的维度。像这样的东西,例如:

struct mytable {
    char **data;
    size_t dim;
};

SQLCONTROL_API char** sql_execQuery(char *dbName, char *sqlStatement)
{
    struct mytable a = { NULL, 0 };

    // ...

    int rc = sqlite3_exec(db, sqlStatement, callback, &a, &zErrMsg);

    return a.data;
}

static int callback(void *data, int argc, char **argv, char **azColName)
{
    struct mytable *old = data;
    char **temp;

    old->dim++;
    temp = realloc(old->data, old->dim * sizeof(*old->data));
    if (temp) {
        old->data = temp;
        old->data[old->dim - 1] = NULL;
    } else {
        // handle allocation error ...
    }

    for (int i = 0; i < old->dim; i++) {
        char *temp2 = realloc(old->data[i], argc * sizeof(*old->data[i]));

        if (temp2) {
            old->data[i] = temp2;
            old->data[i][argc - 1] = NULL;
        } else {
            // handle allocation error ...
        }
    }

    // ... other stuff ...

    // no need for anything like data = old

    return 0;
}