ANSI,MBCS,Unicode与使用swprintf的陷阱
原创作者:CC
我们可能平时经常使用swprintf这个API函数,其实这个函数存在很容易让人误解的陷阱,有时在我们的程序使用的时候平时不会出现错误,可是这些API函数的误使用可能会产生一些偶然的错误,这些错误是相当的难找的,所以在平时我们写程序就应该对我们每使用的一个API函数都应该足够的了解其正确的用法,就好像是我们把一个CString直接强转为LPTSTR ,这样就会形成潜在的危险,如果我们了解CString的用法,就可以这样更安全的使用:
CString str ;
…..//assume str has contain valid data
LPTSTR lpszBuff =str.GetBuffer(0);
swprintf这个函数的陷阱也像上面说的一样,使用不当都存在潜在的危险,所谓”知其然知其所以然’,在分析这个API可能存在的陷阱之前,先简单的说一下与之相关的几个概念:
ANSI字符集:single-byte character set (SBCS),就是我们平时说的char ,每个字符是一个字节,以’\<?xml:namespace prefix = st1 />0’作为字符串的结束符.
MBCS:(multi-byte character set ):有的字符是占两个字节,有的字符是占一个字节,OS提供了一些API对之进行操作,如果是两个字节,包括一个称为”Lead bytes”和”trail byte”.也是以一个’\0’作为结束符.
Unicode:又称为宽字符,每个字符占两个字节,以两个字节的’\0’结束
以上只是简单的介绍一下,详细的可见: String complete Guide
下面进入正题,先看一下这一段代码,猜一下结果是什么:
wchar_t strTemp[64];
swprintf(strTemp,L"%s","ngangche结果");
第一种情况:
BYTE* pData =(BYTE*) strTemp;
int nLen = _mbslen(pData); //结果是:0x0A
第二种情况:
char* pData =(char*) strTemp;
int nLen = strlen(pData); //结果是0x0C
第三种情况:
int nLen = wcslen(strTemp);//结果是0x06
呵呵,为什么同一段内存,会有这么多不同,你可能在想这段内存到底放的什么,因为这个函数名称是有些让人迷惑的,swprintf ,而不是swprintf而不是sprintf ,可能想到,最后strTemp中应该放的是Unicode字符,而实际放的是什么东西呢,为什么第三种情况结果不是我们想要的10而是6,不用着急,看一下这张图就什么都清楚了:
这会应该明白了为什么了吧!
上面的API用法,并不是Unicode ,你可以看到0x12ff00 ~0x12ff07是ASCII 码:ngangche(8个字符),而后面的两个中文字符:0x12ff08~0x12ff0b是两个中文字符,后面是两个’\0’,所以用_mbslen算出的结果是正确的,是10个字符.这个也不是MBCS,因为MBCS是一个’\0’结束.
第二种情况就很容易了:中文占两个字符正好是12个字符
第三种情况也很容易理解12/2 = 6个字符.
看来这个API函数实现并不像我们想像的那么简单,是不是?你以后用的时候就在注意了,其实上面的应该调用wcslen返回正确的结果,看来并不是那么回事了.
上面代码改成这样,第三个就能得到正确的结果了:
swprintf(strTemp,L"%s",L"ngangche结果");
int nLen = wcslen(strTemp); //nLen是10,是我们想要得到的结果.
再看一下内存,就知道为什么:
我们可以看到从0x0012ff00可以看到每个字符占两个字符,这一下结果正确了,一个API的理解不正确就可能导致不可预料的结果!比如上面的这个例子,我想可能引起的问题很多,因为涉及的字符的长度,很容易出问题的,看来写程序要更加细心的呀!