VC6.0中关于字符编码的问题(UNICODE和ASCII)

8488 人浏览 | 时间: 2015-06-27 19:27:58 | 作者: superb
    在TCHAR.H头文件中总体的结构如下:
/* For backwards compatibility */为了向后的兼容性
#ifdefine _UNICODE     //如果定义了_UNICODE标识符,字符或字符串则按照宽字符集进行编码
#ifdef __cplusplus
}   /* ... extern "C" */
#endif  /* __cplusplus */
/* ++++++++++++++++++++ UNICODE ++++++++++++++++++++ */
#include <wchar.h>     //包含宽字符头文件
#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus */
#ifndef _WCTYPE_T_DEFINED
typedef unsigned short wint_t ;
typedef unsigned short wctype_t ;
#define _WCTYPE_T_DEFINED
#endif  /* _WCTYPE_T_DEFINED */
#ifndef __TCHAR_DEFINED
typedef wchar_t      _TCHAR;
typedef wchar_t      _TSCHAR;
typedef wchar_t      _TUCHAR;
typedef wchar_t      _TXCHAR;
typedef wint_t       _TINT;
#define __TCHAR_DEFINED
#endif  /* __TCHAR_DEFINED */
#define __T (x )      L ## x           //将x字符或字符串按照宽字符进行存储,L表示宽字符,##表示L和x字符中间没有空格被称为"令牌粘贴"。
#define _tcslen          wcslen        //将求宽字符长度的函数定位_tcslen
#define _tprintf         wprintf      //将宽字符输出函数定义为_tprintf
...
#define _tscanf          wscanf      //将宽字符输入函数定义为_tscanf
#else //以下就是在没有定义_UNICODE标识符的时候,字符或字符串将按照ASCIII编码方式存储或者多字节字符集进行存储

/* ++++++++++++++++++++ SBCS and MBCS ++++++++++++++++++++ */
#ifdef __cplusplus
}   /* ... extern "C" */
#endif  /* __cplusplus */
#include <string.h>   //包含字符串头文件
#ifdef __cplusplus

extern "C" {

#endif  /* __cplusplus */
#define _TEOF        EOF
#define __T (x )      x     //  _T将字符或者字符串x按照单字节字符存储
/* Program */
#define _tmain       main
#define _tWinMain    WinMain
#define _tenviron   _environ
#define __targv      __argv

/* Formatted i/o */
#define _tprintf     printf   //将_tprintf按照printf函数进行输出
#define _tscanf     scanf   //将_tscanf   按照 scanf函数进行输入
/* "logical-character" mappings */
#define _tcsclen         strlen    //将_tcslen按照strlen来求得字符串的长度 
#define _tcscnlen        strnlen
...
..
#endif

以上就是TCHAR.H为兼容UNICODE字符集而定义的头文件,头文件整体结构可以归纳为
#ifdef  _UNICODE
     将通用函数解释为处理宽字符的函数 ;
#else
     将通用函数解释为处理单字节字符的函数或者说是处理ASCII码字符的函数;
#endif

和定义_UNICODE不同的是,在头文件WinUser.h(主要包含用户界面函数)中,如果定义了标识符UNICODE。
WINUSERAPI
int
WINAPI
MessageBoxA(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCSTR lpText,
    _In_opt_ LPCSTR lpCaption,
    _In_ UINT uType);
WINUSERAPI
int
WINAPI
MessageBoxW(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCWSTR lpText,
    _In_opt_ LPCWSTR lpCaption,
    _In_ UINT uType);
#ifdef UNICODE
#define MessageBox   MessageBoxW
#else
#define MessageBox   MessageBoxA
#endif // !UNICODE
    下面很多还有同样的定义
    //如果定义了UNICODE标识符,则MessageBox()函数将按照MessageBoxW()进行解析输出,即按照宽字符进行输出。否则按照MessageBoxA()函数进行输出,即按照ASCII码或多字节字符集进行输出 。
-------------------------------------------------------------------------------------------
问题就来了:
    在Microsoft Visual Studio 6.0开发环境中,如果使用MessageBox()函数,比如下列程序:
#include<windows.h> 
#include<tchar.h>
 int WINAPI WinMain(
HINSTANCE hInstance, 
HINSTANCE hPrevInstance, 
PSTR szCmdline,
 int iShowCmd) 
{ 
    MessageBox(NULL, _T("Hello Windows NT!"), _T("HelloMsg"),MB_YESNOCANCELL); 
    return 0; 
}

1、在”工程项目“中设置属性,C/C++选项下的Preprocessor definition中,默认是按照多字节字符集进行编码的如图,即没有定义_UNICODE和UNICODE,所以_T(x)将按照x进行编码存储,即按照ASCII码。    MessageBox将按照MessageBoxA()进行输出,即按照ASCII码。 所以他们的编码表是一致的,所以输出正常。

2、如果在Preprocessor definition中添加UNICODE或者_UNICODE中的任何一个则上面的程序就出现乱码或者显示不完全的现象。
    如果添加UNICODE:
    则MessageBox将按照MessageBoxW进行输出,即宽字符输出,但是_T(x)由于没有定义_UNICODE,所以依然按照ASCII进行编码即为x,两个字码表不一致           
    所以就会出现乱码。
    如果添加_UNICODE:(注意前面有下划线)
    则MessageBox()将按照MessageBoxA进行输出,即ASCII或多字节字符集,但是_T(x)会按照L##x,即宽字符进行编码,即一个字符占用两个字节,低字节在前,高字节在后,所以在输出字符时候,比如编码为0x0041,而存储的顺序则为0x41,0x00,所以MessageBox再按照ASCII码进行输出时只会输出一个字符而把0x00当做字符串结束标识。所以最终只会输出一个字符。

    因此在VC++6.0中需要将两个同时添加到里面才会显示正常,如图。


     相比vs以后产品,比如vs2010,vs2013中,字符集中的”使用UNICODE字符集“,则表示已经同时将两个标识符添加到里面了。

    以上可以看到UNICODE和_UNICODE的区别,_UNICODE的定义是在tchar.h头文件中,这个头文件并不是ANSI C标准的一部分,所以其中定义的每一个函数和宏都有一个下划线前缀。也就是说在在非windows环境下同样可以使用,而UNICODE是定义在头文件WinUser.h头文件中,这个头文件是windows中的一个重要头文件,windows中重要的头文件有:windef.h   winnt.h  winbase.h   winuser.h   wingdi.h 。  所以UNICODE是windows环境下使用的。

    注:在群主的指点下明白了,上述问题的原因,在此感谢群主的帮助,文章有点乱,希望对初学者有帮助,不喜勿喷!

相关阅读