程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> ANSI與Unicode編碼,TCHAR | LPSTR | LPCSTR | LPWSTR | LPCWSTR | LPTSTR | LPCTSTR 的含義,lpcstrlpwstr

ANSI與Unicode編碼,TCHAR | LPSTR | LPCSTR | LPWSTR | LPCWSTR | LPTSTR | LPCTSTR 的含義,lpcstrlpwstr

編輯:C++入門知識

ANSI與Unicode編碼,TCHAR | LPSTR | LPCSTR | LPWSTR | LPCWSTR | LPTSTR | LPCTSTR 的含義,lpcstrlpwstr


一個字符可以用1-byte表示,即ANSI編碼;

一個字符也可用2-bytes表示,即Unicode編碼(Unicode其實還包含了更多內容,不止2-bytes)。

Visual C++支持char和wchar_t作為ANSI和Unicode的原始數據類型。

例如

char cResponse; // 'Y' or 'N'
char sUsername[64];
// str* functions

以及

wchar_t cResponse; // 'Y' or 'N'
wchar_t sUsername[64];
// wcs* functions

它們可以統一寫成

#include<TCHAR.H> // Implicit or explicit include
TCHAR cResponse; // 'Y' or 'N'
TCHAR sUsername[64];
// _tcs* functions

 

TCHAR則是根據選擇的字符集決定是翻譯成char還是wchar_t,字符集的設置如下:

所以TCHAR的定義如下:

#ifdef _UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif

 

在windows中,一般前綴 代表了它可以自適應不同的字符集。

比如:strcpystrlenstrcat(包括安全後綴_s)代表ANSI版本;

         wcscpywcslenwcscat(包括安全後綴_s),代表Unicode版本,這裡WC代表Wide Character;

         _tcscpy_tcslen_tcscat則視情況而定:

size_t strlen(const char*); //ANSI
size_t wcslen(const wchar_t* ); //Unicdoe
size_t _tcslen(const TCHAR* ); //ANSI or Unicode

 


 我們知道一個string使用雙引號表示,這種表示說明它是一個ANSI-string,每個字符占1-byte,例如:

"This is ANSI String. Each letter takes 1 byte."

要轉換成Unicdeo-string需要加前綴:L

[__strong__]L"This is Unicode string. Each letter would take 2 bytes, including spaces."

 

Unicode編碼的字符,每個都占用2-bytes,哪怕是可以用1-byte表示的,比如英文字母,數字,null字符等。所以一個unicode-string占用的字節總是2-bytes的倍數。

結合上面提到的 T 前綴,一種適用於兩種字符集的寫法是這樣的:

"ANSI String"; // ANSI
L"Unicode String"; // Unicode

_T("Either string, depending on compilation"); // ANSI or Unicode

 

_TTEXT是一個宏定義,它與前綴 T 表示的意思一樣,定義如下:

// SIMPLIFIED
#ifdef _UNICODE 
 #define _T(c) L##c
 #define TEXT(c) L##c
#else 
 #define _T(c) c
 #define TEXT(c) c
#endif

上面的##叫“token-pasting operator”。在Unicode下,_T("Unicode")被翻譯成 L"Unicode";在ANSI下,_T("Unicode")被翻譯成 “Unicode”。

 

注意,不能通過_T來轉換一個變量(string or character),下面的操作是不允許的:

char c = 'C';
char str[16] = "CodeProject";

_T(c);
_T(str);

如果你是在ANSI(Multi-Byte)下編譯,可以順利通過,_T(c), _T(str)被翻譯成c, str;

但是在Unicode下編譯,就會報錯:

error C2065: 'Lc' : undeclared identifier
error C2065: 'Lstr' : undeclared identifier

結合_T的定義不難弄懂。

 

在windows中,幾乎所有需要傳入string或character的API,都有通用的版本,例如: SetWindowTextA/W,就可以統一寫成:

BOOL SetWindowText(HWND, const TCHAR*);

但我們知道SetWindowText是一個宏,它代表了以下兩種之一:

