C++ 从函数返回动态分配的wchar_t*数组

C++ 从函数返回动态分配的wchar_t*数组,c++,memory-leaks,memory-management,C++,Memory Leaks,Memory Management,我有一个函数,其签名如下: GetCustomers( wchar_t** Name,int *count); 在main方法中:对客户的调用如下所示: GetCustomers( Name,&count); 函数的主体如下:由于客户数未知,所以我尝试动态分配meomry GetCustomers( wchar_t** Name,int *count) { //Logic to get customer count : Stored in int myCustomersCo

我有一个函数,其签名如下:

GetCustomers( wchar_t** Name,int *count);
在main方法中:对客户的调用如下所示:

GetCustomers( Name,&count);
函数的主体如下:由于客户数未知,所以我尝试动态分配meomry

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;
}
我认为这个实现将允许将数组名正确地传递回主函数,并在堆上分配内存,但它似乎不起作用。这里怎么了?MyCustomerCount在caller中似乎是正确的


ps:代码编译和执行,但在C++中接收的数组是垃圾。

你似乎是用C来思考,而不是真正的C++。我会使用类似于:

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

GetCustomers(std::back_inserter(customers));

你似乎是用C来思考,而不是C++。我会使用类似于:

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

GetCustomers(std::back_inserter(customers));

我真的不知道你想在这里做什么;从我对你的代码的理解来看;您试图将一些字符串存储到字符指针数组中

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

我真的不知道你想在这里做什么;从我对你的代码的理解来看;您试图将一些字符串存储到字符指针数组中

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

大体上,你大概有这样的东西

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覆盖第一个字符。分配是不必要的,事实上是内存泄漏,您应该像Suroot的回答那样,从strName分配到循环中的每个元素。

大体上,您可能有这样的情况

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覆盖第一个字符。分配是不必要的,实际上是内存泄漏,您应该像Suroot的回答那样,从strName分配到循环中的每个元素。

2个明确问题和1个潜在问题与您的代码有关。首先导致问题的主要问题是:名称本身是通过值传递的。这意味着,当您在函数的第一行指定内存时,您是在指定副本,而不是原始内存!您有三个选项:1保留双指针,让调用者负责分配内存,并为数组可容纳的名称数添加第三个参数;2将名称设置为三指针wchar_t***名称,然后您可以通过取消引用来分配它:*Name=new wchar_t*[MyCustomerCount];或者3只返回wchar\u t**,因为传递的值不用于任何内容

还有一个明确的问题:当您为每个名称分配内存时,还需要使用新的[]运算符,因为否则您只能为单个wchar\t分配空间

最后,潜在的问题。您无法显示此代码获取每个客户名称的准确程度。但是,如果strName指向的内存在您将每个客户名称都放在阵列中时会被重用,那么您必须将每个名称wstrcpy到阵列中。如果没有,则不需要为每个名称[i]分配内存,因为您可以将结果直接存储到名称[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;
}

注意,这假设名称已经初始化,并指向可以存储wchar_t*的内存,就像原始版本假设计数已经初始化一样,并指向可以存储int的内存。

2个明确问题和1个代码潜在问题。首先导致问题的主要问题是:名称本身是通过值传递的。这意味着,当您在函数的第一行指定内存时,您是在指定副本,而不是原始内存!您有三个选项:1保留双指针,让调用者负责分配内存,并为数组可以容纳的名称数量添加第三个参数,或2使名称成为一个trip le pointer wchar\u t***名称,然后您可以通过取消引用为其分配:*名称=新wchar\u t*[MyCustomerCount];或者3只返回wchar\u t**,因为传递的值不用于任何内容

还有一个明确的问题:当您为每个名称分配内存时,还需要使用新的[]运算符,因为否则您只能为单个wchar\t分配空间

最后,潜在的问题。您无法显示此代码获取每个客户名称的准确程度。但是,如果strName指向的内存在您将每个客户名称都放在阵列中时会被重用,那么您必须将每个名称wstrcpy到阵列中。如果没有,则不需要为每个名称[i]分配内存,因为您可以将结果直接存储到名称[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;
}

请注意,这假定名称已经初始化,并指向可以存储wchar_t*的内存,就像原始版本假定计数已经初始化,并指向可以存储int的内存。

我想我应该在新答案中重新开始

这里有一个简单的程序,它完成了我认为您正在尝试做的事情,但有一个约束条件,即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签名外,所有东西都保持原样


这有用吗?

我想我应该用一个新的答案重新开始

这里有一个简单的程序,它完成了我认为您正在尝试做的事情,但有一个约束条件,即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签名外,所有东西都保持原样


这有帮助吗?

