API入門系列之三 -那迷惑人的Windows字符和字符指針類型


原創文章,轉載請註明作者及出處。

首發

http://blog.csdn.net/beyondcode

http://www.cnblogs.com/beyond-code/

http://hi.baidu.com/beyondcode

 

大家好,通過前面兩篇打頭文章,我也看了留言,感謝那些給我提意見的人和指出錯誤之處的人。再次謝謝你們的支持。 另外,Windows SDK編程交流羣已經建立了,歡迎各位志同道合者加入進行交流( 羣號:81543028 )

本打算通過前面兩篇文章的講解,後來的系列就可以通過使用一些簡單的,常用的API寫一些示例程序的講解進行,但是發現還有一個不得不先講一講的要點,Windows下和字符串操作有關的數據類型。我看留言中也有幾位朋友提到了,那我就在這篇中講它吧。不會很枯燥的,各位慢慢看下去就是了。

 

下面我羅列一些我們在Windows平臺下編程經常使用到的和字符或字符串有關的數據類型。

char  和  wchar_t 

這兩個類型大家絕對不會陌生吧,一個是單字節的字符類型,一個是寬字節的字符類型(也就是Unicode字符)。

char   c = 'b';

wcha_t  wc = L'b';

上面我就分別定義了2個變量c和wc ,相信第一個定義大家都看的懂,就是定一個字符變量c,其中保存了'b'這個字符。 那麼第二個呢?  我相信還是很多人都看的懂,要是你看不懂也沒關係,現在就告訴你,也是定義一個字符變量wc, 只不過這個字符變量是Unicode字符變量,用2個字節來保存一個字符,而上面的c這個字符變量只有一個字節來保存,那麼在'b'前面的L又是什麼意思呢,它就表示這裏的'b'這個字符是一個Unicode字符,所以第二個定義的意思就是將L'b'這個Unicode字符保存到wc這個Unicode字符變量中。

如果我要定義一個字符數組怎麼定義呢? 用分別用單字節的char和寬字節的wchar_t來定義就應該是:

char  c[10];

wchar_t wc[10];

如果是要帶初始化的字符數組的聲明,我們來看看怎麼寫

char c[] = "beyondcode";

wchar_t wc[] = L"beyondcode";

看到了嗎,寬字節的操作其實和單字節的字符操作一樣吧,只是在前面加上L表示是寬字節的字符或者字符串。

上面都是屬於C/C++中的知識,並沒有涉及太多Windows中的數據類型,那麼各位朋友們在Windows編程中看到的滿到處都是的 TCHAR,LPSTR, LPCSTR, LPWSTR, LPCWSTR, LPTSTR, LPCTSTR 這些數據類型又是怎麼回事呢? 別急,我們一步一步的來,最後我會聯繫到那上面去的。

上面的你都知道或者是理解了的話,那我們繼續,除了可以聲明一個字符數組,我還可以定義一個字符指針變量來指向一個字符數組,當然這個字符數組可以是Unicode的寬字節字符數組,也可以是單字節字符數組,如下:

char  c[] = "hello beyondcode"; //定義一個字符數組

wchar_t  wc[] = L"hello beyondcode"; //定義一個寬字節字符數組

char   *p = c; //定義一個字符指針,指向剛纔的字符數組

wchar_t *wp = wc; //定義一個寬字節字符指針,指向剛纔的寬字節字符數組

這樣之後,我就可以通過指針來改變剛纔我們定義的2個數組,例如:

p[0] = 'H';

wp[0] = L'H';

把上面2個數組的第一個字符通過指針改變成大寫。這裏是可以通過指針來修改的,因爲我沒有定義指針爲常量指針,也就是沒有加const 修飾符。如果我像下面這樣定義的話,那麼就不能通過這些指針來改變他們所指向的數據了,而是隻有讀取他們。

const  char  *p = c;

const  wchar_t  *wp = wc;

上面將的都是C/C++的基礎知識,有點囉嗦,爲了照顧新手朋友們嘛,下面我們就來看看Windows是怎麼定義它的數據類型的

首先,定義了CHAR, WCHAR的這2個字符數據類型,就是我們上面討論的兩個字符數據類型改了一下名字而已。現在你還不昏吧··

