Windows程序設計_Chap02_Unicode_學習筆記


    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,能不定數目,主要是因為它們連續存儲,可以按順序讀取。

 


 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com