简体   繁体   中英

Returning array of dynamically allocated wchar_t* from a function

I have a function whose signature is as follows:

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

In main method: Call to customer looks like this:

GetCustomers( Name,&count);

The body of the function is as follows: (since count of customers is unknown , I am trying to allocate meomry dynamically)

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

I would think that this implementation would allow array Name to be passed back correctly to the Main() function with memory allocation on heap but it seems not to work. What is wrong here? myCustomersCount seems to be correct in caller.

PS: The code compile and executes but array received in Main is garbage.

You seem to be thinking in terms of C, not really C++. I'd use something like:

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

or (probably preferred):

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

The latter you'd use something like:

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

GetCustomers(std::back_inserter(customers));

The third obvious possibility would be to just equip your customers class with a begin() and end() member functions that return iterators to the customers data.

Edit2: Here's some tested demo code:

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

I'm really not sure what you're trying to do here; from what I understand about your code; you're trying to store some strings into an array of an array of character pointers.

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

In main, presumably you've got something like this

wchar_t *Name = NULL;

and then you say

GetCustomers( Name,&count);

This passes Name by value, but you want to pass it by reference:

GetCustomers( &Name,&count);

And presumably it's just a typo, but your parameter name is Name (singular) but you refer to it as Names (plural) in the function:

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

In any case, you want to assign to where Name is pointing, not to itself:

*Names = new wchar_t*[myCustomersCount];

Then for each element in Names you allocate one character, but then overwrite the first one with strName. The allocation is unnecessary (and in fact is a memory leak), and you should assign from strName to each element within the loop, as Suroot's answer does.

2 definite issue and 1 potential issue with your code. The main issue causing your problem first: Name itself is passed by value. That means when you you assign to it in the very first line of your function when you new the memory, you're assigning to the copy, not the original! You have three options: 1) keep the double pointer, make the caller responsible for allocating the memory, and add a third parameter for number of names that the array can hold (recommended) or 2) make Name a triple pointer ( wchar_t*** Name ) and then you can assign to it by dereferencing it: *Name = new wchar_t*[myCustomersCount]; or 3) just return the wchar_t** since you don't use the passed value for anything.

Then another definite issue: when you allocate memory for each name, you need to use the new[] operator there as well because otherwise you're only allocating room for a single wchar_t .

Finally, the potential issue. You don't show how exactly this code is getting each customer name. But if strName points to memory that is getting reused for each customer name as you put them all in your array, you're going to have to wstrcpy each name into the array. If it doesn't, then you don't need to allocate memory for each Names[i] as you can just store the result right into Names[i] .

A final note: just from looking at this code it seems like you're going to have lots of problems with memory management as it seems very unclear who is responsible for allocating and deallocating memory which is likely going to lead to memory leaks. Try your best to keep the responsibility for allocating and deallocating the memory in the same location and you'll reduce lots of potential headaches -- have callers allocate the memory before they call the function and have the caller deallocate the memory when they're done with it.

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

Edit If you really absolutely can't change the function signature, the only solution I can think of is to flatten your array and use C memory functions (you could also just use a long series of new s and delete s, but why not use realloc when this is what it's made for and you're managing memory without using other C++ features like the STL anyways?):

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

Note that this assumes that Name has already been initialized and points to memory where you can store a wchar_t* , just like the original version assumed count has been initialized and points to memory where you can store an int .

I thought I'd make a fresh start in a new answer.

Here's a simple program that does what I think you're trying to do, with the constraint that the signature of GetCustomers must not be altered.

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

Note that I've matched the cast inside the function with another one in main. This way we keep everything as it should be, except for that pesky GetCustomers signature.

Does this help?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM