在声明指向C字符串数组的指针时,我应该使用多少个星号?

时间:2009-05-22 00:16:38

标签: c dll vb6 pointers reference

我有一个VB应用程序请求来自C DLL的用户列表:
VB将询问DLL有多少用户,然后将数组初始化为适当的大小 然后,VB将通过引用DLL函数传递其数组,该函数将用用户名填充它。

我开始编写像这样的C函数:foo(char **bar);,它将被视为一个字符串数组。但后来我意识到,我将使数组中的每个项指向不同的C字符串(char *username链表中的struct userlist),而不是修改已经指向的数据。数组数组按值传递:地址列表的副本,因此地址指向原始数据,但修改该副本中的地址不会更改调用者的地址列表(我想,无论如何)。那么,我应该宣布它foo(char ***bar);吗?这将是一个指向字符串数组的指针,因此,如果我更改数组所指向的字符串,它将修改调用者(VB)正在使用的字符串数组....对吗?

这是我到目前为止的用法(还没有测试过......我还是刚刚编写DLL的代码,到目前为止还没有VB前端调用它)

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

}

3 个答案:

答案 0 :(得分:4)

一般来说,做你要问的事情并不简单,因为VB只是不理解C风格的ASCIIZ字符串和数组。

如果您的DLL不期望BSTR的VB SafeArray,那么填充它会有一些困难。

通过引用第一个元素让VB传入一个Long(C int)数组会很简单,你可以用指向各个字符串的指针填充它。 VB端可以将它们复制到VB字符串。但在那种情况下,谁处理了C字符串,何时处理?

如果你创建VB数组并用预先调整大小的字符串填充它,你仍然需要处理C端的SafeArray,因为你不能通过引用传递单个VB字符串数组元素并期望在内存中找到与其相邻的剩余字符串。

最好,最安全的方法是让你的DLL创建一个所谓的'Ansi BSTR'的SafeArray,并在VB中声明该函数返回一个字符串数组。那么你不需要两个调用,因为数组边界将告诉整个故事。

===== edit =====

当VB将字符串数组传递给Declared函数时,它会在幕后执行一些伏都教。它首先将所有字符串从Unicode转换为通常称为“Ansi BSTR”的混蛋形式。对于C,这些看似并且可以被视为ASCIIZ或LPSTR,除了你不能以正常的C方式创建或延长它们,你只能填充它们。在C方面,传递的数组看起来像ppSA(SAFEARRAY **)。 Ansi BSTR是由SafeArray的pData成员引用的一系列指针。

你绝对不能从数组中传递一个字符串(作为char *),并期望在内存中找到与其连续的其余字符串。您必须传递数组本身并使用SafeArray API(或SA结构的知识)对其进行操作。

这就是为什么总体上最好的选择是直接在DLL中完成所有这些操作。使用SafeArrayCreate创建数组,然后使用SysAllocStringByteLen创建Ansi BSTR并将这些字符串(BSTR,这是一个4字节指针)放入数组插槽中。返回时,VB会执行其voodoo并将字符串转换为Unicode。

在VB中,您的函数将被声明为返回String()。

答案 1 :(得分:1)

两个星号是要走的路。

char*   // is a pointer to a char
char**  // is a pointer to a char pointer
char*** // is a pointer to a pointer to a char pointer - e.g. multi-dimensional array (err...)

我迷惑了自己:)

答案 2 :(得分:0)

所以让我直截了当。您的函数是否填充了链表中包含的数据的字符串数组?

如果你事先知道列表的大小,你可以传递一个char **,但是如果你不知道它的大小并且需要能够增加列表,你将需要一个char ***。 / p>

通过查看代码,您似乎已经知道了长度,因此您只需要在调用函数之前分配一个正确长度的数组。这是一个例子:

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

更新:以下是各种指针级别之间的差异:

  1. void func1(char * data)
    这会将指针的副本传递给C字符串。如果将指针更改为指向其他字符串,则调用函数仍将指向原始字符串。

  2. void func2(char ** data)
    这会将指针的副本传递给指向C字符串的指针数组。您可以将指针替换为数组中的任何字符串,并且调用函数的数组将被更改,因为它没有创建数组的副本,它只指向调用者的数组。

  3. void func3(char *** data)
    这会将指针传递给指向C字符串指针数组的指针。有了这个,您就可以完全替换整个阵列。如果需要增长数组,则只需要这种级别的间接,因为C数组无法重新调整大小。