轉:http://blog.sina.com.cn/s/blog_9ffcd5dc01014nw9.html
前面的幾天一直都在復習着被實習落下的C++基礎知識。今天在復習着上次創建的窗口程序時,出現了一個錯誤,百思不得其解。因為是同樣的代碼,上次的都能順利的通過編譯,這次自己新建了一個工程結果就有一個錯誤出現,是在調用Create()函數時,傳參數出現問題如下圖所示:
錯誤提示為:“const char*”與函數申明的參數類型不兼容。於是就查看了Create()函數的第二個參數類型,其函數原型如下:
virtual BOOL Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, //第二個參數類型為LPCTSTR
DWORD dwStyle = WS_OVERLAPPEDWINDOW,
const RECT& rect = rectDefault,
CWnd* pParentWnd = NULL, // != NULL for popups
LPCTSTR lpszMenuName = NULL,
DWORD dwExStyle = 0,
CCreateContext* pContext = NULL);
然后我就一路Go To Definition下去,發現LPCTSTR的定義:
typedef __nullterminated CONST WCHAR *LPCWSTR, *PCWSTR;
發現其最終是一個指向WCHAR的指針,至此毫無辦法,只知道是數據類型上的錯誤。可以說是完全從上次編譯過的程序復制過來的。后面我就把其編譯過的錯誤放上百度:
error C2664: 'CFrameWnd::Create' : cannot convert parameter 2 from 'const char [9]' to 'LPCTSTR';
一搜就在CSDN上面看到一一篇文章:(博文地址:http://blog.csdn.net/zxj2018/article/details/6765236)
VC錯誤之_cannot convert parameter 2 from 'const char [12]' to 'LPCWSTR'
是一篇英文,不過一看就開始有眉目了:
Question
I'm trying to compile a piece of code such as:
MessageBox("Hello world!");
... when I compile the project, the compiler yields:
error C2664: 'CWnd::MessageBoxW' : cannot convert parameter 1 from 'const char [12]' to 'LPCTSTR'
What am I doing wrong?
Problem
This error message means that you are trying to pass a multi-byte string (const char [12]) to a function which expects a unicode string (LPCTSTR). The LPCTSTR type extends to const TCHAR*, where TCHAR is char when you compile for multi-byte and wchar_t for unicode. Since the compiler doesn't accept the char array, we can safely assume that the actual type of TCHAR, in this compilation, is wchar_t.
Resolution
You will have to do one of two things:
<1> Change your project configuration to use multibyte strings. Press ALT+F7 to open the properties, and navigate to Configuration Properties > General. Switch Character Set to "Use Multi-Byte Character Set".
<2> Indicate that the string literal, in this case "Hello world!" is of a specific encoding. This can be done through either prefixing it with L, such as L"Hello world!", or surrounding it with the generic _T("Hello world!") macro. The latter will expand to the L prefix if you are compiling for unicode (see #1), and nothing (indicating multi-byte) otherwise.
Variations
Another error message, indicating the same problem, would be:
cannot convert parameter 1 from 'const char [12]' to 'LPCWSTR'
Where LPCWSTR maps to a wchar_t pointer, regardless of your build configuration. This problem can be resolved primarily by using solution #2, but in some cases also #1. A lot of the Microsoft provided libraries, such as the Platform SDK, have got two variations of each function which takes strings as parameters. In case of a unicode build, the actual functions are postfixed W, such as the MessageBoxW seen above. In case of multi-byte, the function would be MessageBoxA (ASCII). Which of these functions is actually used when you compile your application, depends on the setting described in resolution #1 above.
一看到上面的project configuration,有一個Character Set,因此我就對比了我上次編譯通過的的工程配置和今天我新建的工程配置,下面是對比的圖:第一幅圖是今天編譯出錯時的配置,第二幅是測試上次編譯通過時的配置,發現在Character Set選項里有區別,編譯出錯時是把Character Set設置成了Use Unicode Character Set,按照上面的提示給設置成"Use Multi-Byte Character Set".順利解決。
可我還是不知道為什么是這樣的?上面說還有一種方法就是(不去設置工程配置):隨便翻譯一下<2>申明這個字符串,在這種情況下"Hello world!"作為一種特別編碼,這可以通過把L作為前綴,比如這樣寫:L"Hello world!",或者使用通用的_T("Hello world!")宏的形式,后者將比前綴L更廣泛吧,就是當你在使用unicode時而不是在設置為多字節編碼。
如圖:
當我再百度了幾個關鍵字LPCWSTR,unicode,TCARH,后開始懂了為什么了,
TCARH百度百科:
定義
使用原理
因為C++支持兩種字符串,即常規的ANSI編碼(使用""包裹)和Unicode編碼(使用L""包裹),這樣對應的就有了兩套字符串字符串處理函數,比如:strlen和wcslen,分別用於處理兩種字符串
微軟將這兩套字符集及其操作進行了統一,通過條件編譯(通過_UNICODE和UNICODE宏)控制實際使用的字符集,這樣就有了_T("")這樣的字符串,對應的就有了_tcslen這樣的函數
為了存儲這樣的通用字符,就有了TCHAR:
當沒有定義_UNICODE宏時,TCHAR = char,_tcslen = strlen
當定義了_UNICODE宏時,TCHAR = wchar_t , _tcslen = wcslen
當我們定義了UNICODE宏,就相當於告訴了編譯器:我准備采用UNICODE版本。這個時候,TCHAR就會搖身一變,變成了wchar_t。而未定義UNICODE宏時,TCHAR搖身一變,變成了unsigned char。這樣就可以很好的切換寬窄字符集。
tchar可用於雙字節字符串,使程序可以用於中日韓等國語言文字處理、顯示。使編程方法簡化。
LPCWSTR百度百科:
MSDN 原文
An LPCWSTR is a 32-bit pointer to a constant string of 16-bit Unicode Charactor, which may be null-terminated.
This type is declared as follows:
typedef const wchar_t* LPCWSTR;
簡要解釋
LPCWSTR是一個指向unicode編碼字符串的32位指針,所指向字符串是wchar型,而不是char型。
因為在VS2005以后,編碼方式默認為Unicode,部分函數在使用時默認調用Unicode方式(函數名+W,exp:MessageBox+W=MessageBoxW),而非ASNI方式(函數名+A,exp:MessageBox+A=MessageBoxA)。
請看winuser.h中的聲明如下:
WINUSERAPI
int
WINAPI
MessageBoxA(
__in_opt HWND hWnd,
__in_opt LPCSTR lpText,
__in_opt LPCSTR lpCaption,
__in UINT uType);
WINUSERAPI
int
WINAPI
MessageBoxW(
__in_opt HWND hWnd,
__in_opt LPCWSTR lpText,
__in_opt LPCWSTR lpCaption,
__in UINT uType);
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif
上述聲明的意思是,在unicode編碼下MessageBox被編譯為MessageBoxW,否則就編譯為MessageBoxA。而兩者的區別則看函數聲明中參數2、3就可以明白了。
關於錯誤
如果遇到參數錯誤(cannot convert parameter * from 'const char [**]' to 'LPCWSTR'),可以考慮察看聲明,如果有ASNI方式的只要在函數后面加個A就可以了,或者在定義參數時把char*改為WCHAR*。
如果是混合使用的,那可以考慮轉化,方法很多,不解釋。
同時還了解了unicode和ASCII編碼的相關知識。對C++數據類型wchar_t這種雙字節的數據類型也復習了一遍,印象不是很深,C語言中沒有該類型。在經過了很多時間的資料查找學習后,開始明白了上面的參數提示錯誤問題,主要就是char和wchar_t的問題了。當把工程配置里的Character Set 設置成"Use Multi-Byte Character Set"時,即編譯器使用的是多種字符集編譯,那么在使用Unicode字符集的時候,什么時候要在字符串前面加_T這個宏,其實關鍵就是函數所需傳遞參數是char *,還是TCHAR *。
下面是搜索的關於字符集的資料:
字符基礎 -- ASCII, DBCS, Unicode
所有的 string 類都是以C-style字符串為基礎的。C-style 字符串是字符數組。所以我們先介紹字符類型。這里有3種編碼模式對應3種字符類型。第一種編碼類型是單子節字符集(single-byte character set or SBCS)。在這種編碼模式下,所有的字符都只用一個字節表示。ASCII是SBCS。一個字節表示的0用來標志SBCS字符串的結束。
第二種編碼模式是多字節字符集(multi-byte character set or MBCS)。一個MBCS編碼包含一些一個字節長的字符,而另一些字符大於一個字節的長度。用在Windows里的MBCS包含兩種字符類型,單字節字符(single-byte characters)和雙字節字符(double-byte characters)。由於Windows里使用的多字節字符絕大部分是兩個字節長,所以MBCS常被用DBCS代替。
在DBCS編碼模式中,一些特定的值被保留用來表明他們是雙字節字符的一部分。例如,在Shift-JIS編碼中(一個常用的日文編碼模式),0x81-0x9f之間和 0xe0-oxfc之間的值表示 "這是一個雙字節字符,下一個子節是這個字符的一部分。 "這樣的值被稱作 "leading bytes ",他們都大於0x7f。跟隨在一個leading byte子節后面的字節被稱作 "trail byte "。在DBCS中,trail byte可以是任意非0值。像SBCS一樣,DBCS字符串的結束標志也是一個單字節表示的0。
第三種編碼模式是Unicode。Unicode是一種所有的字符都使用兩個字節編碼的編碼模式。Unicode字符有時也被稱作寬字符,因為它比單子節字符寬(使用了更多的存儲空間)。注意,Unicode不能被看作MBCS。MBCS的獨特之處在於它的字符使用不同長度的字節編碼。Unicode字符串使用兩個字節表示的0作為它的結束標志。
單字節字符包含拉丁文字母表,accented characters及ASCII標准和DOS操作系統定義的圖形字符。雙字節字符被用來表示東亞及中東的語言。Unicode被用在COM及Windows NT操作系統內部。
你一定已經很熟悉單字節字符。當你使用char時,你處理的是單字節字符。雙字節字符也用char類型來進行操作(這是我們將會看到的關於雙子節字符的很多奇怪的地方之一)。Unicode字符用wchar_t來表示。Unicode字符和字符串常量用前綴L來表示。
今天通過對這個問題的處理,雖然還不是全理解了,但也了解了很多相關的知識。