对于刚做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
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);
}
// 内容就不比较了,直接在调试时看内存
}
(转载请指明出处)