Возвращение массива динамически размещенного wchar_t * из функции - PullRequest
1 голос
/ 18 марта 2011

У меня есть функция, подпись которой выглядит следующим образом:

GetCustomers( wchar_t** Name,int *count);

В основном методе: звонок клиенту выглядит так:

GetCustomers( Name,&count);

Основная функция функции выглядит следующим образом: (так как количество клиентов неизвестно, я пытаюсь динамически распределить меомерию)

GetCustomers( wchar_t** Name,int *count)
{
    //Logic to get customer count : Stored in int  myCustomersCount
    Names = new wchar_t*[myCustomersCount];

    for (int i=0; i < myCustomersCount; i++ )     
    {
        Names[i] = new wchar_t;
    }

    //Logic to get customer names in  wchar_t* strName = "Name1";
    Names[0] = strName;
    *count = myCustomersCount;
}

Я думаю, что эта реализация позволит правильно возвращать имя массива в функцию Main () с выделением памяти в куче, но, похоже, это не сработает. Что здесь не так? myCustomersCount, кажется, правильно в вызывающей стороне.

PS: код компилируется и выполняется, но массив, полученный в Main, является мусором.

Ответы [ 5 ]

3 голосов
/ 18 марта 2011

Вы, кажется, думаете с точки зрения C, а не C ++.Я бы использовал что-то вроде:

std::vector<std::string> GetCustomers();

или (вероятно, предпочтительнее):

template <class outIt>
void GetCustomers(outIt output_iterator);

В последнем случае вы бы использовали что-то вроде:

std::vector<std::wstring> customers;

GetCustomers(std::back_inserter(customers));

Третья очевидная возможность состоит в том, чтобы просто снабдить ваш класс customers функциями-членами begin() и end(), которые возвращают итераторы для данных клиентов.

Edit2: Вот несколько протестированных демонстрационных кодов:

#include <stdio.h>
#include <string.h>
#include <wchar.h>

void GetCustomers(wchar_t ***names, int *count) { 
    static wchar_t *myCustomers[] = { 
        L"You",
        L"Him",
        L"Her"
    };
    int myCustomersCount = 3;
    wchar_t **temp = new wchar_t *[myCustomersCount];

    *count = myCustomersCount;
    for (int i=0; i<myCustomersCount; i++) {
        temp[i] = new wchar_t[wcslen(myCustomers[i])+1];
        wcscpy(temp[i], myCustomers[i]);
    }
    *names = temp;
}

int main() { 
    wchar_t **customers;
    int count;

    GetCustomers(&customers, &count);

    for (int i=0; i<count; i++)
        printf("%S\n", customers[i]);
    return 0;
}
1 голос
/ 18 марта 2011

Я действительно не уверен, что вы пытаетесь сделать здесь;из того, что я понимаю о вашем коде;Вы пытаетесь сохранить некоторые строки в массиве массивов символьных указателей.

GetCustomers(wchar_t **Name, int *count) {
   Name = new wchar_t*[myCustomersCount];
   for(int i = 0; i < myCustomersCount; i++) {
     /* Get your customer name and store into strName */
     Name[i] = strName;
   }
   *count = myCustomersCount;
}
0 голосов
/ 18 марта 2011

Я думал, что начну все сначала с нового ответа.

Вот простая программа, которая делает то, что, я думаю, вы пытаетесь сделать, с ограничением, что подпись GetCustomers не должнаизменено.

void GetCustomers(wchar_t** Names,int *count)
{
    // Allocate the array of names
    wchar_t **ar = new wchar_t*[3];
    // Allocate space for each name in the array
    ar[0] = new wchar_t[10];
    ar[1] = new wchar_t[10];
    ar[2] = new wchar_t[10];
    // Fill in the names
    wcscpy(ar[0],L"joe");
    wcscpy(ar[1],L"jim");
    wcscpy(ar[2],L"bob");
    // Return the array through the bad GetCustomers signature
    *Names = (wchar_t*)ar;
    *count = 3;
}

int wmain(int argc, wchar_t* argv[])
{
    // names is an array of strings
    wchar_t **names = NULL;
    int count;
    // Squeeze names into the dodgy GetCustomers signature
    GetCustomers((wchar_t**)&names,&count);

    // Delete each name
    for(size_t x = 0; x < count; ++x)
        delete[] names[x];
    // Delete the array
    delete[] names;

    return 0;
}

Обратите внимание, что я сопоставил приведение внутри функции с другим в main.Таким образом, мы сохраняем все как положено, кроме этой надоедливой подписи GetCustomers.

Помогает ли это?

0 голосов
/ 18 марта 2011