typedef char  CHAR;

typedef wchar_t  WCHAR;

然後,用剛纔定義的 CHAR, WCHAR這2個字符數據類型去定義了一系列其他字符指針類型。

typedef  CHAR  *LPSTR;

typedef  WCHAR  *LPWSTR;

這樣一定義之後,LPSTR的就是 CHAR*, 而CHAR 又是char, 所以LPSTR的本質就是 char*,也就是我們上面熟悉的不能再熟悉的字符指針,  那LPWSTR不用我推導,相信你也推導出來了吧。不過我還是推導一下,LPWSTR是 WCHAR * , WCHAR是wchar_t,這樣LPWSTR就是 wchar_t* ,也就是我們上面討論的寬字節字符指針。上面這些定義都是在WinNT.h這個頭文件中定義的,讀者朋友們有興趣在這個頭文件裏面去挖掘挖掘吧,上面2個定義我只是提取了重要的部分,其實在裏面他還定義了其他很多別名.

看了LPSTR, LPWSTR是怎麼一回事之後,我們再接再厲,看看LPCSTR,LPCWSTR這2個數據類型又是怎麼一回事呢, 老規矩,先看windows的定義。

typedef  CONST  CHAR  *LPCSTR;

typedef  CONST  WCHAR *LPCWSTR;

和上面的比較,名字中就多了一個大寫的C,這個C的含義就代表是const修飾符,也就是我們上面所說的常量指針,指向的內容不能通過這個指針被改變,但可以讀取。定義中的大寫的CONST也是一個宏,我在第一篇文章中就講過了,代換出來也就是const, 所以請讀者自己推導一下這兩個數據類型的本質是什麼。

所以,在windows平臺下的編程過程中,凡是可以使用char* 的地方,你都可以使用LPSTR來代替,凡是可以使用wchar_t*的地方,你都可以使用LPWSTR來代替,至於怎麼用,還是那句老話,看你個人心情,只不過Windows的API函數中關於字符串的都是使用LP這種數據類型。但是你還是可以給他傳遞char* 或者 wchar_t* ,只要他們的本質是一樣的,那怎麼不可以呢~~

下面,我們來看一看一些示例。

char  c = 'c';  和 CHAR c = 'c';    是一樣的

wchar_t wc = L'w'; 和 WCHAR wc = L'w';    是一樣的

char* p  和 LPSTR p 是一樣的

wchar_t* wp  和 LPWSTR wp    是一樣的

再來看看動態內存分配怎麼寫的呢

char* p = new char[10]; //動態分配了十個字符

也可以寫成

CHAR* p = new CHAR[10];

LPSTR p = new CHAR[10];

LPSTR p = new char[10];

寬字節的再來一次

wchar_t* wp = new wchar_t[10];

也可以寫成下面這些形式

WCHAR*  wp = new WCHAR[10];

LPWSTR  wp = new WCHAR[10];

LPWSTR  wp = new wchar_t[10];

上面定義的這些字符指針 p , wp都沒有用const修飾符,所以可以通過他們來修改他們所指向的內容。這裏留給讀者一個問題,怎麼定義有const修飾符的字符指針呢,都可以用什麼形式來寫呢,寫得越多越好喲。。

通過上面這些,我想你大概已經瞭解了LPSTR, LPCSTR, LPWSTR, LPCWSTR這四個數據類型了,他們無非就是:

LPSTR  -------    char*

LPCSTR -------  const char*

LPWSTR -------  wchar_t*

LPCWSTR --------   const wchar_t* 

下面我提一個問題,如果你在你的程序中使用的字符串都是通過LPWSTR,LPCWSTR這種寬字節(Unicode)字符指針來進行操作的,那麼在Unicode環境下編譯,完全沒有問題,如果這時你需要編譯一套ASCII版本的程序,那你會怎麼辦呢?   你說將用LPWSTR 和LPCWSTR的地方全部換成LPSTR和LPCSTR,再將字符串前面的L去掉就可以了,對,這是一種方法,但是!!所有人在這裏都應該知道我要說但是,這也太麻煩了吧。難道沒有通用點的方法嗎?   有!!  所有人在這裏也都知道我會說有,呵呵。  那就是使用微軟的通用數據類型,說通用數據類型有點太專業了,其實也就那樣,請聽我慢慢分析來。我在上一篇文章中說過,凡是涉及字符串操作的API函數有2套,一個A系列的,一套W系列的,還有一套宏,能根據不同的工程環境定義成不同的API函數名。那麼在字符類型上微軟也使用幾乎同樣的技術,定義了一套宏能根據不同的工程環境定義成不同的字符數據類型。我上面就提到過的TCHAR,LPTSTR, LPCTSTR就是這樣的類型。

