Windows程式設計_Chap02_Unicode_學習筆記
――By: Neicole(2013.05.24)
01. 開篇
《Windows程式設計》的第2章,主要內容為Unicode的曆史由來簡述(書本講得很好啊,一點不覺得枯燥,看上去更像是在講故事),以及寬字元在C語言中的使用,寬字元在Windows程式設計時的基礎知識及應用。書本的講述由淺入深,學得很輕鬆,不過在總結時候卻感覺這章做筆記沒有想像中輕鬆,如何將它們整理成一個個知識模組是一個最大的問題。通過總結,形成自己的一個知識模組,有自己的知識架構,我個人覺得這才是有意義的學習,學有所思,學有所獲。
02. Unicode由來及概括
前期:[1824年,盲文(6位編碼)]-> [1854年,電報代碼] -> [1903年,電傳碼(5位編碼)]
發展:[BCDIC,6位編碼] -> [EBCDIC,8位編碼] -> [ASCII,7位編碼]-> [DBCS,字元集內字元長度不統一,8位或16位] -> [Unicode,16位編碼,可代表65536個字元]
概括:Unicode是一種在電腦上使用的字元編碼,可實現跨語言、跨平台的文本轉換及處理。
注意:寬字元並不一定是Unicode,Unicode只是寬字元編碼的一種實現。
03. C語言中的wchar
“大寫字母L(表示長整型)緊接左引號”,這向編譯器表明這個字串將用寬字元儲存。
// wchar_t測試.cpp#include <iostream>#include <wchar.h>int main(){ // 在 WCHAR.H 中,typedef unsigned short wchar_t,因此,wchar_t資料類型和無符號整型一樣,都是16位寬。 // 定義 wchar_t c = 'A'; wchar_t c2 = L'A'; wchar_t * str = L"HaHa"; // 加L代表寬字元儲存 // 字串長度測量 // 測長不能用strlen(...),可以用wcslen(...),函數原型:size_t __cdecl wcslen(const wchar_t *); size_t strLen = wcslen(str); wchar_t arr[] = L"HaHa"; size_t arrStrLen = sizeof(arr) / sizeof(wchar_t) - 1 ; // 還有一位是結束符 return 0;}
04. Windows編程中的wchar
04.01在TCHAR.H標頭檔中,_UNICODE標識符的作用。
04.01.01 關於字串測長的函數名稱
#ifdef _UNICODE
#define _tcslen wcslen
#else
#define _tcslen strlen
#endif
04.01.02 關於字元是否採用寬字元編譯
#ifdef _UNICODE
#define __T(x) L##x
#else
#define __T(x) x
#endif
04.01.03 關於字串是否採用寬字元編譯
#ifdef _UNICODE
#define _T(x) __T(x)
#define _TEXT(x) __T(x)
#else
...
#endif
04.02 WINNT.H中的寬字元
04.02.01WINNT.H中CTYPE.H定義CHAR與WCHAR
typedef char CHAR
typedef wchar_t WCHAR
04.02.02 WINNT.H中指標類型
typedef CHAR * PCHAR, * LPCH, * PCH, * NPSTR, * LPSTR, *PSTR;
typedef CONST CHAR * LPCCH, * PCCH, * LPCSTR, * PCSTR;
typedef WCHAR * PWCHAR, * LPWCH, * PWCH, * NWPSTR, * LPWSTR, * PWSTR;
typedef CONST WCHAR * LPCWCH, * PCWCH, * LPCWSTR, * PCWSTR;
04.02.03 WINNT.H中沒有底線的UNICODE
#ifdef UNICODE
typedef WCHAR TCHAR, * PTCHAR;
typedef LPWSTR LPTCH, PTCH, PTSTR, LPTSTR;
typedef LPCWSTR LPCTSTR;
#else
typedef char TCHAR, * PTCHAR;
typedef LPSTR LPTCH, PTCH, PTSTR, LPTSTR;
typedef LPCSTR LPCTSTR;
#endif
04.02.04 WINNT.H中的TEXT
#ifdef UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif
#define TEXT(quote) __TEXT(quote)
04.03 WINUSER.H中的寬字元函數
04.03.01 MESSAGEBOX的函數原型
WINUSERAPI int WINAPI MessageBoxA(HWND hWnd,LPCSTR lpText, LPCSTR lpCaption, UNIT uType);
WINUSERAPI int WINAPI MessageBoxW(HWND hWnd,LPCSWTR lpText, LPCWSTR lpCaption, UNIT uType);
關鍵在於第二個參數和第三個參數的不同,它們是否接受寬字元。
04.03.02 MESSAGEBOX的選擇
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
05. Windows程式設計使用寬字元執行個體
05.01 效果
05.01 源碼
/*----------------------------------------------------- SCRNSIZE.C -- Displays screen size in a message box (c) Charles Petzold, 1998 -----------------------------------------------------*/#include <windows.h>#include <tchar.h> #include <stdio.h> int CDECL MessageBoxPrintf (TCHAR * szCaption, TCHAR * szFormat, ...){ TCHAR szBuffer [1024] ; va_list pArgList ; // The va_start macro (defined in STDARG.H) is usually equivalent to: // pArgList = (char *) &szFormat + sizeof (szFormat) ; va_start (pArgList, szFormat) ; // The last argument to wvsprintf points to the arguments _vsntprintf (szBuffer, sizeof (szBuffer) / sizeof (TCHAR), szFormat, pArgList) ; // The va_end macro just zeroes out pArgList for no good reason va_end (pArgList) ; return MessageBox (NULL, szBuffer, szCaption, 0) ;}int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { int cxScreen, cyScreen ; cxScreen = GetSystemMetrics (SM_CXSCREEN) ; cyScreen = GetSystemMetrics (SM_CYSCREEN) ; MessageBoxPrintf (TEXT ("ScrnSize"), TEXT ("The screen is %i pixels wide by %i pixels high."), cxScreen, cyScreen) ; return 0 ;}
05.02 解釋
要讀懂這程式並不困難,主要得弄懂CDECL是什麼,va_arg、va_start、va_end是什麼。主函數調用了庫函數擷取顯示器的寬度和高度,然後調用自訂函數,將寬度和高度轉化為TCHARSTR,再建立TCHAR字串,並調用MessageBox函數將結果顯示出來。
05.02.01 CDECL(來自MSDN)
瞭解__cdecl即可瞭解CDECL,__cdecl為Microsoft 專用,這是調用 C 和 C++ 程式的預設呼叫慣例。 由於調用方清理堆棧,可以執行 vararg 功能。
1.參數在列表中,從右至左調用。
2.堆棧維護職責,調用函數將從堆棧的參數。
3.名稱修飾約定, 底線 (_) 首碼的名稱,但,在匯出使用 C 連結的 __cdecl 功能。
4.大小寫轉換約定,不執行的大小寫轉換。
樣本:
//Example of the __cdecl keyword on function
int__cdecl system(const char *);
//Example of the __cdecl keyword on function pointer
typedefBOOL (__cdecl *funcname_ptr)(void * arg1, const char * arg2, DWORD flags, ...);
05.02.02 va_arg、va_start、va_end(來自MDSN)
執行個體:
// testArg.cpp#include <stdio.h>#include <stdarg.h>#include <Windows.h>void testArg ( int i, ...){ va_list argptr; va_start(argptr, i); while ( i--) { char *s = va_arg( argptr, char* );// 讀取參數 printf( "%s\n", s);// 輸出參數 } va_end(argptr);}int main(){ testArg( 3, "Ha", "HaHa", "HaHaHa" ); // 結果為六個Ha system("pause"); return 0;}
MSDN說明:
va_arg, va_end,和va_start宏提供訪問函數的參數時該函數採用的參數數目可變的可移植的方式。
va_start設定arg_ptr第一個參數列表中的選擇性參數傳遞給函數。 該參數arg_ptr必須具有va_list類型。該參數prev_param是前面的第一個選擇性參數,參數列表中所需的參數的名稱。如果prev_param與寄存器儲存類,該宏的行為未定義聲明。
va_start之前,必須使用va_arg用於第一次。
va_arg檢索一個值的type位置從arg_ptr和增量arg_ptr指向下一個參數的列表中,使用的大小type來確定下一個參數的開始位置。 va_arg可以是用於檢索參數列表中任意數量的函數內的時間。
已檢索所有參數後, va_end指標,將重設空。
原理:
typedefchar * va_list;
#define_INTSIZEOF(n) \
((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#defineva_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#defineva_arg(ap,t) \
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#defineva_end(ap) ( ap = (va_list)0 )
定義_INTSIZEOF(n)主要是為了某些需要記憶體的對齊的系統.C語言的函數是從右向左壓入堆棧的,圖(1)是函數的參數在堆棧中的分布位置.我們看到va_list被定義成char*,有一些平台或作業系統定義為void*.再看va_start的定義,定義為&v+_INTSIZEOF(v),而&v是固定參數在堆棧的地址,所以我們運行va_start(ap, v)以後,ap指向第一個可變參數在堆棧的地址。
06. 結尾語
其實呢,本章主要收穫是使用Unicode可以使程式更趨於國際化,若想使用寬字元時,include標頭檔TCHAR或者Windows都可以,定義UNICODE後,在用字串的時候用T宏。另外,如果想傳不定數目的參數,還可以使用Windows的CDECL,能不定數目,主要是因為它們連續儲存,可以按順序讀取。