摘要小结:
(
对于一个windows API函数来说,其函数名结尾跟上A的函数版本为ANSI字符版本函数,A不是代表ASCII之意
)char数据类型的变量不仅可以存储以ASCII码字符集编码的字符,也可以存储以多字节字符集(如简体中文字符集GBK)编码的字符。如char str[20]="asdfg",str2[20]="中文汉字";
wchar数据类型的变量可以存储以UNICODE字符集编码的字符,如wchar str[20]=L"asdfg",str2[20]=L"中文汉字"。
对于一个windows API函数来说,
它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)的参数里有一个使用字符串的参数,那么ASCII版本函数使用的字符串只能是char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符串,如char str[20]="asdfg";),而不能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串,如wchar str[20]=L"asdfg"。
它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W)则只能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串,而不能使用char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符串)。
注释:
1、确切地说是,char数据类型的变量可以存储以多字节字符集(如简体中文字符集GBK)编码的字符。而char数据类型的变量可以存储以ASCII码字符集编码的字符,是因为多字节字符集都是兼容ASCII码字符集的,即后者是前者中的一组成部分。
在char数据类型的变量里存储的字符是具体为哪种多字节字符集(如是以简体中文字符集GBK还是以韩文字符集来编码的)编码的,则是看(Windows)操作系统中的系统区域设置里选择的是什么字符集了。例如,
假设在项目A里的一源代码里有一个字符串为charstr2[20]="中文汉字";,当前(Windows)操作系统中的系统区域设置里选择的是简体中文字符集GBK,则此时编译该项目时字符串"中文汉字"就按照简体中文字符集GBK里的编码值存储(在内存上)。编译好后,又将(Windows)操作系统中的系统区域设置里改为日语代码页946(或是在编译好后所得的exe文件所运行的进程所打开的控制台窗口的属性里的默认值中修改代码页为代码页946(或是oem 437)),则此时运行编译好后所得的exe文件,在其打开的控制台窗口上显示就不是"中文汉字"这几个字符,而是一些乱码。
2、更确切地说,无论以何种字符集来编码一字符串,该字符串的编码值都可以存储在任何数据类型的变量上,前提是要将该字符串的编码值原原本本的复制到该变量上(而不经过一些数据类型间的隐式转换,即如char数据类型实质上是整型,只是读取解释为一个字符图形,即是整型Int,那其若要转换为float(形如0.39*e的5次方),则同样是5这个数值,存放在int变量里和存放在float变量里的值不一样。再例如,因为变量st的数据类型是filetime,filetime结构体中的两个变量是unsigned long类型的,而*(__int64 *)&st只是表示将(两)unsigned long类型(合并)的变量st的数据按照__int64(=longlong)来解释读取而已,并未实际将(两)unsigned long类型(合并)的变量st的数据转换成__int64(=longlong)数据类型格式的,所以__int64 d = *(__int64 *)&st;应该改为__int64 d =(__int64)st;参见:FileTime如何转换为Time_t)。所以说,无论以何种字符集来编码一字符串,该字符串的编码值既可以存储在char数据类型的变量上,也可以存储在wchar数据类型的变量上。(此处说明可以用于解:utf8编码的字符(如汉字 占三个字节)用啥字符数据类型储存 C++)例如,以Unicode字符集编码的字符串“我是中国人”可以存储在char数据类型的变量上,以多字节字符集(如简体中文字符集GBK)编码的字符串“我是中国人”可以存储在wchar数据类型的变量上。所以,wchar数据类型和char数据类型的区别不在于它们能存放什么字符集编码的字符串。而wchar数据类型和char数据类型的区别在于它们能正确读取显示什么字符集编码的字符串,即它们能用哪些字符集来读取解释显示存放在其上的编码值。那两者分别能用什么字符集来读取解释显示存放在其上的编码值呢?wchar数据类型只能用Unicode字符集来读取解释显示存放在其上的编码值,而char数据类型则是看(Windows)操作系统中的系统区域设置里选择的是什么字符集了,假如当前(Windows)操作系统中的系统区域设置里选择的是UTF8字符集,则char数据类型当前可以用UTF8字符集来读取解释显示存放在其上的编码值,假如当前(Windows)操作系统中的系统区域设置里选择的是简体中文字符集GBK(即一种多字节字符集),则char数据类型当前可以用简体中文字符集GBK来读取解释显示存放在其上的编码值,假如当前(Windows)操作系统中的系统区域设置里选择的是ASCII字符集(即一种单字节字符集),则char数据类型当前可以用ASCII字符集来读取解释显示存放在其上的编码值,即char数据类型可以根据(Windows)操作系统中的系统区域设置里选择的字符集来显示除了Unicode字符集以外的任何一种字符集,而wchar数据类型只能用Unicode字符集来读取解释显示存放在其上的编码值。所以,例如,以Unicode字符集编码的字符串“我是中国人”虽然可以存储在char数据类型的变量上,但是显示时会是乱码,以UTF8字符集或是多字节字符集(如简体中文字符集GBK)编码的字符串“我是中国人”虽然可以存储在wchar数据类型的变量上,但是显示时会是乱码。
当然,像char str[20]="sdf中国"或是wchar str[20]="sdf中国"中要复制给数组str的值"sdf中国"是根据当前(Windows)操作系统中的系统区域设置里选择的字符集来编码的,而像char str[20]=L"sdf中国"或是wchar str[20]=L"sdf中国"中要复制给数组str的值"sdf中国"是根据Unicode字符集来编码的(因为字符串"sdf中国"前有个L这个标志,意即叫编译器用Unicode字符集来编码字符串)。同一个字符串的不同字符集编码值间的转换可以用如下两个函数:
例如,
宽字符到多字节字符转换函数,函数原型如下:
此函数把宽字符串转换成指定的新的字符串,如ANSI,UTF8等。参数:
我想最常用的应该是CP_ACP和CP_UTF8了,前者将宽字符转换为ANSI,后者转换为UTF8。
3、
对于一个windows API函数来说,
它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)的参数里有一个使用字符串的参数,那么ASCII版本函数使用的字符串的编码值可以是单字节字符集(如,ASCII码字符集)也可以是多字节字符集编码的也可以是UTF8字符集编码的(反正是除了Unicode字符集之外的其他字符集),只是不能使用unicode字符集编码的。它的ASCII版本函数当前具体能处理什么字符集也是要看(Windows)操作系统中的系统区域设置里选择的是什么字符集(?)。
上述这种windows API函数是有预定义的宏来做函数各版本间选择的开关的一种函数封装的形式。而类似strcpy函数以及它的宽字符版本wcscpy和多字节字符集版本_mbcscpy的一组函数区别于上述所说的windows API函数就是它没有有预定义的宏来做函数各版本间选择的开关将各个版本函数组织起来对外形成一个统一的接口函数。
对于类似strcpy函数以及它的宽字符版本wcscpy和多字节字符集版本_mbcscpy来说,
strcpy函数只能处理单字节字符集ASCII;
它的宽字符版本wcscpy只能处理Unicode字符集;
多字节字符集版本_mbcscpy来说只能处理多字节字符集(不知道能否处理如UTF8字符集)。
4、
vs2010中一个项目的属性的字符集选项的作用就是起一个开关选择的作用,即windows API函数实际使用哪个函数版本以及TCHAR数据类型实际使用哪个具体版本的数据类型(即是char还是wchar)。
5、
编译(或进程运行前的准备时期)时 对变量的初始化,也就是说可执行文件(即编译后的代码里),含有这些初始值。例如,源码里一字符串的初始值为“中国”,编译时,操作系统的字符集为gbk,则该初始值“中国”就按其在gbk中的编码值存放在可执行文件里。每个初始值根据自己书写方式不同有自己默认的数据类型 可不同于它要初始化的变量的类型。如整数值默认为int l带为long
wchar char都是一个字符数据类型,只不过用了不同字符集来解读
运行时 才对变量赋值
初始化值是以常量(里值不可改变修改)的形式的内存空间存放在程序全局区内存部分。例如,char *P=“fffDHC”;在一函数里,函数返回值是P的值,函数外用该值依然可以访问该字符串常量,说明该常量在全局区。
淡然,没有 int *P=&5
虽然5这个常量也在代码区(或是编译时分配其到全局区),但是编译时5没有常量的地址值这个属性
参考:
编译原理
编译器编译时会对代码做各种隐式转化什么的(如初始化或是赋值的数据类型隐式转化)
正文:
对于一个windows API函数最后实际是用它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)还是用它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W),取决于相关设置。
相关设置有两种方式。一种影响范围为整个项目,一种影响范围为一个(含有#include"stdafx.h"的)cpp文件。
一种影响范围为整个项目:
选中一个项目,右键点击属性,跳出窗口里选择字符集来设置。
一种影响范围为一个(含有#include"stdafx.h"的)cpp文件:
假设当前项目-》属性-》字符集里的值为“使用unicode字符集”,而你想在项目里的某些cpp文件上使用多字节字符集编码的字符串,则你可以在stdafx.h里写上一语句:#undefUNICODE,并在那些要使用多字节字符集编码的字符串得cpp文件上写上一语句:#include"stdafx.h"即可。
那么你如何知道刚才的设置已经起作用了呢?
两种方法:
1、 如果你装了VX插件,则鼠标移到一windowsAPI函数上时会有如下显示:
2、 在windowsAPI函数上右键选“转到定义”之后看到当前不是灰色的函数版本即为起作用的函数版本:
假设当前项目-》属性-》字符集里的值为“使用多字节字符集”,而你想在项目里的某些cpp文件上使用unicode字符集编码的字符串,则你可以在stdafx.h里写上一语句:#defineUNICODE,并在那些要使用多字节字符集编码的字符串得cpp文件上写上一语句:#include"stdafx.h"即可。
附加:
1、
至于像#defineUNICODE或是#undefUNICODE一定要放在stdafx.h里才起作用,而直接放在那些要使用多字节字符集编码的字符串得cpp文件上不起作用的原因暂时不知了。反正,程序员自己在cpp文件上定义的宏是可以在该cpp文件上起作用的。例如,在一cpp文件上
// #define ttttt
//
#ifdefttttt
intG_r=5;//没定义宏ttttt时该句就是灰色的,有定义则不是灰的,这说明定义的宏起作用了。
#endif
2、
#ifdefUNICODE
#defineGetUserName GetUserNameW
#else
#defineGetUserName GetUserNameA
#endif// !UNICODE
从上述定义模板设计可以看出,对于一个windows API函数要用它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)还是用它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W),是利用定义或是取消定义宏UNICODE,而定义或是取消定义宏MBCS之类的名字不起作用的。
3、
对于在tchar.h里定义的TCHAR数据类型,上述第一种方法对其是起作用的:
如果你装了VX插件,
假设当前项目-》属性-》字符集里的值为“使用多字节字符集”,则鼠标移到TCHAR数据类型上时会有如下显示:
typedefchar TCHAR;
假设当前项目-》属性-》字符集里的值为“使用unicode字符集”,则鼠标移到TCHAR数据类型上时会有如下显示:
typedefwchar_t TCHAR;
上述第二种方法对在tchar.h里定义的TCHAR数据类型是不起作用的,原因暂时不知。原因应该是
在 tchar.h 头文件里,使用 _UNCODE 定义而不是使用UNCODE 定义:
#ifdef _UNICODE
... ...
#define __T(x) L##x /* for UNICODE */
... ...
#else
... ...
#define __T(x) x /* for ANSI */
... ...
#endif
#define _T(x) __T(x)
上述第二种方法只要改为定义或是取消定义宏_UNCODE即可起作用。
参见:
4、
char数据类型的变量不仅可以存储以ASCII码字符集编码的字符,也可以存储以多字节字符集(如简体中文字符集GBK)编码的字符。如char str[20]="asdfg",str2[20]="中文汉字";
wchar数据类型的变量可以存储以UNICODE字符集编码的字符,如wchar str[20]=L"asdfg",str2[20]=L"中文汉字"。
对于一个windows API函数来说,
它的ASCII版本函数(即该windows API函数的函数名结尾跟上A)的参数里有一个使用字符串的参数,那么ASCII版本函数使用的字符串只能是char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符串,如char str[20]="asdfg";),而不能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串,如wchar str[20]=L"asdfg"。
它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W)则只能使用unicode字符集编码的字符串,即wchar数据类型定义的字符串,而不能使用char数据类型定义的字符串(即单字节字符集(如,ASCII码字符集)或是多字节字符集编码的字符串)。
不要以为当前项目-》属性-》字符集里的值为“使用unicode字符集”后,该项目里出现的字符串就都是unicode字符集编码的字符串了。字符串是由哪种字符集编码的,取决于定义该字符串的数据类型是char数据类型还是wchar数据类型。
当前项目-》属性-》字符集,设置该字符集的值,会对源代码的那些对象有影响?对windows API函数和TCHAR数据类型有影响,因为windows API函数和TCHAR数据类型相当于一个开关函数对象。当前项目-》属性-》字符集里的值为“使用unicode字符集”时,windows API函数实际使用它的宽字符(即unicode)版本函数(即该windows API函数的函数名结尾跟上W),TCHAR数据类型实际使用它的wchar数据类型版本;当前项目-》属性-》字符集里的值为“使用多字节字符集”时,windows API函数实际使用它的ASCII版本函数(即该windows API函数的函数名结尾跟上A),TCHAR数据类型实际使用它的char数据类型版本。所以,在源代码里,windows API函数和TCHAR数据类型两者要配套起来使用,出现windows API函数时,如果它要使用字符串就要用TCHAR数据类型定义该字符串。
例如,
TCHAR szUser[80]=T("s");//T()这个宏也是起个开关选择作用,即当前项目-》属性-》字符集里的值为“使用unicode字符集”时,T()里的字符串就是用Unicode字符集编码的;当前项目-》属性-》字符集里的值为“使用多字节字符集”时,T()里的字符串就是用多字节字符集编码的。
DWORD cbUser=80;
getUserName(szUser,&cbUser);
再例如,
假设当前项目-》属性-》字符集里的值为“使用unicode字符集”时,
CHAR szUser[80];
DWORD cbUser=80;
getUserName(szUser,&cbUser);
运行上述代码就会提示错误,
因为函数getUserName此时实际用的是getUserNameW,而该getUserNameW函数不能使用char类型的字符串,只能用wchar类型的。
注释:
CHAR szUser[80];
DWORD cbUser=80;
getUserName(szUser,&cbUser);
第二个参数cbUser是一个输入输出参数,即它既是输入参数又是输出参数。首先,它在函数里起的作用是输入参数,作为输入参数,它的作用是告诉函数数组szUser的大小多大,故而cbuser的初始值可以小于80只要不大于数组实际大小即是安全的,虽然大于数组实际大小,函数也不一定使用那么多,所以也不一定出错函数执行时。
最后,它作为一个输出参数时,起的作用是当函数运行好后函数将用户名这个字符串的长度(即szuser实际被使用的大小)保存在参数cbuser中。
参考: