问题
I am working on a sqlite-.dll for educational purpose. I am trying to dynamically add a row in my 2 dimensional array for each time the callback function is called with a new row from the database. (e.g. SELECT * FROM CUSTOMER). The data stored in this array should then be returned as a C-Interface.
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;
}
With the callback function:
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;
}
When inserting data to the database, everything works fine. But when I try to retrieve data, I get read access violation. Now my question, am I on the right way with my approach or do I miss some important requirements for my intention?
回答1:
In your sql_execQuery(), you declare a as a char **, and you pass its address, &a, as the fourth argument of sqlite3_exec(). That argument therefore has type char ***, and it points to a location somewhere in the program's stack. There's nothing inherently wrong with that.
But then we get to callback(), which has serious problems, principal among them:
- It treats the
datapointer as if it were of typechar **, instead of the correct type,char ***. If that were your only problem, you could fix it like this:
char **old = *(char ***)data;
// ...
*(char ***)data = old;
It tries to compute the dimensions of the allocated space via the
sizeofoperator, as would be reasonable ifoldwere, in fact, a 2D array, but it is not an array at all. It is a pointer to pointer tochar, sosizeof(old)is the size of a pointer (to pointer tochar),sizeof(old[0])is the size of a pointer (tochar) andsizeof(old[0][0])is the size of achar. This does not tell you anything about how much space has been allocated.After allocating memory for
old, it dereferences the parts of the allocated memory without initializing them, by passing them torealloc(). Generally, all but one of these will have been initialized, but the one uninitialized one causesrealloc()to exhibit undefined behavior.You fail to check for allocation errors.
It looks like you need a more complex data structure to be passed through to your callback, so that you can track the allocated dimensions. Something like this, for example:
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;
}
来源:https://stackoverflow.com/questions/40096266/dynamically-realloc-2-dim-array-in-callback-function-of-sqlite