wstring::c_str() 包含垃圾

Posted

技术标签:

【中文标题】wstring::c_str() 包含垃圾【英文标题】:wstring::c_str() contains garbage 【发布时间】:2014-10-10 16:46:28 【问题描述】:

我有一个std::wstring decode(const char *s) 函数。

我是这样使用的:

const char *src = "some string";
const wchar_t *result = decode(src).c_str();

我总是在result[0] 中得到垃圾,有时在result[1] 中也是如此。

当我以其他方式使用它时,我不会得到垃圾:

std::wstring res = decode(src);
const wchar_t *result = res.c_str();

我的decode 函数定义如下,它可以完成它的工作。唯一的问题是调用代码(上)。

std::wstring decode(const char *s, UINT cp=CP_ACP)

    if(s == NULL)
        return std::wstring();
    int length = ::MultiByteToWideChar(cp, 0, s, -1, NULL, 0 );
    wchar_t *buf = new wchar_t[length];
    ::MultiByteToWideChar(cp, 0, s, -1, buf, length);
    std::wstring r(buf);
    delete[] buf;
    return r;

我使用 Visual C++ 2008 SP1 编译。

【问题讨论】:

查看所有关于临时变量的 cmets,这是您的问题的原因。我想指出 decode 在您的示例用法中使用一个参数,但是 decode 是用两个参数定义的。如果您的意图是从 MBCS 转换为 Widechar,您可以使用 ATL 字符串转换宏 CA2W(您可以 #include <atlbase.h> 。宏 ATL 字符串宏信息可以在 MSDN documentation 中找到 我已经编辑了代码,并提供了默认参数,正如我在标题中声明的那样。谢谢建议 【参考方案1】:
const wchar_t *result = decode(src).c_str();

decode的返回值是一个临时值,在调用c_str()后被销毁。因此result 指向释放的内存。

要么

延长返回值的生命周期(例如,将其分配给局部变量) 复制结果

【讨论】:

我会接受这个。我也可以在函数调用中使用 decode(src).c_str() 对吧? 是的。当完整表达式完成评估时,临时对象将被销毁。 decode(src).c_str() 作为函数调用中的函数参数是函数调用的子表达式。【参考方案2】:

12.2 临时对象[class.temporary]

3 当实现引入具有非平凡构造函数(12.1、12.8)的类的临时对象时,它应确保为临时对象调用构造函数。类似地,应该使用非平凡的析构函数(12.4)调用析构函数。 临时对象在评估完整表达式 (1.9) 的最后一步时被销毁,该完整表达式 (从词法上) 包含它们的创建点。 即使评估以抛出异常结束也是如此。销毁临时对象的值计算和副作用仅与完整表达式相关联,与任何特定子表达式无关。

因此,在销毁后不要使用临时对象,就像在您的第一个示例中一样。

【讨论】:

【参考方案3】:

您的具体问题的答案可以在提供的两个好的答案中找到。由于问题标题只是wstring::c_str() contains garbage,我将指出一种方法来获得正确的结果没有decode 函数。

Microsoft 添加了许多 ATL 转换宏和类来在宽字符 (Unicode) 和 MBCS (Ascii) 之间进行转换。它们在 VS2008 上可用。支持的转换可以在MSDN Documentation中找到:

ATL 7.0 引入了几个新的转换类和宏,对现有宏进行了重大改进。新的字符串转换类和宏的名称采用以下形式: CSourceType2[C]DestinationType[EX]。

[剪辑]

源类型/目标类型

A = ANSI 字符串。

W = Unicode 字符串。

T = 通用字符串(当 _UNICODE 定义时相当于 W,否则相当于 A)。

OLE = OLE 字符串(相当于 W)。

[EX] 是可选的,如果您不想指定与宏一起使用的默认内部缓冲区的大小,则经常使用它。

在您的情况下,转换宏 CA2W(将 Ascii 转换为 Widechar)应该可以满足您的需求。您只需要#include <atlbase.h> 即可使用它们。这些宏有 2 个参数 - 要转换的字符串和代码页(如果未指定,则默认为 CP_ACP)

在你的情况下,你有:

std::wstring res = decode(src);
const wchar_t *result = res.c_str();

使用 ATL 转换宏,您可以这样做:

std::wstring res = CA2W(src, CP_ACP);
const wchar_t *result = res.c_str();

由于宏默认为 Ansi 代码页,因此您也可以像这样省略 CP_ACP:

std::wstring res = CA2W(src);
const wchar_t *result = res.c_str();

关于临时对象的问题同样适用于这些宏返回的类。微软甚至在之前提供的链接中用一个不正确的使用示例记录了这个问题:

// Example 3 
// Incorrect use of conversion macros. 
void ExampleFunction3(LPCWSTR pszW)

    // Create a temporary instance of CW2A, 
    // save a pointer to it and then delete 
    // the temportary instance.
    LPCSTR pszA = CW2A(pszW);
    // The pszA in the following line is an invalid pointer, 
    // as the instance of CW2A has gone out of scope.
   ExampleFunctionA(pszA);

【讨论】:

以上是关于wstring::c_str() 包含垃圾的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 12c 数字字段映射到 SSIS 中的 DT_WSTR

SSIS - SQL Server datetimeoffset目标列,标识为DT_WSTR

Unicode 转码 Ansi ,用于读取注册表后的值转换

如何使用带字符串函数的强制转换

SSIS包写入CRM 2011数据类型错误

windows下字符编码的转化函数