Сколько звездочек следует использовать при объявлении указателя на массив C-строк? - PullRequest
1 голос
/ 22 мая 2009

У меня запрос приложения VB на список пользователей из DLL C:
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 ]

4 голосов
/ 22 мая 2009

В общем, непросто сделать то, что вы просите, потому что VB просто не понимает строки и массивы ASCIIZ в стиле C.

Если ваша DLL не ожидает VB SafeArray BSTR, у вас возникнут некоторые трудности с ее заполнением.

Было бы просто сделать так, чтобы VB передавал массив Long (C int) со ссылкой на первый элемент, и вы могли бы заполнить его указателями на отдельные строки. Сторона VB может скопировать их в строки VB. Но в таком случае, кто располагает строками C и когда?

Если вы создаете массив VB и заполняете его строками предварительно заданного размера, вам все равно придется иметь дело с SafeArray на стороне C, поскольку вы не можете передать один элемент массива VB по ссылке и ожидать найдите в памяти оставшиеся строки, смежные с ним.

Лучший, самый безопасный метод - создать в вашей DLL SafeArray из так называемого Ansi BSTR и объявить функцию в VB как возвращающую массив строк. Тогда вам не нужно два вызова, потому что границы массива расскажут всю историю.

===== edit =====

Когда VB передает строковый массив в объявленную функцию, он делает некоторое вуду за кулисами. Сначала он преобразует все строки из Unicode в ублюдочную форму, обычно известную как Ansi BSTR. Для C они выглядят и могут рассматриваться как ASCIIZ или LPSTR, за исключением того, что вы не можете создавать или удлинять их обычным способом C, вы можете только заполнить их. На стороне C переданный массив выглядит как ppSA (SAFEARRAY **). BSTR Ansi - это серия указателей, на которые ссылается член pData SafeArray.

Вы абсолютно не можете передать одну строку из массива (как char *) и ожидать, что остальные строки, смежные с ней, будут в памяти. Вы должны передать сам массив и управлять им, используя SafeArray API (или знание структуры SA).

Вот почему лучший вариант - делать все это прямо в DLL. Создайте массив, используя SafeArrayCreate, затем создайте BSTR Ansi, используя SysAllocStringByteLen, и поместите эти строки (которые являются BSTR, то есть 4-байтовым указателем) в слоты массива. По возвращении VB выполняет свое вуду и конвертирует строки в Unicode для вас.

В VB ваша функция будет объявлена ​​как возвращающая String ().

1 голос
/ 22 мая 2009

две звездочки - это путь.

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

Я запутался:)

0 голосов
/ 22 мая 2009

Итак, позвольте мне получить это прямо. Ваша функция заполняет массив строк из данных, содержащихся в связанном списке?

Если вы заранее знаете размер списка, вы можете просто передать символ **, но если вы не знаете его размера и вам необходимо увеличить список, вам понадобится символ ***.

Посмотрев на свой код, вы, похоже, уже знаете длину, поэтому вам просто нужно выделить массив правильной длины перед вызовом функции. Вот пример:

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 не могут быть изменены.

...