对于刚做windows下VC的开发同学,类型转换应该是一个令其很苦恼的问题。我刚写工作的时候,也为这类问题不停的在网上搜索转换方法。最近工作中遇到一个“神奇”的bug(一般“神奇”的问题往往是低级错误导致的),最后跟踪发现还是类型转换问题。(转载请指明出处)
ATL::CStringA和std::string都可以“接受”\0,也就是说,在CStringA的对象的内容和std::string类型数据中可以包含多个\0,而不是最后一位是\0,。这个可能是很多人对它们认识的一个误区。
贴一下测试的相关代码
// string.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <string> std::string RetCommonString() { std::string str = "ABCDE\0FGH"; return str; } std::string RetBreakString() { std::string str = ""; char charrayp[9]; charrayp[0] = 'A'; charrayp[1] = 'B'; charrayp[2] = 'C'; charrayp[3] = 'D'; charrayp[4] = 'E'; charrayp[5] = '\0'; charrayp[6] = 'F'; charrayp[7] = 'G'; charrayp[8] = 'H'; str.append( charrayp, 9); return str; } ATL::CStringA RetCommonCStringA() { ATL::CStringA strA = "ABCDE\0FGH"; return strA; } ATL::CStringA RetBreakCStringA() { ATL::CStringA strA = ""; char charrayp[9]; charrayp[0] = 'A'; charrayp[1] = 'B'; charrayp[2] = 'C'; charrayp[3] = 'D'; charrayp[4] = 'E'; charrayp[5] = '\0'; charrayp[6] = 'F'; charrayp[7] = 'G'; charrayp[8] = 'H'; LPSTR lpTmp = strA.GetBuffer(9); if ( NULL != lpTmp ) { memcpy( (void*)lpTmp, charrayp, 9 ); } strA.ReleaseBuffer(9); return strA; } int _tmain(int argc, _TCHAR* argv[]) { ATL::CStringA strCommonCStringA = RetCommonCStringA(); ATL::CStringA strBreakCStringA = RetBreakCStringA(); void* pstrCommonCStringA = &strCommonCStringA; void* pstrBreakCStringA = &strBreakCStringA; std::string strCommonString = RetCommonString(); std::string strBreakString = RetBreakString(); void* pstrCommonString = &strCommonString; void* pstrBreakString = &strBreakString; { int nstrBreakCStringA = strBreakCStringA.GetLength(); int nstrCommonCStringA = strCommonCStringA.GetLength(); if ( nstrBreakCStringA == nstrCommonCStringA ) { // 这儿不会相等 ATLASSERT(FALSE); } std::string::size_type lstrBreakStringLength = strBreakString.length(); std::string::size_type lstrCommonStringLength = strCommonString.length(); if ( lstrCommonStringLength == lstrBreakStringLength ) { // 这儿不会相等 ATLASSERT(FALSE); } } // std::string转CStringA的正确方法,但存在长度限制 { std::string::size_type lstringlength = strBreakString.length(); ATL::CStringA CStringAobj = ""; LPSTR lpCStringAobj = CStringAobj.GetBuffer( (int)lstringlength ); memcpy( (void*) lpCStringAobj, strBreakString.c_str(), lstringlength ); CStringAobj.ReleaseBuffer((int)lstringlength); std::string::size_type lstrBreakStringLength = strBreakString.length(); int nCStringobj = CStringAobj.GetLength(); if ( lstrBreakStringLength != nCStringobj ) { ATLASSERT(FALSE); } // 内容就不比较了,直接在调试时看内存 } // std::string转CStringA的错误方法 { // ERROR: CStringAObj = stringobj.c_str() { ATL::CStringA CStringAobj = strBreakString.c_str(); std::string::size_type lstrBreakStringLength = strBreakString.length(); int nCStringobj = CStringAobj.GetLength(); if ( lstrBreakStringLength != nCStringobj ) { ATLASSERT(FALSE); } } // ERROR: CStringobj = CStringA( stringobj.c_str() ); { ATL::CStringA CStringAobj( strBreakString.c_str() ) ; std::string::size_type lstrBreakStringLength = strBreakString.length(); int nCStringobj = CStringAobj.GetLength(); if ( lstrBreakStringLength != nCStringobj ) { ATLASSERT(FALSE); } } // ERROR: CStringAobj.Format( "%s", stringobj.c_str() ); { ATL::CStringA CStringAobj; CStringAobj.Format( "%s", strBreakString.c_str() ); std::string::size_type lstrBreakStringLength = strBreakString.length(); int nCStringobj = CStringAobj.GetLength(); if ( lstrBreakStringLength != nCStringobj ) { ATLASSERT(FALSE); } } } // CStringA转std::string的正确方法 { int nstrBreakCStringALength = strBreakCStringA.GetLength(); LPSTR lpstrBreakCStringA = strBreakCStringA.GetBuffer( nstrBreakCStringALength ); strBreakCStringA.ReleaseBuffer( nstrBreakCStringALength ); std::string strobj = ""; strobj.append( lpstrBreakCStringA, nstrBreakCStringALength ); std::string::size_type lstrobjLength = strobj.length(); int nCStringobj = strBreakCStringA.GetLength(); if ( lstrobjLength != nCStringobj ) { ATLASSERT(FALSE); } } // ERROR: stringobj = CStringAObj { std::string strobj = strBreakCStringA; std::string::size_type lstrobjLength = strobj.length(); int nCStringobj = strBreakCStringA.GetLength(); if ( lstrobjLength != nCStringobj ) { ATLASSERT(FALSE); } } return 0; }
调试这个程序,我们查看一下相关内存。
std::string类型数据strBreakString(内容为"ABCDE\0FGH") 的在内存中的数据如下图
红线标志的09就是这个strBreakString的长度。
std::string类型数据strCommonString(内容为"ABCDE") 的在内存中的数据如下图
红线标志的05就是这个strCommonString的长度。
查看一下strBreakString和strCommonString的来源,可以看出,给std::string类型数据用=赋值,如果内容中包含\0,则std::string类型数据只能接受\0之前的数据。所以strCommonString的数据只有\0之前的ABCDE。而使用std::string的append方法,将会将\0也赋值进去。
我们再看一下ATL::CStringA对象在内存中的数据形式。
ATL::CStringA类型数据strBreakCStringA (内容为"ABCDE\0FGH") 的在内存中的数据如下图
红线标志的09就是这个strBreakCStringA 的长度。
ATL::CStringA类型数据strCommonCStringA (内容为"ABCDE") 的在内存中的数据如下图
红线标志的05就是这个strCommonCStringA 的长度。
查看一下strBreakCStringA 和strCommonCStringA 的来源,可以看出,给ATL::CStringA类型数据用=赋值,如果内容中包含\0,则ATL::CStringA类型数据只能接受\0之前的数据。所以strCommonCStringA 的数据只有\0之前的ABCDE。而使用ATL::CStringA的GetBuffer、ReleaseBuffer等方法,再加上memcpy,可以将\0也赋值进去。
如果方便,可以调试一下这个例子。可以发现网上一些std::string和ATL::CStringA之间的转换方法存在错误。如:网上有些方法是CStringAObj = stringobj.c_str(),或者CStringAobj.Format( "%s", stringobj.c_str() ),这些方法都会导致ATL::CStringA对象的内容可能被std::string中的存在的\0截断。而正确的方法大致如下框架
{ std::string::size_type lstringlength = strBreakString.length(); ATL::CStringA CStringAobj = ""; LPSTR lpCStringAobj = CStringAobj.GetBuffer( (int)lstringlength ); memcpy( (void*) lpCStringAobj, strBreakString.c_str(), lstringlength ); CStringAobj.ReleaseBuffer((int)lstringlength); std::string::size_type lstrBreakStringLength = strBreakString.length(); int nCStringobj = CStringAobj.GetLength(); if ( lstrBreakStringLength != nCStringobj ) { ATLASSERT(FALSE); } // 内容就不比较了,直接在调试时看内存 }
(转载请指明出处)