首先說說TCHAR,它是被這樣定義的:

#ifdef  UNICODE

typedef  WCHAR  TCHAR;

#else

typedef  char TCHAR

看到了嗎? 它也是根據UNICODE這個宏被定義沒有,如果被定義了,那麼TCHAR代表的數據類型就是WCHAR, 也就是wchar_t, 如果沒被定義,那麼TCHAR 就代表的是char

同樣LPTSTR,LPCTSTR也是這樣的,考慮到篇幅,我就只列出LPTSTR來給大家看看了

#ifdef   UNICODE

typedef  LPWSTR   LPTSTR;

#else

typedef  LPSTR LPTSTR;

這個是我簡化了的定義,真實面目有些複雜,不過意思也是如此,有興趣可以自己看看,在WinNT.h這個頭文件中。下面再次解釋一下上面這個LPTSTR的定義, 還是老樣子,根據UNICODE這個宏被定義與否來決定怎麼定義LPTSTR ,如果是定義了UNICODE這個宏,表示當前工程環境是Unicode環境,那麼LPTSTR就被定義爲了LPWSTR, LPWSTR就是我們前面所講的wchar_t* ,所以此時LPTSTR代表的數據類型就是wchar_t* ,  如果這時的工程沒有定義UNICODE這個宏,那麼就定義LPTSTR爲LPSTR,而LPSTR就是我們前面所說的char* ,所以這是的LPTSTR就代表char*。懂了嗎?各位,我都覺得自己有些囉嗦了··不好意思···

然後還有一個宏需要講一下,由於我們使用通用數據類型,那麼我事先就不知道我的源代碼需要在Unicode下編譯還是在ASCII環境下編譯,所以如下這種情況

TCHAR tc = 'a';  或者是 TCHAR tc = L'a';  是否合適呢? 前面我已經說過了字符或字符串常量前面加L代表這是寬字節的字符或字符串,將一個寬字節字符賦值給一個TCHAR數據類型的變量tc,什麼情況下是正確的呢?  各位思考一下呢?  

如果當前工程是Unicode環境,那麼TCHAR數據類型就是wchar_t的寬字節類型,所以tc就是寬字節字符變量,那麼上面第二個賦值語句就是正確的,而第一個就是錯誤的。

如果反過來,當前的工程是ASCII環境,那麼TCHAR代表的是char這種數據類型,那麼第一個賦值語句就是正確的,而第二個就是錯誤的了。

分析了這麼多,我就是要講一個宏 _T(), 只要將字符或者字符串常量放在_T()這個宏裏面,那麼這個宏就能根據當前的環境決定是否在字符或字符串前面加L,如下面:

TCHAR tc = _T('A');

如果這麼寫,在不需要改寫源代碼的情況下,就可以編譯出Unicode和ASCII兩套程序

而只需要改變工程的環境而已。

這篇文章的內容大概就這麼多了,關於後續文章的內容安排,我會適當採納各位朋友的留言來進行安排。

在這裏我介紹的是Windows平臺下的和字符串操作有關的數據類型,至於MFC中的CString類,c++標準庫中的string,我就不做講解了。

還有,前兩篇的文章中我說微軟定義這些數據類型爲大寫是爲了編碼的方便,不需要切換輸入法,很多朋友都留言給我指出了,其實我是開了一個小玩笑,畢竟面對的是初學者,我覺得在目前這種環境下,沒有必要給他們講什麼代碼移植性啊,兼容性這些概念,那些在達到一定程度之後就會慢慢理解的,所以我自己想了一個小小的優點來說明那個問題,不過還是感謝你們的補充說明,謝謝··

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章