How many asterisks should I use when declaring a pointer to an array of C strings?
I have a VB application request for a list of users from a C DLL:
VB will ask the DLL how many users there are, and then initializes the array to the appropriate size.
VB then passes its array by reference to the DLL function, which fills it with usernames.
I started writing a C function like this: foo(char **bar);
which would be treated as an array of strings. But then I realized: I'm going to make each element in the array a point on another C-string ( char *username
in a struct userlist
linked list), not change the data already pointed to. The array of arrays is passed by value: a copy of the address list, so the addresses point to the original data, but changing the addresses in that copy doesn't change the caller's address list (I think anyway). So should you declare it foo(char ***bar);
? It will be a pointer to an array of strings, so if I change the strings the array points to, it changes the array of strings that the caller (VB) is using .... right?
This is my use so far (haven't tested it yet ... I'm still just coding a DLL, but there is still no VB interface)
EXPORT void __stdcall update_userlist(char ***ulist){
int i = 0;
userlist *cur_user = userlist_head; //pointer to first item in linked list
for(; i < usercount_; ++i){
*ulist[i] = cur_user->username;
cur_user = cur_user->next;
}
}
a source to share
In general, it is not easy to do what you ask, because VB just doesn't understand ASCIIZ strings and C-style arrays.
If your DLL doesn't expect a VB SafeArray from BSTR, you will have a hard time filling it in.
It would be simple to have a VB pass in a Long (C int) array by reference to the first element, and you can fill it with pointers to individual lines. The VB side could copy them to VB strings. But in this case, who owns the C strings, and when?
If you create a VB array and fill it with predefined strings, you still have to deal with a SafeArray on the C side, because you cannot pass a single element of a VB array by reference and expect to find the remaining strings adjacent to it in memory.
The best, safest method is to create your "Ansi BSTR" SafeArray DLL and declare the function in VB to return an array of strings. Then you don't need two calls, because the array bounds will tell the whole story.
===== edit =====
When VB passes the string array to the Declared function, it does some voodoo behind the scenes. It will first convert all strings from Unicode to bastard form commonly known as "Ansi BSTR". In C they look the same as ASCIIZ or LPSTR, except that you cannot create or lengthen them in the usual C way, you can only fill them. On the C side, the passed array looks like ppSA (SAFEARRAY **). Ansi BSTRs are a series of pointers referenced by the pData SafeArray member.
You absolutely cannot pass one string from an array (like char *) and expect to find the rest of the strings adjacent to it in memory. You must pass the array and manipulate it using the SafeArray API (or knowledge of the SA structure).
This is why the best option in general is to do it all directly in the DLL. Create an array with SafeArrayCreate, then create an Ansi BSTR with SysAllocStringByteLen and put these strings (which are BSTR, so a 4 byte pointer) in the slots of the array. On return, VB does its voodoo and converts strings to Unicode for you.
In VB, your function will be declared as returning String ().
a source to share
So let me get this straight. Is your function populating an array of strings from the data contained in the linked list?
If you know the size of the list ahead of time, you can just pass char **, but if you don't know the size and need to be able to grow the list, you need char * **.
From looking at your code, you seem to already know the length, so you just need to allocate an array of the correct length before you call the function. Here's an example:
void update_userlist(char **ulist)
{
int i = 0;
userlist *cur_user = userlist_head;
for(; i < usercount_; ++i)
{
ulist[i] = cur_user->username; // I am assuming that username is a char *
cur_user = cur_user->next;
}
}
// This sets up the array and calls the function.
char **mylist = malloc(sizeof(char*) * usercount_);
update_userlist(mylist);
Update: here is the difference between the different levels of pointers:
-
void func1 (char * data)
This passes a copy of the pointer to string C. If you change the pointer to a different string, the calling function will still point to the original string. -
void func2 (char ** data)
This passes a copy of the pointer to an array of pointers to C strings. You can replace the pointer with any string in the array and the caller array will be modified because it didn't make a copy of the array, it only points to the caller array ... -
void func3 (char *** data)
This passes a pointer to a pointer to an array of pointers to C strings. With this, you can completely replace the entire array. You only need this level of indirection if you need to grow an array, since C arrays cannot be modified.
a source to share