3.Unicode简介
历史
为了尽可能的涵盖全球所有语言的字符,Windows字符的占用由最初的ASCII的8位,演变为Unicode的统一的宽字符16位,65536个字符足以满足要求。(可是像这样??的符号表情在那个时候应该是没有的,所以字符集又被扩充了?颜色也没有随文字改变)前一篇文章的TEXT()宏就是将8位字符转换为16位。
16位也就是unsigned short,在C++中被定义为wchar_t类型
数据在内存中的表示方式
就int a = 0x12345678
而言,计算机以多个单个字节存储多字节数值,一个字节正好是16进制的两位。考虑到存储的,假设是小端,a在内存中的存储方式是这样的:
0x51FB4FF7E4 | 0x51FB4FF7E5 | 0x51FB4FF7E6 | 0x51FB4FF7E7 | 0x6addf6c7 |
0x78 | 0x56 | 0x34 | 0x12 | 0x12 |
vs调试工具
在VS中打上断点,在编辑区右击,点击【快速监视】,【表达式】区输入&a,点击右侧【重新计算】,复制&a的值并粘贴到【地址】
即可得a在内存中的存储方式
内存数据截取
单字节读取多字节
#include
int main()
{
int a = 0x12345678;
char *b = (char*)&a;//以单字节读取a的地址,与a的地址有关联
for (int i = 0; i < 5; i++)
{
printf("%#x\t",int( b + i)), printf("%d\n", *(b + i));
}
return 0;
}
在我的64位系统中,int类型占用4个字节,int *占用8个字节,(char*)&a以单个字节读取a的4个字节的数据的地址,并将内存的低位地址赋值给指针b。(好像还是挺乱的)
共用体
#include
union MyUnion
{
char A;
int B;
};
int main()
{
MyUnion myunion;
myunion.B = 0x12345678;
for (int i = 0; i < 4; i++)
{
printf("%#x\t", int(*(&(myunion.A) + i)));
}
return 0;
}
原因请看c语言之共用体union、枚举、大小端模式,
主要是因为给共用体成员间共享一块内存,其中一个成员赋值时,另一个成员同时被覆盖,但由于将int值赋值给char,只会将int上的一个字节数据覆盖到char上,同时共用体union的存放顺序是所有成员都从低地址开始存放的。
Unicode字符的存储方式
这里直接列出wchar_t str[] = L"hello";
的存储方式
16位的表示方式对于最初的ASCII字符而言,Unicode的高位上是0,导致以0作为结束位置的原C函数strlen
不能正确执行,所以Unicode宽字符有自己的一套函数,如wcslen
(wide-character string length:宽字符串长度),wprintf是printf的宽字符版
宽字符和单个字符数据的转换
考虑机器的支持与数据的占用,可能要采用宽字符和单个字符两种版本的数据,重写太过麻烦了,好在库函数事先定义好了。
如果定义了名为_UNICODE的标识符,并且程序中包含了TCHAR.H表头文件,那么_tcslen就定义为wcslen:
在VS的【项目】菜单下,点击【项目属性】,在【C/C++】选项下点击【预处理器】,在【预处理器定义】下就可以看到已经定义好UNICODE和_UNICODE,可以手动取消,这时函数的定义就变为单字节定义
#define _tcslen wcslen
如果没有定义UNICODE,则_tcslen定义为strlen:
#define _tcslen strlen
TCHAR.H还用一个新的数据型态TCHAR来解决两种字符数据型态的问题。如果定义了_UNICODE标识符,那么TCHAR就是wchar_t:
typedef wchar_t TCHAR ;
否则,TCHAR就是Char:
typedef char TCHAR ;
再谈TEXT()宏
查看TEXT()的定义
#ifdef UNICODE
/*若干代码*/
#define TEXT(quote) __TEXT(quote)
/*若干代码*/
#define __TEXT(quote) L##quote
在定义了UNICODE时TEXT()被解释为L##quote,否则被解释为quote,之间还使用了一个转接宏__TEXT()
宏定义只会展开当前宏,如果##右侧仍然有宏定义,不会展开,为了预防这种情况的发生,会添加一个转接宏。
例如:
#define A "2" #define MYTEXT(quote) L##quote int main() { wprintf(L"%s", MYTEXT(A));//扩展为LA,未声明的标识符,报错 return 0; }
有转接宏时,转接宏会先展开,MYTEXT(A) --->> __MYTEXT(2) --->>L##"2",正确输出
#define A "2" #define MYTEXT(quote) __MYTEXT(quote) #define __MYTEXT(quote) L##quote int main() { wprintf(L"%s", MYTEXT(A)); return 0; }
_T() ,_TEXT()宏和TEXT()具有相同的功能