前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ATL::CStringA和std::string之间转换的一些误区

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

作者头像
方亮
发布2019-01-16 15:13:01
9860
发布2019-01-16 15:13:01
举报
文章被收录于专栏:方亮

        对于刚做windows下VC的开发同学,类型转换应该是一个令其很苦恼的问题。我刚写工作的时候,也为这类问题不停的在网上搜索转换方法。最近工作中遇到一个“神奇”的bug(一般“神奇”的问题往往是低级错误导致的),最后跟踪发现还是类型转换问题。(转载请指明出处)

         ATL::CStringA和std::string都可以“接受”\0,也就是说,在CStringA的对象的内容和std::string类型数据中可以包含多个\0,而不是最后一位是\0,。这个可能是很多人对它们认识的一个误区。

        贴一下测试的相关代码

代码语言:javascript
复制
// 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截断。而正确的方法大致如下框架

代码语言:javascript
复制
{
        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);
        }
        // 内容就不比较了,直接在调试时看内存
 }

(转载请指明出处)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2011年07月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档