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

Visual c++ 如何在将托管字符串转换为UTF-8编码的非托管字符*后释放内存?,visual-c++,utf-8,c++-cli,Visual C++,Utf 8,C++ Cli,我不熟悉C++/CLI,因此不确定在使用以下代码时如何释放内存(获得了解决方案并进行了一些修改): 我也不熟悉Visual C++,但根据 固定指针不能用作:[……]函数的返回类型 我不确定当函数结束时指针是否有效(即使它伪装成char*)。 似乎您在函数中声明了一些要传递给调用作用域的局部变量。但是,当您从函数返回时,这些局部变量可能会超出作用域。 也许你应该重新考虑一下你首先要达到的目标是什么 注意,在本文中引用了一个std::string(按值传递,即按副本传递)作为返回参数 std::s

我不熟悉C++/CLI,因此不确定在使用以下代码时如何释放内存(获得了解决方案并进行了一些修改):


我也不熟悉Visual C++,但根据

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

我不确定当函数结束时指针是否有效(即使它伪装成
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 managedStringToStlString(系统::字符串^s)
{
编码^u8=编码::UTF8;
数组^bytes=u8->GetBytes(s);
pinptr pinnedPtr=&字节[0];
返回字符串((char*)pinnedPtr);
}
因此,不会将任何局部变量传递到它们的作用域之外。该字符串由copy作为非托管
std::string
处理。这正是建议的

当您以后需要
const char*
时,您可以使用该方法获取一个。注意,您还可以使用将
std::string
写入文件。
这是您的选项吗?

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

解决办法是

  • 获取System::String缓冲区并将其固定,以便GC无法 移动它
  • 在非托管堆(或仅堆)上分配内存,其中 它不在总承包商的管辖范围内,且不能由总承包商移动 GC
  • 从中复制内存(并转换为所需编码) 字符串缓冲区到非托管堆上分配的缓冲区
  • 取消锁定指针,以便GC可以再次移动System::String 内存中。(当引脚ptr退出功能时完成此操作 范围。)
  • 示例代码:

    char*ManagedStringToUnmanagedUTF8Char(字符串^str)
    {
    //从System::String获取缓冲区并将其固定
    pin_ptr wch=PtrToStringChars(str);
    //获取所需的字节数
    int nBytes=::宽图表多字节(CP_UTF8,NULL,wch,-1,NULL,0,NULL,NULL);
    断言(n字节>=0);
    //在C++中分配GC无法移动的内存
    char*lpszBuffer=新字符[n字节];
    //将缓冲区初始化为空
    零内存(lpszBuffer,(N字节)*大小(字符));
    //将wchar_t*转换为char*,指定UTF-8编码
    n字节=::宽图表多字节(CP_UTF8,NULL,wch,-1,lpszBuffer,n字节,NULL,NULL);
    断言(n字节>=0);
    //返回缓冲区
    返回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;
    

    这显然是Visual C++中的某个语言扩展。该扩展无法工作,当函数返回时,数组将被取消固定。之后,垃圾收集器可以移动数组并使程序随机失败。您必须复制它。我使用该函数将UTF-8字符写入文本文件,它每次都工作,因此指针似乎不可用o函数结束时有效(幸运的是):)我想要实现的是创建一个函数,将托管.NET字符串转换为非托管字符*(必须是UTF-8),我的包装类(托管代码和非托管代码之间的边界)将大量使用它。我在网上找到了很多解决方案,这是我能找到的最简单的一个。是的,这是一个选项,你能教我如何为我的案例这样做吗?好吧,我错了,我很幸运,gc在我测试时没有收集到它。我手动调用了System::GC::Collect(),它在调用范围内变成了一些随机字符。我想我需要复制一下,但我不知道怎么做。我已经添加了你提到的问题中的建议。我想这也解决了你的问题。有关引用超出其范围的局部变量的有效性,请参阅。
    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 );
    }
    
    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;