0

I'm currently trying to write a callback function for a database request. The function is called for every result/database entry/row, and I want to store its 2D array (array of strings/char *) in an array. My current solution looks like this:

Global declaration:

char ***entries;

Allocating memory for the first dimension in a setup function:

entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(char **)*200);

I'm working with the Windows API/Win32, basically it's malloc with zeroed memory and the last parameter referring to the size.

Initialising callback count and registering callback function for database execute:

cbCount = 0;
rc = sqlite3_exec(db, sql, insertListEntries, 0, &zErrMsg);

Callback function:

static int insertListEntries(void *NotUsed, int argc, char **argv, char **azColName) {
    entries[cbCount] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(argv)*argc); 
    memcpy(entries[cbCount], argv, sizeof(argv)*argc);
    ...
    cbCount++;
}

Parameters: argc is the size of argv/columns of the data, and argv the string array with the row data. I'm simply copying the memory data of argv into the 3D array.

However, the problem is now that for each callback all previous data sets are overwritten with the current result like this:

Callback 1: entries[0] = dataset1

Callback 2: entries[0] = dataset2, entries[1] = dataset2

...

Any ideas? I'm still trying to learn this concept of the "pointer/array-dualism" and memory allocation, but the best way for me is by doing it practically. Also, I DID study the theory beforehand alot, but I may have missed something.

Edit: added cbCount++;

13
  • 1
    Try checking strings in argv, do they actually get re-used? If they do, then you can't just copy argv, you need to copy each string. Commented Aug 17, 2016 at 11:17
  • Beware: you speak of 2D/3D arrays, but your code if for arrays of pointers which are different animals... Commented Aug 17, 2016 at 12:24
  • @hyde what do you mean with "get re-used"? I printed out the entries array afterwards and all data was where it should be, also the argv array always contained the current dataset for each callback. Commented Aug 17, 2016 at 12:36
  • 1
    I mean, you have argv array of pointers to strings (char buffers). You stored the pointers to these buffers to entries[0]. If they get re-used and their contents changed, then it looks like entries[0] changed (the pointers didn't change, but the data at the char buffers they point to did). Commented Aug 17, 2016 at 12:43
  • 1
    Use something like printf("entries[%d][0]=%p", cbCount, entries[cbCount][0]) after your memcpy to see the pointer values nicely. Commented Aug 17, 2016 at 12:46

1 Answer 1

1

Converting comments to an answer:

It looks like sqlite re-uses buffers, and actually passes same pointers in the argv vector for different calls.

So when your insertListEntries is called first time (cbCount 0), parameter argv might contain pointer values { 0x1111100, 0x1111200, 0x1111300, ... }, which you copy to your entries[0] array.

Then, when insertListEntries is called second time (cbCount 1), the pointer values in argv are at least partially or sometimes the same! This means, the buffers allocated for result data are re-used, their contents changed. Because you copied the pointers to entries[0], and now copy the possibly same pointers to entries[1], they will point to same strings, which get overwritten for every call.

Solution is to copy the actual strings, the actual data, instead of just copying pointers to the library's internal buffers.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.