【问题标题】:How to free up memory after converting a managed string to UTF-8 encoded unmanaged char*?将托管字符串转换为 UTF-8 编码的非托管 char* 后如何释放内存?
【发布时间】:2011-12-14 10:11:50
【问题描述】:

我不熟悉 C++/CLI,所以不知道在使用下面的代码时如何释放内存(得到了解决方案 here 并稍作修改):

char* ManagedStringToUnmanagedUTF8Char( String^ s )
{
    array<unsigned char> ^bytes = Encoding::UTF8->GetBytes( s );
    pin_ptr<unsigned char> pinnedPtr = &bytes[0];
    return (char*)pinnedPtr;
}

当我通过在文本文件中写入字符来测试上面的代码时,它是有效的。如果我遗漏了什么(需要清理 pinnedPtr?),请告诉我。

现在当我使用它时:

char* foobar = ManagedStringToUnmanagedUTF8Char("testing");

//do something with foobar

//do I need to free up memory by deleting foobar here? 
//I tried 'delete foobar' or free(foobar) but it crashes my program

【问题讨论】:

  • 这显然是 Visual-C++ 中的一些语言扩展
  • 这不行,当函数返回时,数组将被取消固定。之后垃圾收集器可以移动数组并使您的程序随机失败。你必须复制它。

标签: visual-c++ utf-8 c++-cli


【解决方案1】:

Hans Passant 的评论是正确的,垃圾收集器可以将返回的指向缓冲区的指针移动到内存中。这是因为,当函数堆栈展开时,pin_ptr 将解除指针。

解决办法是

  1. 获取 System::String 缓冲区并将其固定,以便 GC 无法 移动它。
  2. 在非托管堆(或只是堆)上分配内存 它不属于 GC 的管辖范围,并且不能由 GC。
  3. 从 System::String 缓冲区指向在非托管堆上分配的缓冲区。
  4. 取消固定指针,以便 GC 可以再次移动 System::String 在记忆中。 (这是在 pin_ptr 退出函数时完成的 范围。)

示例代码:

char* ManagedStringToUnmanagedUTF8Char(String^ str)
{
    // obtain the buffer from System::String and pin it
    pin_ptr<const wchar_t> wch = PtrToStringChars(str);

    // get number of bytes required
    int nBytes = ::WideCharToMultiByte(CP_UTF8, NULL, wch, -1, NULL, 0, NULL, NULL);
    assert(nBytes >= 0);

    // allocate memory in C++ where GC cannot move
    char* lpszBuffer = new char[nBytes];

    // initialize buffer to null
    ZeroMemory(lpszBuffer, (nBytes) * sizeof(char)); 

    // Convert wchar_t* to char*, specify UTF-8 encoding
    nBytes = ::WideCharToMultiByte(CP_UTF8, NULL, wch, -1, lpszBuffer, nBytes, NULL, NULL);
    assert(nBytes >= 0);

    // return the buffer
    return lpszBuffer;
}

现在,使用时:

char* foobar = ManagedStringToUnmanagedUTF8Char("testing");

//do something with foobar

//when foobar is no longer needed, you need to delete it
//because ManagedStringToUnmanagedUTF8Char has allocated it on the unmanaged heap.
delete foobar;

【讨论】:

    【解决方案2】:

    我也不熟悉Visual-C++,但是根据this article

    固定指针不能用作:[...]函数的返回类型

    我不确定函数结束时指针是否有效(即使它伪装成char*. 似乎您在要传递给调用范围的函数中声明了一些局部变量。 '然而,当你从函数返回时,这些可能无论如何都会超出范围。 或许您应该重新考虑您首先要实现的目标?

    请注意,您在文章中引用了 std::string(按值传递,即通过复制)用作返回参数。

    std::string managedStringToStlString( System::String ^s )
    {
      Encoding ^u8 = Encoding::UTF8;
      array<unsigned char> ^bytes = u8->GetBytes( s );
      pin_ptr<unsigned char> pinnedPtr = &bytes[0];
      return string( (char*)pinnedPtr );
    }
    

    因此没有局部变量被传递到它们的范围之外。该字符串作为非托管std::string 被复制处理。这正是this post 所建议的。

    以后需要const char*时,可以使用string::c_str()的方法来获取。请注意,您也可以使用file streamsstd::string 写入文件。 这是您的选择吗?

    【讨论】:

    • 我使用该函数将 UTF-8 字符写入文本文件并且每次都有效,因此当函数结束时指针似乎是有效的(幸运的是):) 我想要实现的是创建一个将托管 .NET 字符串转换为非托管 char*(必须是 UTF-8)的函数,我的包装类(管理代码和非托管代码之间的边界)将大量使用它。我在网上找到了很多解决方案,这是我能找到的最简单的一个..
    • 是的,这是一个选项,你能教我如何为我的情况这样做吗?
    • 好吧我错了,我测试的时候gc没有收集到是我的幸运。我手动调用了 System::GC::Collect() ,它在调用范围内变成了一些随机字符。我想我需要复制它,但我不知道如何..
    • 我已经添加了您引用的问题的建议。我认为这也解决了你的问题。有关超出其范围引用的局部变量的有效性,请参阅this very good post
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 2010-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-02
    相关资源
    最近更新 更多