DQQpy DQQpy - 1 year ago 45
C Question

Storing 2D arrays in 3D array

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);

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++;

Answer Source

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.