如何在将托管字符串转换为UTF-8编码的非托管字符*后释放内存?

时间:2011-12-14 10:11:51

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

我不熟悉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;
}

当我通过在文本文件中写入char来测试它时,上面的代码正在工作。如果我遗漏了某些东西(需要清理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

2 个答案:

答案 0 :(得分:0)

我也不熟悉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写入文件。 这是你的选择吗?

答案 1 :(得分:0)

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

解决方案是

  1. 获取System :: String缓冲区并将其固定,以便GC不能 移动它。
  2. 在非托管堆(或只是堆)上分配内存 它不属于GC的管辖范围,不能被移动 GC
  3. 从中复制内存(并转换为所需的编码) System :: String缓冲区到非托管堆上分配的缓冲区。
  4. 取消固定指针,以便GC可以再次移动System :: String 在记忆中。 (这是在pin_ptr退出函数时完成的 范围)。
  5. 示例代码:

    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;