1、宽窄字节简介:
什么是宽字节,什么是窄字节?对于新手来说可能比较迷糊,我当初学习的时候也是这样!之前学习了C语言/C++语言,使用的字符串指针就是 char* 类型,C++中的字符串是 string,内部也是对 char* 的封装,那么这些跟宽窄字节有什么关系呢?

其实最早的系统都是窄字节的,也就是我们很常用的 char 因为都是英文的,英文本身就26个字母,再加上其他的一些标点符号之类的,char 也能表示的下,无符号的 char 最多能表示 255个字符,对吧,所以足够用了!

随着,操作系统的国际化,比如:Windows 系统不仅有英文的,也有中文的,韩文的,日文的,所以原来用一个 char 来表示一个英文字符的方式已经无法表达中文的一个汉字了。汉字是很多的,好几万个,单纯的 char 的取值范围已经无法表达的下了。这时候有大牛就想到了,既然用一个char表示不下,那么就用2个char来表示一个汉字,这样就可以解决了,所以窄字节的表示方法就是数字、字母之类的仍然用一个char来表示,一个汉子或者全角字符使用2个 char 来表示。

没错,这样可以解决大多数问题,在中文的系统上能正常的显示中文,在日文的系统上也能正常的显示日文,但是如果把一个在中文系统上写的软件,界面上带有汉字的程序拿到一个日文的Windows操作系统上就会有问题了,乱码了,汉字无法正常显示,同理,把一个界面上带有日文或者韩文的软件拿到中文的系统上也显示乱码!

为了解决这个国际化的问题,微软在Windows操作系统中引入了宽字节的功能,即:Unicode,Unicode中规定任意一个字符都占用两个字节的存储空间,即2个char,不管是数字或者字母,还是一个汉字 都占用2个字节。用两个char难免不方面,所以微软直接使用一个新的类型:wchar_t,大家看起来比较陌生,不过他的原型实际上就是 unsigned short,这个大家比较熟悉吧,占用2个字节的存储空间。

所以,微软就是利用Unicode编码来解决这个国际化的问题!

2、操作系统及VS编译器对宽窄字节的编码支持:
①、Windows操作系统提供了两种类型的 API 函数,例如 MessageBox 函数,其实 MessageBox 他只是一个宏,他对应的两个版本的函数分别为:MessageBoxA 和 MessageBoxW,你在VS编译器中使用的时候系统会根据是否定义了_UNICODE 宏来进行判断当前工程使用的是宽字节的Unicode编码还是窄字节编码,根据这个来决定该使用哪个版本的函数!如果你的工程没有定义 _UNICODE 宏,也就是非Unicode编码,那么就使用窄字节的 MessageBoxA,如果定义了,那么就使用宽字节的 MessageBoxW,具体在代码中,右键找定义,见演示!

查看DLL中的导出函数可以使用 depends 这个工具来查看!

备注:Windows 2000 及其以后的 Xp、2003、Vista、Win7、Win8、Win10 等系统都是使用Unicode从头进行开发的,如果调用任何一个Windows API 函数并给它传递一个 ANSI 字符串,那么系统首先要将字符串转换成Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode来开发应用程序,就能够使你的应用程序更加高效的运行!

②、编译器对宽窄字节的支持:
VC++ 6.0 默认为窄字节编码,vs2005、vs2008、vs2010、vs2012、vs2013、vs2015、vs2017 等默认都是Unicode编码,当然可以进行工程的设置从而进行编码的转换,见演示!
备注:从vs2013开始,如果要让工程从默认的Unicode编码转到窄字节编码,需要安装vs2013的多字节补丁才行!

3、宽窄字符串的优缺点:
上面说了那么多,可能大家认为既然 Unicode 这么好,那以后我程序当中所有的字符串就都用Unicode的宽字节就行了吧?实际也不是的,宽字节也有缺点!
一般来说只是涉及到界面,或者是跟字符串操作相关的建议大家使用宽字节,其他地方还是可以用窄字节。因为宽字节的占用空间比窄字节多了一倍,所以如果是单纯在本机的话还好,如果是进行字符串的网络传输,那么传输量就会是窄字节的二倍,所以这块也不是说什么时候都一味的用Unicode宽字节。

那么,既然在同一个工程代码中,有的地方用宽字节,有的地方用窄字节,那么怎么来统一呢,这个时候肯定要进行宽窄字节的一个转换的工作了,不过还好,有多种方法可以进行转换,微软也为我们提供好了相应的 API 函数,我们直接拿过来用就可以了。关于转换这块我们之后会讲解的!

4、复杂的宽窄字节数据类型:
刚开始学编程的时候我们就接触了 char、char* 之类的,属于窄字节的,按照刚刚讲的,又多了一个 wchar_t 类型的字符,字符串指针的话就可以是 wchar_t* 类型。但是,在我们平时的编程过程中还会见到很多其他的复杂类型,如下:
● 窄字节:
char、char * 、const char *
CHAR、(PCHAR、PSTR、LPSTR)、LPCSTR

● Unicode 宽字节:
wchar_t、wchar_t * 、const wchar_t *
WCHAR、(PWCHAR、PWSTR、LPWSTR)、LPCWSTR

● T 通用类型:
TCHAR、(TCHAR * 、PTCHAR、PTSTR、LPTSTR)、LPCTSTR

其中:P代表指针的意思,STR代表字符串的意思,L是长指针的意思,在WIN32平台下可以忽略,C代表const常量的意思,W代表wide宽字节的意思,T大家可以理解为通用类型的意思,
就是可以根据工程中是否定义_UNICODE 宏,来判断当前工程的编码类型是宽字节还是窄字节,之后分别定义成不同的类型,比如:TCHAR 类型,如果工程中定义了_UNICODE 宏,那么就表明工程是宽字节编码,他最终就被定义成 wchar_t 类型,如果工程中没有定义_UNICODE 宏,就表明工程当前是窄字节编码,那么 TCHAR 被最终定义成 char 类型。

〓※※※〓 其方便性就是修改了工程的编码格式之后不用修改代码,所以还是建议大家在编写程序的时候使用通用类型!
sizeof 求宽窄字节字符串的注意事项:
char* p_str = "hello";
我想求这个字符串所占用的字节数:sizeof(p_str) 肯定是错误的!
strlen(p_str) + 1,实际上就是 p_str 指针指向的字符串所占用的空间对吧?因为本身1个char占用1个字节的存储空间,所以默认字符串的长度加上字符串的结束符 \0 就是字符串所占用的字节数。

那么宽字节呢?wchar_t* p_wstr = L"hello";
他占用的字节数应该如何来求?
wcslen(p_str) + 1 这个对吗?当然也是不对的,正确的是:(wcslen(p_str) + 1) * sizeof(wchar_t)

通用类型:TCHAR* p_tstr = _T("hello");
(_tcslen(p_tstr) + 1) * sizeof(TCHAR)

应用在哪里呢?比如:
TCHAR *pszBuf = new TCHAR[100];
定义完了之后,我要给这个字符串数组清空,于是我应该这么做:
memset(pszBuf, 0, 100 * sizeof(TCHAR)) 这样做是最安全的,不管当前的 TCHAR 是 char 也好,是 wchar_t 也好,都可以满足。

最后修改:2022 年 12 月 05 日
如果觉得我的文章对你有用,请随意赞赏