【问题标题】:C string to wide C string assignmentC 字符串到宽 C 字符串赋值
【发布时间】:2012-11-04 12:06:45
【问题描述】:

我对 C 字符串和宽 C 字符串有点困惑。为了这个问题,假设我使用 Microsoft Visual Studio 2010 Professional。如果我的任何信息不正确,请告诉我。

我有一个带有 const wchar_t* 成员的结构,用于存储名称。

struct A
{
    const wchar_t* name;
};

当我这样为对象“a”分配名称时:

int main()
{
    A a;

    const wchar_t* w_name = L"Tom";
    a.name = w_name;

    return 0;
}

这只是将 w_name 指向的内存地址复制到 a.name 中。现在 w_name 和 a.name 都是指向内存中相同地址的宽字符指针。

如果我是正确的,那么我想知道如何处理这种情况。我正在使用 tinyxml2 从 XML 属性中读取 C 字符串。

tinyxml2::XMLElement* pElement;
// ...
const char* name = pElement->Attribute("name");

获得 C 字符串后,我将其转换为宽字符串,如下所示:

size_t newsize = strlen(name) + 1;
wchar_t * wcName = new wchar_t[newsize];
size_t convertedChars = 0;
mbstowcs_s(&convertedChars, wcName, newsize, name, _TRUNCATE);

a.name = wcName;

delete[] wcName;

如果到目前为止我是正确的,那么行:

 a.name = wcName;

只是将数组 wcName 的第一个字符的内存地址复制到 a.name 中。但是,我在分配此指针后直接删除 wcName,这将使其指向垃圾。

如何将我的 C 字符串转换为宽字符 C 字符串,然后将其分配给 a.name?

【问题讨论】:

  • 为什么要删除 wcName?如果重点是转换为 wchar_t,那么为什么要删除保存转换结果的新分配内存?
  • 发布的代码有问题。似乎没有删除它的理想位置。我想过添加一个 ~A() 析构函数来删除它,但出于某种原因,这对我来说真的很难看。
  • @user974967 (析构函数)精确地在您可以删除它的位置(或者在您需要重新分配它的任何时候,但我建议尝试重用缓冲区,如果新任务仍然合适。

标签: c++ string cstring


【解决方案1】:

最简单的方法可能是让name 变量负责内存管理。反过来,这很容易通过将其声明为

std::wstring name;

这些家伙没有独立内容和对象变异的概念,也就是说,您不能真正将单个字符设为const,而将整个对象设为const 会阻止它被分配到。

【讨论】:

  • 但这将如何完成他的转换呢?当然,这将使他不必管理其转换缓冲区的范围外删除,该缓冲区当前在保存地址后被错误地删除。还是标准库添加了char*std::wstring 上转换而我错过了它(这不是我第一次在方向盘上睡觉)。
  • 他似乎已经在使用mbstowc() 进行转换(以及某个供应商试图强加的“安全”可憎mbstowcs_s())。这是否正确处理 XML 文件中的任何编码,我不知道,但似乎问题不在于转换。
  • @WhozCraig - 使用wstring 将使a.name = wcName; 正常工作,之后可以毫无问题地删除临时缓冲区。
  • @BoPersson 或使用 std::wstring as 缓冲区本身。 (见answer
【解决方案2】:

可以在使用std::wstring 时执行此操作,而无需依赖额外的临时转换缓冲区分配和销毁。除非您非常担心堆碎片或在有限的系统(又名 Windows Phone)上,否则这并不是非常重要。它只需要在正面进行一些设置。让标准库为您管理内存(稍微推动一下)。

class A
{
   ...
   std::wstring a;
};


// Convert the string (I'm assuming it is UTF8) to wide char
int wlen = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, NULL);
if (wlen > 0)
{
    // reserve space. std::wstring gives us the terminator slot
    // for free, so don't include that. MB2WC above returns the
    // length *including* the terminator.
    a.resize(wlen-1);
    MultiByteToWideChar(CP_UTF8, 0, name, -1, &a[0], wlen);
}
else
{   // no conversion available/possible.
    a.clear();
}

在一个完整的旁注中,您可以构建 TinyXML 以使用标准库和 std::string 而不是 char *,这对您没有太大帮助,但可能会为您节省 未来strlen() 稍后调用。

【讨论】:

    【解决方案3】:

    正如您正确提到的,a.name 只是一个指针,它不假设任何已分配的字符串存储。您必须使用 new 或静态/作用域数组手动管理它。

    要摆脱这些无聊的东西,只需使用可用的字符串类之一:来自 ATL 的 CStringW(易于使用但特定于 MS)或来自 STL(C++ 标准,但不太容易从 @ 转换)的 std::wstring 987654326@):

    #include <atlstr.h>
    
    // Conversion ANSI -> Wide is automatic
    const CStringW name(pElement->Attribute("name"));    
    

    不幸的是,std::wstringchar* 的使用并不那么容易。 在此处查看转换功能:How to convert std::string to LPCWSTR in C++ (Unicode)

    【讨论】:

      猜你喜欢
      • 2012-10-09
      • 2016-04-01
      • 2012-05-24
      • 2011-09-08
      • 1970-01-01
      • 2020-08-23
      • 1970-01-01
      • 1970-01-01
      • 2012-03-01
      相关资源
      最近更新 更多