MARK : Windows 程序设计
1.双字节字符集和Unicode的区别:
双字节其实只是ASCII的扩展,用扩展出来的代码表示其他的非ASCII字符;Unicode则不同,在Unicode中,ASCII中的字符也是用两个字节表示的,它是一个完整的字符集,不像双字节字符集那样只是一个ASCII的扩展。
2.char的数据形态
char c = 'A'; //变量C需要用一个字节来保存
char* p = "Hello!"; //变量p指针需要用32位的指针来保存(32位系统指针),字符串本身需要6个字节,加上一个末尾的字符串终止号0;总共占用的空间是4+6+1 = 11个字节
static char a[] = "Hello!"; //数组需要用6位的数据和1位的结束符。用sizeof察看就可得知,这是7个字节;如果你不需要结束符,那是不行的。(sizeof查看的是所占的字节数)
3.宽字符
Unicode或者宽字符都没有改变char数据形态在C中的含义。char继续表示1个字节的存储空间,sizeof返回1。
在wchar.h中定义了宽字符typedef unsigned short wchar_t;
所以,wchar_t数据型态与无符号短整数形态相同,都是16位宽。
wchar_t c = 'A'; //返回的是2
wchar_t* p = L"Hello!"; //注意那个大写的L[long],那是必须加的,告诉编译器,该字符串按宽字符保存,每个字符占用2个字节。4+14 =18个字节
static wchar_t a[] = L"Hello!"; //占用14个字节
4.宽字符链接库函数
char* pc = "Hello!"; //strlen(pc); 得到的结果是6,也就是字符串中的字符数,注意和sizeof不同哦
wchar_t* pc = "Hello!"; //此时就会发生一个错误,因为strlen只接受每个字符占一个字节的字符串
通过上面就发现,必须要有基于双字节的字符串处理函数
strlen函数的宽字符版wcslen(wide-character string length),并在STRING.H和WCHAR.H中说明。
size_t_cdecl strlen(const char*);
size_t_cdecl wcslen(const wchar_t*);
5.维护单一原始码
写程序的时候也许你在想,建立两个版本的程序,一个处理ASCII字符串,另一个处理Unicode字符串,提出了问题,我们来解决问题:
一个办法是使用TCHAR.H表头文件,这个表头文件中,每个函数和宏定义的前面都有一条下划线。TCHAR.H为需要字符串参数的标准执行时期链接库函数提供一系列的替代名称,其实这些函数名称及可以指向Unicode版也可以指向非Unicode版。
如果定义了_UNICODE的标识符,并且程序中包含了TCHAR.H表头文件,那么_tcslen就定义为wcslen,如果没有定义,则_tcslen定义为strlen,原理就是这样
还有为了解决数据类型的问题,引入了一个新的数据类型TCHAR,如果定义了_UNICODE,那么TCHAR就是wchar_t,如果没有定义,则为char。数据类型解决了,函数名称解决了,还有一个"L",没有解决,这里用一个宏解决了问题 #define __T(x) L##x,如果没有定义_UNICODE则,#define __T(x) x;如果定义了_UNICODE,则 #define _T(x) __T(x)或者#define _TEXT(x) __T(x),这两个含义是一样的,写法不同。
6.宽字符和Windows
Windows NT从底层支持Unicode,但是因为世界上许多地方不使用16位字符串,所以Windows NT必须做字符串在操作系统内转换,所以Windows NT可以执行ASCII,Unicode或者二者混合编写的程序。
Windows CE只支持Unicode,不支持ASCII。
7.Windows表头文件类型
Windows.h中包含WinNT.h,WinNT.H中处理基本的Unicode,WinNT.h中又包含了CTYPE.h,其中定义了新的数据类型
typedef char CHAR;
typedef wchar_t WCHAR; //wc
这其实就是为了程序设计的标准化,当我们需要定义8位或16位字符时,尽量使用CHAR,WCHAR,定义WCHAR数据类型的变量可以在前面附加wc以说明一个宽字符。
数据类型指针:
typedef CHAR *PCHAR,*LPCH,*PCH,*NPSTR,*LPSTR,*PSTR; //N代表Near,其实在Win32中near和long指标没有区别
typedef CONST CHAR *LPCCH,*PCCH,*LPCSTR,*PCSTR; //L代表long,p指针,C代表CONST
typedef WCHAR *PWCHAR,*LPWCH,*PWCH,*NWPSTR,*LPWSTR,*PWSTR;
typedef CONST WCHAR *LPCWCH,*PCWCH,*LPCWSTR,*PCWSTR;
虽然有了他们,但是我们在写程序的时候为了实现单一源码的效果,我们需要将CHAR,WCHAR进行统一,就像TChar.h中一样,WinNT.h中如下定义(注意:是UNICODE,而不是_UNICODE):
#ifdef UNICODE
typedef WCHAR TCHAR,*PTCHAR;
typedef LPWSTR LPTCH,PTCH,PTSTR,LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef char TCHAR,*PTHCAR;
typedef LPSTR LPTCH,PTHC,PTSTR,LPTSTR;
typedef LPCSTR LPCTSTR;
#endif
当然也少不了"L"宏
#define __TEXT(quote) L##quote
如果没有定义UNICODE,则像这样定义_TEXT宏:#define __TEXT(quote) quote
如果定义了UNICODE,则#define TEXT(quote) __TEXT(quote)
8.Windows函数呼叫
在USER32.DLL中,没有32位MessageBox函数入口点,实际上有两个进入点一个MessageBoxA(ASCII版),另一个MessageBoxW(宽字符版)。
二者主要的区别是在text和caption的参数上MessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);MessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
但是事实上,我们都是使用的MessageBox这个函数,其实这是在WinUser.h中完成的技巧:
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
函数呼叫少不了字符串函数
pString = lstrcpy(pString1,pString2);
pString = lstrcpyn(pString1,pString2,iCount);
pString = lstrcat(pString1,pString2);
iComp = lstrcmp(pString1,pString2);
iComp = lstrcmpi(pString1,pString2);
代码解释 /
#include <iostream.h>
#include <wchar.h>
#include <string.h>
void main()
{
char a = 'A'; // a 1 Byte
char* p = "Hello!"; // p 4 Bytes , "Hello!" 7 Bytes
char ar[] = "Hello!"; // ar 7 Bytes
/*. 宽字符
* C 中的宽字符基于 wchar_t 数据型态,它在几个表头文件包括 WCHAR.H 中都有定义:
* typedef unsigned short wchar_t;
* 因此,wchar_t 数据型态与无符号短整型相同,都是 16 位宽
*/
wchar_t b = 'A'; // b 2 Bytes
wchar_t* pb = L"Hello!"; // pb 4 Bytes , "Hello!" 14 Bytes.
//----------------------------------------------------------------------------
/*. 宽链接库函数
* wcslen [wide-character string length]
*/
char* pc = "Hello!";
int iLength = strlen(pc); // iLength = 6 .
wchar_t* pcw = L"Hello!";
int iLengthW = wcslen(pcw); // iLengthW = 6 .
//----------------------------------------------------------------------------
/*. 维护单一源码
* 如果定义了名为 _UNICODE 的标示符,并且程序中包含了 TCHAR.H 表头文件,
*
* #ifdef _UNICODE
* #define _tcslen wcslen
* #else
* #define _tcslen strlen
* #endif
*
* 这样就解决了单一源码问题
*
* TCHAR.H 还用一个新的数据型态 TCHAR 来解决两种数据类型的问题
*
* #ifdef _UNICODE
* typedef wchar_t TCHAR
* #else
* typedef char TCHAR
* #endif
*/
//----------------------------------------------------------------------------
/*. 宽字符和Windows
* 一个 Windows 程序包括表头文件 Windows.h 该文件包括许多表头文件,包括 WinDef.h
* WinDef.h 定义了许多在 Windows 中使用的基本形态定义,而且本身也包括 WinNT.h
* WinNT.h 处理基本的 Unicode 支持,WinNT.h 定义了新的数据类型 CHAR , WCHAR
*
* typedef char CHAR;
* typedef wchar_t WCHAR;
*
* 至此,我们有了数据类型 CHAR(一个8bit的char)和WCHAR(一个16位的wchar_t)
* WinNT.H 中还定义了一个宏
*
* #ifdef UNICODE //注意,WinNT.H 中是 UNICODE ,TCHAR.H中是 _UNICODE
* #define __TEXT(quote) L##quote //##是粘贴符,相当于连接符,起连接作用
* #else
* #define __TEXT(quote) quote
* #endif
* #define TEXT(quote) __TEXT(quote)
*/
//----------------------------------------------------------------------------
/*. Windows 函数呼叫
* MessageBox , Windows 支持 Unicode 的关键:在USER32.DLL中,没有 32 位
* MessageBox 函数的入口点,而是由两个 MessageBoxA(ASCII) 和 MessageBoxW(宽)
* MessageBoxA 和 MessageBoxW 在 WinUser.h 中定义
*
* #ifdef UNICODE
* #define MessageBox MessageBoxW
* #else
* #define MessageBox MessageBoxA
* #endif
*/
}
/ 示例 ///
#define UNICODE UNICODE
#include <Windows.h>
#include <stdio.h>
int CDECL MessageBoxPrintf(TCHAR* szCaption, TCHAR* szFormat, ...)
{
TCHAR szBuffer [1024] ;
va_list pArgList ;
va_start(pArgList, szFormat) ;
_vsnwprintf( szBuffer, sizeof (szBuffer) / sizeof (TCHAR),
szFormat, pArgList) ;
//va_arg(pArgList, int);
va_end (pArgList) ;
return MessageBox (NULL, szBuffer, szCaption, 0) ;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)
{
int cxScreen, cyScreen;
cxScreen = GetSystemMetrics(SM_CXSCREEN);
cyScreen = GetSystemMetrics(SM_CYSCREEN);
MessageBoxPrintf(TEXT("Screen Size"),
TEXT("The Screen is %i Pixels Wide By %i Pixels High"), cxScreen, cyScreen);
return 0;
}
// _vsnwprintf 函数是用一个指向变参列表的指针格式化一段输出,Write formatted output using a pointer to a list of arguments. 第一个参数是接收输出的空间,第二个参数是空间的大小,第三个参数是格式字符串,第四个参数是指向变参列表的指针
// 宏 va_list, va_start, va_arg, va_end
// va_list 是 C 语言中解决变参问题的一组宏,包含在 stdio.h 和 stdarg.h 中
// 1.首先在函数里定义一个 va_list 变量,这个变量是指向参数的指针
// 2.然后用 va_start 宏初始化 va_list,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定参数
// 3.然后用 va_arg 返回可变参数,va_arg 的第二个参数是你要返回的参数类型,va_arg 返回之后,将指向参数的指针指向下一个参数
// 4.最后用 va_end 宏结束可变参数的获取
// 小结:我们写一个可变参数的C函数时,有利也有弊,所以在不必要的 场合,我们无需用到可变参数,如果在C++里,我们应该利用C++多态性来实现可变参数的功能,尽量避免用C语言的方式来实现。