设置项目的字符集所涉及到的UNICODE和_UNICODE宏的相关问题的说明分析,理解这个对于字符集就不在害怕了。
在tchar.h头文件中,定义了宏_UNICODE。
根据是否定义_UNICODE来判断使用了Unicode字符集,进而包含对应的字符集头文件,以及设定对应的函数版本。并定义__T(x)、_T(x)、_TEXT(x)宏方便使用字符串,而不用管是使用说明版本的函数和字符集。
节选定义部分如下:
#ifdef _UNICODE /* 定义了 _UNICODE */
#include <wchar.h>
#define _TEOF WEOF
#define __T(x) L ## x
#define _tmain wmain
#define _tWinMain wWinMain
#define _tprintf wprintf
#define _tscanf wscanf
#define _gettc getwc
#define _tcstod wcstod
#define _tcstol wcstol
#define _tcstoul wcstoul
#define _tfopen _wfopen
#define _tcsclen wcslen
#endif
#else /* 没定义 _UNICODE */
#include <string.h>
#define _TEOF EOF
#define __T(x) x
#define _tmain main
#define _tWinMain WinMain
#define _tprintf printf
#endif /* 结束 */
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
然而在windows.h头文件中包含的winuser.h头文件中,定义了Windows中使用的一些函数如MessageBox函数的ANSII版本和Unicode版本。
定义MessageBox函数版本如下:
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
从这里看出,这里定义的是UNICODE,而不是前面的_UNICODE宏。在VC6.0和VS中,如果要正常使用宏_T同时也要使用Windows中的函数,那么需要同时设置预处理器定义(Preprocessor definitions)为UNICODE和_UNICODE,或者设置字符集,会自动将两者设置上。建议使用设置字符集,而不要用预处理器定义。如下图所示。
从上面可以看出,_UNICODE宏用于定义C/C++语言中的定义,比如定义C语言库函数的版本之类的,而UNICODE则是定义Windows函数的版本。其实这两者,是有一个约定的,在非Windows函数中,定义宏一般使用_表示非Windows宏定义。这样作为区分标志。这种约定是非常常见的,你可以到stdio.h头文件中去看看。当然这只是一个约定,你也可以不遵守,但是你看到标准的微软代码中,从中可以看出差别,有时候是很有帮助的。
_UNICODE定义了_T宏,设置后,可以让此宏包含的字符串都是设定的字符集的字符。需要清楚的一点就是,在tchar.h中,并不会去定义_UNICODE宏,而只是去根据这个宏是否被定义,以确定项目使用的是什么字符集,从而使用对应版本的字符和函数。设置字符集在工程项目属性中设置。如果项目中设置的字符集是Unicode,那么_T代表的就是Unicode字符集,_txxx版本的函数代表的就是xxxW()的Unicode版本的函数。这样,不管项目的字符集如何改变,都不用更改代码。
如果此时,你没有设置宏UNICODE,那么在不使用Windows函数的情况下,没有任何问题。这都是自动正确匹配的。但是此时使用Windows的函数,如MessageBox函数,使用的则是根据宏UNICODE来判断得到的函数版本,如果定义了此宏,那么MessageBox就是Unicode版本,否则就是ANSII版本。
在预处理器中可以加入此宏,表示设置Unicode字符集。如果你没有加入,那么在使用MessageBox函数时,使用的是ANSII版本,这样的话,字符是占一个字节。遇到一个空字符即表示字符串结束。在定义了_UNICODE情况下,_T可以正常使用,没有问题。然后使用MessageBox就会发现只能显示一个字符。因为存入内存的是Unicode字符,英文则表现为第二个字节为0,所以只能显示一个字符。如果是中文,那么在ANSII字符表中找不到对应的字符,就出现乱码。这也是为什么推荐使用设置字符集的原因。
幸运的是,在VS中,项目属性里,设置字符集只需要更改使用的字符集即可同步设置这两个宏,就会保证,无论是C/C++环境里,还是Windows环境里,都能同时设置和取消,使用时也不会出问题。
最后总结一下,项目判断使用的是什么字符集,利用_UNICODE 和 UNICODE 宏是否被定义来判断的。如果设置了,表示使用的是UNICODE字符集,否则是非UNICODE字符集。_UNICODE用于非Windows环境中的函数等定义,而 UNICODE用于Windows环境中的函数定义,这是个约定。而定义这两个宏,则在预处理器指令中。可以直接填,也可以使用选择字符集即可自动填上。