BOOL SetWindowTextA(HWND, const char*);
BOOL SetWindowTextW(HWND, const wchar_t*);

但其實,在內部實現時,不論ANSI還是Unicode都統一通過Unicode方式實現,當你調用 SetWindowTextA 時(傳入ANSI-string),它會先轉化成Unicode-string,再調用 SetWindowTextW實現。真正發揮作用的只有Unicode的版本!

所以在寫代碼時建議是直接調用Unicode版本的api,盡管我們對ANSI版本的string更熟悉。

Note:存在另外一個typedef:WCHAR,它等價於wchar_t

 


 我們知道strlen定義如下:

size_t strlen(const char*);

它也可以寫成

size_t strlen(LPCSTR);

所以

// Simplified
typedef const char* LPCSTR;  

它的含義如下

  • LP: Long Pointer
  • C: Constant
  • STR: String

Long Pointer與Pointer意思一樣。

 

舉一反三,對於Unicode字符,我們有:

size_t wcslen(const wchar_t* szString); // Or WCHAR*
size_t wcslen(LPCWSTR szString);

這裡 LPCWSTR代表

typedef const WCHAR* LPCWSTR;

它的含義如下

  • LP - Pointer
  • C - Constant
  • WSTR - Wide character String

更進一步,有LPCTSTR

  • LP - Pointer
  • C - Constant
  • T = TCHAR
  • STR = String

總結:

  • TCHAR - char / wchar_t (取決於字符集)
  • LPSTR - char*
  • LPCSTR - const char*
  • LPWSTR - wchar_t*
  • LPCWSTR - const wchar_t*
  • LPTSTR - TCHAR*
  • LPCTSTR - const TCHAR*

 


 在編程中有時候會因為選擇的字符集不同,而編譯出錯,如下面的寫法在ANSI下沒事,但在Unicode下就會報錯:

int main()
{
    TCHAR name[] = "Saturn";
    int nLen; // Or size_t

    lLen = strlen(name);
}
  • error C2440: 'initializing' : cannot convert from 'const char [7]' to 'TCHAR []'
  • error C2664: 'strlen' : cannot convert parameter 1 from 'TCHAR []' to 'const char *'

同樣的問題出現在:

nLen = wcslen("Saturn");
// ERROR: cannot convert parameter 1 from 'const char [7]' to 'const wchar_t *'

遺憾的是,上面的錯誤不能通過強制轉換的方法修改:

nLen = wcslen((const wchar_t*)"Saturn");

上面的寫法會得到錯誤的結果,往往導致越界。原因是“Saturn”占用7個字節

'S'(83) 'a'(97) 't'(116) 'u'(117) 'r'(114) 'n'(110) '\0'(0)

但傳給wcslen的時候,對於每個字符分配2-bytes。因此頭兩個字節[83,97]被看作一個字符,value:(97<<8 | 83),是字符'?'.後面的以此類推。

所以如果用Unicode的api,需要提前轉換:

TCHAR name[] = _T("Saturn");
//或者
wcslen(L"Saturn");

 

在之前的例子中,strlen(name)中的name在Unicode下編譯,每個字符占2-bytes,如果強制轉換成ANSI:

lLen = strlen ((const char*)name);

也會出現問題,‘S'原來表示為[83,0],但在ANSI中第一個字節[83]可以被正確翻譯成'S',但接著第二個字節[0]直接被翻譯為為'\0',結束了整個字符串。所以strlen得到的結果為1

綜上,C語言風格的強制轉換在這裡是行不通的。

 


 如果需要分配內存,在C++中通過new直接指定字符的個數,不用去管具體分配了多少字節:

LPTSTR pBuffer; // TCHAR* 

pBuffer = new TCHAR[128]; // Allocates 128 or 256 BYTES, depending on compilation.

 但如果你是用malloc,LocalAlloc,GlobalAlloc這類api分配空間,就需要指定具體的字節數:

pBuffer = (TCHAR*) malloc (128 * sizeof(TCHAR) );

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved