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()具有相同的功能