绕死你不偿命的UNICODE、_UNICODE、__TEXT、__T、_T、_TEXT、TEXT宏
最近在看一些关于VC++和MFC的书时,书上对字符串的处理一般都会使用TEXT("a string")的形式或者_T("a string")的形式,自己写程序时MFC自动生成的代码中也有类似的宏。作为菜鸟,不加思考地照搬书上的TEXT()或者_T()不是我的风格,喜欢追根究底的性格促使我决定弄懂这些宏。但如果我按照以往写文章的习惯,跟着我思考的顺序来写这篇随笔的话,那是倒叙,会很不好写,所以我就按弄懂之后的正常顺序来写吧,但这也让这篇随笔看起来有点说教,各位看客且请忍受一下,谢谢。
C语言发明时尚没有UNICODE这一说,那时候米国人只有ASCII,但随着计算机的进化,程序中需要出现其它国家语言的字符,如中文,这远不是ASCII所能表示的,所以就出现了UNICODE(想深入了解UNICODE的童鞋,请猛击这里),于是C语言也新加了一个类型:wchar_t,用于表示UNICODE字符,其定义为:#define unsigned short wchar_t,说白了其实就是用16位双字节表示一个字符(ASCII用单字节表示一个字符)。
这样编写程序就出现了一个问题,我先定义了一个变量:char *str = "china";这当然没问题,但如果后来要把该字符串换成中文的,那就得换成wchar_t *str = L"中国";(顺便说一下,字符串前的L是告诉编译器,后面紧跟的字符串按UNICODE宽字符处理,即每个字符占两个字节)。如果字符串只有一个,改一下没问题,但很难想象一个程序中只出现一个字符串,所以,这样修改起来的工作量是很大的。
M$永远不会缺乏牛人,所以他们自然为VC++想好了解决办法,那就是__TEXT(),__T()等一系列宏,先来看看WinNT.h头文件,这个文件有几千行,但我们只需要抽取关键的几行出来:
#define __TEXT(quote) L##quote // r_winnt
#else /* UNICODE */ // r_winnt
#define __TEXT(quote) quote // r_winnt
#endif /* UNICODE */ // r_winnt
#define TEXT(quote) __TEXT(quote) // r_winnt
这几行代码应该很简单:如果定义了UNICODE宏,那么就将__TEXT(quote)宏定义为L##quote,如果没定义UNICODE宏,则__TEXT(quote)宏就是普通的quote,最后,再将TEXT(quote)宏定义为__TEXT(quote)宏。(如果有朋友不懂其中##符号的意思的话,这里解释一下,##起连接作用,例如__TEXT("china")在经过宏替换后就会被解释为L"china")
这种解决方法很巧妙,编程人员可以根据需要自由定义UNICODE宏来决定是使用ASCII字符串还是UNCODE字符串,而程序中的字符、字符串只需加个宏处理即可。与此类似的还有__T(),_TEXT()等宏,这些宏在tchar.h头文件里定义,这个文件同样有几千行,仍然只需要抽出关键的几行:
#define __T(x) L ## x
#else /* ndef _UNICODE */
#define __T(x) x
#endif /* _UNICODE */
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
其原理同上,但这里是根据_UNICODE宏(注意前面的下划线)来决定是使用ASCII字符还是UNICODE字符的。同样最后又附加了_T()和_TEXT()宏的定义。
到此,疑问又来了,为什么要定义_UNICODE和UNICODE两个宏?M$的牛人吃多了嫌撑?要是一个宏定义了,另一个宏没定义引起冲突怎么办?于是再来查找一番,在atldef.h和afxv_w32.h两个头文件中,我找到了一模一样的内容,如下:
#ifndef UNICODE
#define UNICODE // UNICODE is used by Windows headers
#endif
#endif
#ifdef UNICODE
#ifndef _UNICODE
#define _UNICODE // _UNICODE is used by C-runtime/MFC headers
#endif
#endif
这段代码有点绕,但目的很清晰,就是要保证UNICODE和_UNICODE这两个宏要么都定义了,要么都未定义,不能只定义一个。所以,在上面的分析中,不论是利用UNICODE宏来定义的__TEXT()和TEXT(),还是利用_UNICODE宏来定义的__T()、_T()和_TEXT(),都是可以正常使用的,不会出现一部分是ASCII字符串,另一部分是UNICODE字符串的低等错误。因此,__TEXT、__T、_T、_TEXT、TEXT这几个宏的具体作用其实是一样的,没有区别,至于M$为什么对相同的功能要搞这么多奇形怪状的符号来表示,那我就不得而知了。
同时,上面的注释还解释了,UNICODE宏用于Windows头文件,而_UNICODE宏用于C运行时和MFC的头文件,当然我这个菜鸟还不太懂具体区别,只能大概猜到,在Windows的头文件中,需要根据是否使用UNICODE来定义不同版本宏的地方就使用UNICODE宏,而在MFC和标准C中,需要根据是否使用UNICODE来定义不同版本宏的地方就使用_UNICODE宏。
另外,小弟真的很菜,尚不知为何关于UNICODE和_UNICODE这两个宏的纠缠在atldef.h和afxv_w32.h两个文件中都出现了,而且还一模一样,园子里高手众多,如果哪位知道这其中的奥秘,还望能给小弟指点一二,先行谢过。