您应该使用std::vector和/或std::wstring。您能否详细说明一下您的意思,但它似乎不起作用?您是否有编译问题;您是在划分错误还是名称无效?strName的定义在哪里?strName可能是GetCustomers中的本地变量,这可能导致返回调用方时它不正确。我没有在caller:Compiles and runs fine中获取名称数组。strName来自对另一个函数的调用…我得到了一个有效的指针,所以我假设它是有效的?我刚刚尝试在新的tempStrName中创建该指针的副本,并将其返回到前面提到的数组中的调用者:在VS2008调试器中,我看到了。您应该使用std::vector和/或std::wstring。您能否详细说明一下您的意思,但它似乎不起作用?您是否有编译问题;您是在划分错误还是名称无效?strName的定义在哪里?strName可能是GetCustomers中的本地变量,这可能导致返回调用方时它不正确。我没有在caller:Compiles and runs fine中获取名称数组。strName来自对另一个函数的调用…我得到了一个有效的指针,所以我假设它是有效的?我只是尝试在新的Twitter中创建一个指针的副本,并将它返回到前面提到的数组中的调用方:在VS2008调试器中,我看到。@ Jerry:这是一个很好的答案,但我不知何故,感觉到OP仍在学习C++的C子集。如果是这样的话,他必须学会手动处理诸如内存分配之类的低级任务,只有这样,他才能学习诸如字符串和向量之类的强大抽象。@Eduardo:你在抽什么?对程序员来说,立即抛弃C子集是有益的。@Eduardo Leon:我不同意。向量和字符串应该放在第一位。许多人根本不需要学习完全手工管理。我们中的一些人学习了JAVA和C的编程方法,但不得不时不时地处理遗留代码……学习任何东西都不会太晚,放弃C子集意味着放弃遗留应用程序。并不总是一个选项。@Ed:这是一个解决方案@懒惰:为什么这个解决方案不起作用?“什么是什么?”Jerry:这是一个很好的答案,但是我不知何故,感觉到OP仍在学习C++的C子集。如果是这样的话,他必须学会手动处理诸如内存分配之类的低级任务,只有这样,他才能学习诸如字符串和向量之类的强大抽象。@Eduardo:你在抽什么?这对进步是有益的

“打夯工们要马上抛弃C子集。”爱德华多·利昂:我不同意。向量和字符串应该放在第一位。许多人根本不需要学习完全手工管理。我们中的一些人学习了JAVA和C的编程方法,但不得不时不时地处理遗留代码……学习任何东西都不会太晚,放弃C子集意味着放弃遗留应用程序。并不总是一个选项。@Ed:这是一个解决方案@懒惰:为什么这个解决方案不起作用?不知何故,这是什么?Line*Names=new wchar\u t*[MyCustomerCount];将引发以下编译错误:无法从“wchar\u t**”转换为“wchar\u t*”是的,我知道。但我不想让事情变得比现在更复杂,所以我想在你解决所有其他问题之前,我会先离开。正确的做法是将参数从wchar\u t**更改为wchar\u t***,基本上是指向字符串数组的指针,但我在您对另一个答案的评论中注意到,您不能更改签名。在这种情况下,您可以使用cast:*Names=wchar\u t*new wchar\u t*[xxx]。你应该尽可能避免石膏,但是,既然您知道它是一个wchar_t*数组,那么指向该数组的指针与指向其第一个元素的指针相同。强制转换的结果:XXX.exe中发生了类型为“System.NullReferenceException”的未处理异常。其他信息:对象引用未设置为对象的实例。听起来您没有传递地址从main获取变量的。应该是这样的:wchar_t*names=NULL;整数计数;获取客户名称和数量;我确实传递了地址,但记住在main.Line*names=new wchar\u t*[MyCustomerCount]中名称被初始化为null;将引发以下编译错误:无法从“wchar\u t**”转换为“wchar\u t*”是的,我知道。但我不想让事情变得比现在更复杂,所以我想在你解决所有其他问题之前,我会先离开。正确的做法是将参数从wchar\u t**更改为wchar\u t***,基本上是指向字符串数组的指针,但我在您对另一个答案的评论中注意到,您不能更改签名。在这种情况下,您可以使用cast:*Names=wchar\u t*new wchar\u t*[xxx]。你应该尽可能避免石膏,但是,既然您知道它是一个wchar_t*数组,那么指向该数组的指针与指向其第一个元素的指针相同。强制转换的结果:XXX.exe中发生了类型为“System.NullReferenceException”的未处理异常。其他信息:对象引用未设置为对象的实例。听起来您没有传递地址从main获取变量的。应该是这样的:wchar_t*names=NULL;整数计数;获取客户名称和数量;我确实传递了地址,但记住名称在main中被初始化为null。对不起,您已经更改了我函数的签名:有些事情我不能做!无论如何,谢谢。@lazynomore那么你能做什么呢?我希望能用原始函数签名将数组取回。@lazynomore如上所述,你可以反复调用new和delete,但每次都必须将旧内容复制到新内容,这将非常低效。此外,这段代码已经在以任何方式处理内存C风格,因此使用realloc也不是代码风格的突破;引发此异常:XXX.exe中发生“System.NullReferenceException”类型的未处理异常其他信息:对象引用未设置为对象的实例抱歉,您已更改我函数的签名:我无法执行某些操作!无论如何,谢谢。@lazynomore那么你能做什么呢?我希望能用原始函数签名将数组取回。@lazynomore如上所述,你可以反复调用new和delete,但每次都必须将旧内容复制到新内容,这将非常低效。此外,这段代码已经在以任何方式处理内存C风格,因此使用realloc也不是代码风格的突破;引发此异常:XXX.exe中发生“System.NullReferenceException”类型的未处理异常其他信息:未设置为对象实例的对象引用仍然无法工作,因为代码假定对Name参数的本地副本的更改将影响调用方使用的实际对象。Name=new wchar_t*[MyCustomerCount];不会对调用方正在使用的实际对象执行任何操作。仍然无法工作,因为代码假定对Name参数的本地副本的更改将影响调用方正在使用的实际对象。Name=new wchar_t*[MyCustomerCount];对调用方使用的实际对象没有任何影响。这是一个很好的解决方案…我在这里看到的唯一缺点是名称长度不能超过10个字符。谢谢这只是一个演示,演示如何获得
经过麻烦的GetCustomers签名。我认为您已经有了获取名称并将其添加到数组中的代码,因此请使用该代码而不是我的示例代码。这是一个很好的解决方案……我在这里看到的唯一缺点是名称长度不能超过10个字符。谢谢这只是一个演示,演示如何通过麻烦的GetCustomers签名。我认为您已经有了获取名称并将其添加到数组中的代码,因此请使用该代码而不是我的示例代码。