2 определенных проблемы и 1 потенциальная проблема с вашим кодом. Основная проблема, вызывающая вашу проблему в первую очередь: Name само передается по значению. Это означает, что когда вы присваиваете ему в самой первой строке своей функции, когда вы new память, вы назначаете копию, а не оригинал! У вас есть три варианта: 1) сохранить двойной указатель, сделать так, чтобы вызывающая сторона отвечала за выделение памяти, и добавить третий параметр для количества имен, которые может содержать массив (рекомендуется), или 2) сделать Name тройным указателем (wchar_t*** Name ) и затем вы можете присвоить ему, разыменовав его: *Name = new wchar_t*[myCustomersCount]; или 3) просто вернуть wchar_t**, так как вы не используете переданное значение ни для чего.

Еще одна определенная проблема: когда вы выделяете память для каждого имени, вам также нужно использовать оператор new[], потому что в противном случае вы выделяете место только для одного wchar_t.

Наконец, потенциальная проблема. Вы не показываете, как именно этот код получает имя каждого клиента. Но если strName указывает на память, которая повторно используется для каждого имени клиента, когда вы помещаете их все в свой массив, вам придется wstrcpy каждое имя в массиве. Если этого не произойдет, вам не нужно выделять память для каждого Names[i], поскольку вы можете просто сохранить результат прямо в Names[i].

Последнее замечание: просто глядя на этот код, вы, похоже, столкнетесь с множеством проблем с управлением памятью, так как кажется, очень неясно, кто отвечает за выделение и освобождение памяти, которая, вероятно, собирается привести к утечкам памяти. Старайтесь изо всех сил держать ответственность за выделение и освобождение памяти в одном и том же месте, и вы уменьшите множество потенциальных головных болей - пусть вызывающие абоненты выделяют память перед вызовом функции, а вызывающий - освобождает память после завершения работы с этим.

/* changed */
wchar_t** GetCustomers( int *count)
{
    //Logic to get customer count : Stored in int  myCustomersCount
    wchar_t **Names = new wchar_t*[myCustomersCount];

    for (int i=0; i < myCustomersCount; i++ )     
    {
                              /* changed */
        Names[i] = new wchar_t[MAX_NAME_SIZE];
    }

    //Logic to get customer names in  wchar_t* strName = "Name1";
    Names[0] = strName; /* possible wstrcpy needed here? */
    *count = myCustomersCount;

    /* changed */
    return Names;
}

Редактировать Если вы действительно абсолютно не можете изменить сигнатуру функции, единственное решение, которое я могу придумать, - это сгладить массив и использовать функции памяти C (вы также можете просто использовать длинные серии из new s и delete s, но почему бы не использовать realloc, если это то, для чего оно предназначено, и вы управляете памятью, не используя другие функции C ++, такие как STL, в любом случае?):

GetCustomers( wchar_t **Names, int *count)
{
    //Logic to get customer count : Stored in int  myCustomersCount
    size_t names_size = 0;

    for (int i=0; i < myCustomersCount; i++ )     
    {
        strName = ???; // whatever you use to get the next name

        size_t old_names_size = names_size;
        names_size += (wstrlen(strName) + 1) * sizeof(wchar_t); //+1 for NULL
        *Names = realloc(*Names, names_size);

        if (!*Names) {
            // Memory allocation failed, log it, abort, do whatever
        }

        wstrcpy(Names[old_names_size], strName);
    }
    *count = myCustomersCount;
}

Обратите внимание, что это предполагает, что Name уже инициализирован и указывает на память, где вы можете хранить wchar_t*, так же, как исходная предполагаемая версия count была инициализирована, и указывает на память, где вы можете хранить int.

0 голосов
/ 18 марта 2011

В основном, по-видимому, у вас есть что-то вроде этого

wchar_t *Name = NULL;

, а затем вы говорите

GetCustomers( Name,&count);

Это передает имя по значению, но вы хотите передать его по ссылке:

GetCustomers( &Name,&count);

И, вероятно, это просто опечатка, но имя вашего параметра - Имя (единственное число), но вы упоминаете его как Имена (множественное число) в функции:

GetCustomers( wchar_t** Name,int *count)
{
    //Logic to get customer count : Stored in int  myCustomersCount
    Names = new wchar_t*[myCustomersCount];

Вв любом случае вы хотите назначить имя, на которое указывает Имя, а не на себя:

*Names = new wchar_t*[myCustomersCount];

Затем для каждого элемента в Именах вы выделяете один символ, а затем перезаписываете первый с помощью strName.Выделение не является необходимым (и фактически является утечкой памяти), и вы должны назначить из strName каждому элементу в цикле, как это делает ответ Suroot.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...