ATL::CStringA和std::string之间转换的一些误区

来源:互联网 发布:网易邮箱端口设置 编辑:程序博客网 时间:2024/06/05 22:57

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

  (转载请指明出处)

 

原创粉丝点击