LPCTSTR,CString與const char*,WCHAR轉換為CHAR雜談


1.LPCTSTR

L表示Long

P表示指針

C表示是一個常量

T表示Win32環境中有一個_T宏

STR表示一個字符串

所以LPCTSTR就是一個指向const對象的指針

2.常量字符串ansi和unicode的區分是由宏_T來決定的。但是用_T("abcd")時, 字符串"abcd"就會根據編譯時是否定是_UNICODE來決定是char* 還是 wchar_t*。

ansi情況下,LPCTSTR 就是 const char*, 是常量字符串(不能修改的)。
而LPTSTR 就是 char*, 即普通字符串(非常量,可修改的)。
這兩種都是基本類型, 而CString 是 C++類, 兼容這兩種基本類型是最起碼的任務了。
由於const char* 最簡單(常量,不涉及內存變更,操作迅速), CString 直接定義了一個類型轉換函數:
operator LPCTSTR( )
{.
.....
}

函數直接返回所維護的字符串。

     當你需要一個const char* 而傳入了CString時, C++ 編譯器自動調用 CString 重載的操作符 LPCTSTR()來進行隱式的類型轉換。
     當需要CString , 而傳入了 const char* 時(其實 char* 也可以),C++編譯器則自動調用CString的 構造函數來構造臨時的 CString對象。
     因此CString 和 LPCTSTR 基本可以通用。

3.LPCTSTR 它的執行過程其實很簡單,只是返回引用內存塊的串地址。 它是作為操作符重載提供的, 

所以在代碼中有時可以隱式轉換,而有時卻需強制轉制。如: 
CString str; 
const char* p = (LPCTSTR)str; 
//假設有這樣的一個函數,Test(const char* p); 你就可以這樣調用 
Test(str);//這里會隱式轉換為LPCTSTR   LPTSTR和LPCTSTR的區別就是不在是const類型,相當於char*

再者,string類型轉換成const char* 類型的方法:string::c_str().

1. string轉const char*
   string s = "abc";
   const char* c_s = s.c_str();
2. const char*轉string
   直接賦值即可
   const char* c_s = "abc";
   string s(c_s);
3. string轉char*
   string s = "abc";
   char* c;
   const int len = s.length();
   c = new char[len+1];
   strcpy(c,s.c_str());
4. char*轉string
   char* c = "abc";
   string s(c);
5. const char*轉char*
   const char* cpc = "abc";
   char* pc = new char[100];//足夠長
   strcpy(pc,cpc);
6. char*轉const char*
   直接賦值即可
   char* pc = "abc";
   const char* cpc = pc;

4.GetBuffer(int nMinBufLength)與 它類似,也會返回一個指針,不過它有點差別,返回的是LPTSTR 

5.這兩者到底有何不同呢?其本質上完全不一樣,一般說LPCTSTR轉換后只應該當常量使用,或者做函數的入參;而GetBuffer(...)取出指針后,可以通過這個指針來修改里面的內容,或者做函數的入參。為什么呢?也許經常有這樣的代碼: 
CString str("abcd"); 
char* p = (char*)(const char*)str; 
p[2] = 'z'; 
其實,也許有這樣的代碼后,你的程序並沒有錯,而且程序也運行得挺好。但它卻是非常危險的。再看 
CString str("abcd"); 
CString test = str; 
.... 
char* p = (char*)(const char*)str; 
p[2] = 'z'; 
strcpy(p, "akfjaksjfakfakfakj");//這下完蛋了 
你知道此時,test中的值是多少嗎?答案是"abzd".它也跟着改變了,這不是你所期望發生的。但為什么會這樣呢?你稍微想想就會明白,前面說過,因為CString是指向引用塊的,str與test指向同一塊地方,當你p[2]='z'后,當然test也會隨着改變。所以用它做LPCTSTR做轉換后,你只能去讀這塊數據,千萬別去改變它的內容。 

假如我想直接通過指針去修改數據的話,那怎樣辦呢?就是用GetBuffer(...).看下述代碼: 
CString str("abcd"); 
CString test = str; 
.... 
char* p = str.GetBuffer(20); 
p[2] = 'z'; // 執行到此,現在test中值卻仍是"abcd" 
strcpy(p, "akfjaksjfakfakfakj"); // 執行到此,現在test中值還是"abcd" 
為什么會這樣?其實GetBuffer(20)調用時,它實際上另外建立了一塊新內塊存,並分配20字節長度的buffer,而原來的內存塊引用計數也相應減1. 所以執行代碼后str與test是指向了兩塊不同的地方,所以相安無事。 

      不過這里還有一點注意事項:就是str.GetBuffer(20)后,str的分配長度為20,即指針p它所指向的buffer只有20字節長,給它賦值時,切不可超過,否則災難離你不遠了;如果指定長度小於原來串長度,如GetBuffer(1),實際上它會分配4個字節長度(即原來串長度);另外,當調用GetBuffer(...)后並改變其內容,一定要記得調用ReleaseBuffer(),這個函數會根據串內容來更新引用內存塊的頭部信息。 

6.拷貝 & 賦值 & "引用內存塊" 什么時候釋放? 


下面演示一段代碼執行過程 
void Test() 

CString str("abcd");//str指向一引用內存塊(引用內存塊的引用計數為1, 
長度為4,分配長度為4) 
CString a;//a指向一初始數據狀態, 
a = str; //a與str指向同一引用內存塊(引用內存塊的引用計數為2, 
長度為4,分配長度為4) 
CString b(a);//a、b與str指向同一引用內存塊(引用內存塊的引用 
計數為3,長度為4,分配長度為4) 

LPCTSTR temp = (LPCTSTR)a;//temp指向引用內存塊的串首地址。 
(引用內存塊的引用計數為3,長度為4,分配長度為4) 
CString d = a; //a、b、d與str指向同一引用內存塊(引用內存塊的引用計數為4, 長度為4,分配長度為4) 
b = "testa"; //這條語句實際是調用CString::operator=(CString&)函數。 
b指向一新分配的引用內存塊。(新分配的引用內存塊的 
引用計數為1,長度為5,分配長度為5) 
//同時原引用內存塊引用計數減1. a、d與str仍指向原 
引用內存塊(引用內存塊的引用計數為3,長度為4,分配長度為4) 
}//由於d生命結束,調用析構函數,導至引用計數減1(引用內存 
塊的引用計數為2,長度為4,分配長度為4) 
LPTSTR temp = a.GetBuffer(10);//此語句也會導致重新分配新內存塊。 
temp指向新分配引用內存塊的串首地址(新 
分配的引用內存塊的引用計數為1,長度 
為0,分配長度為10) 
//同時原引用內存塊引用計數減1. 只有str仍 
指向原引用內存塊(引用內存塊的引用計數為1, 
長度為4,分配長度為4) 
strcpy(temp, "temp"); //a指向的引用內存塊的引用計數為1,長度為0,分配長度為10 
a.ReleaseBuffer();//注意:a指向的引用內存塊的引用計數為1,長度為4,分配長度為10 

//執行到此,所有的局部變量生命周期都已結束。對象str a b 各自調用自己的析構構 
//函數,所指向的引用內存塊也相應減1 
//注意,str a b 所分別指向的引用內存塊的計數均為0,這導致所分配的內存塊釋放 
通過觀察上面執行過程,我們會發現CString雖然可以多個對象指向同一引用內塊存,但是它們在進行各種拷貝、賦值及改變串內容時,它的處理是很智能並且非常安全的,完全做到了互不干涉、互不影響。當然必須要求你的代碼使用正確恰當,特別是實際使用中會有更復雜的情況,如做函數參數、引用、及有時需保存到CStringList當中,如果哪怕有一小塊地方使用不當,其結果也會導致發生不可預知的錯誤  

7.C語言中表示字符串,沒有string概念,只可以用char*和char[];在MFC中,為了服務於字符串操作,添加了類CString,該類的頭 文件是afx.h. 

     從char* 到CString的轉換很簡單,只需要用CString的構造函數即可。 

    CString類功能強大,比STL的string類有過之無不及.新手使用CString時,都會被它強大的功能所吸引.然而由於對它內部機制的不了解,新手在將CString向C的字符數組轉換時容易出現很多問題.因為CString已經重載了LPCTSTR運算符,所以CString類向const char *轉換時沒有什么麻煩,如下所示:

  char a[100];
  CString str("aaaaaa");
  strncpy(a,(LPCTSTR)str,sizeof(a));

  或者如下:

  strncpy(a,str,sizeof(a));                      注:上面語句將CString-->const char*
  以上兩種用法都是正確地.因為strncpy的第二個參數類型為const char *.所以編譯器會自動將CString類轉換成const char *.很多人對LPCTSTR是什么東西迷惑不解,讓我們來看看:

  1.LP表示長指針,在win16下有長指針(LP)和短指針(P)的區別,而在win32下是沒有區別的,都是32位.所以這里的LP和P是等價的.

  2.C表示const

  3.T是什么東西呢,我們知道TCHAR在采用UNICODE方式編譯時是wchar_t,在普通時編譯成char那么就可以看出LPCTSTR(PCTSTR)在UINCODE時是const wchar_t *,PCWSTR,LPCWSTR,在多字節字符模式時是const char *, PCSTR,LPCSTR.接下來我們看在非UNICODE情況下,怎樣將CString轉換成char *,很多初學者都為了方便采用如下方法:

  (char *)(LPCSTR)str

        注:如果只是將CString-->char*,轉換后不修改字符串的內容,這個轉換是可以的。
  這樣對嗎?我們首先來看一個例子:

  CString str("aa");
  strcpy((char *)(LPCTSTR)str,"aaaaaaaa");
  cout<<(LPCTSTR)str<<endl;
  在Debug下運行出現了異常,我們都知道CString類內部有自己的字符指針,指向一個已分配的字符緩沖區.如果往里面寫的字符數超出了緩沖區范圍,當然會出現異常.但這個程序在Release版本下不會出現問題.原來對CString類已經進行了優化.當需要分配的內存小於64字節時,直接分配64字節的內存,以此類推,一般CString類字符緩沖區的大小為64,128,256,512...這樣是為了減少內存分配的次數,提高速度.

  那有人就說我往里面寫的字符數不超過它原來的字符數,不就不會出錯了,比如

  CString str("aaaaaaa");
  strcpy((char *)(LPCTSTR)str,"aa");
  cout<<(LPCTSTR)str<<endl;
  這樣看起來是沒什么問題.我們再來看下面這個例子:

  CString str("aaaaaaa");
  strcpy((char *)(LPCTSTR)str,"aa");
  cout<<(LPCTSTR)str<<endl;
  cout<<str.GetLength()<<endl;
  我們看到str的長度沒有隨之改變,繼續為7而不是2.還有更嚴重的問題:

  CString str("aaaaaaa");
  CString str1 = str;
  strcpy((char *)(LPCTSTR)str,"aa");
  cout<<(LPCTSTR)str<<endl;
  cout<<(LPCTSTR)str1<<endl;
  按說我們只改變了str,str1應該沒有改變呀,可是事實時他們都變成了"aa".難道str和str1里面的字符指針指向的緩沖區是一個.我們在Effective C++里面得知,如果你的類內部有包含指針,請為你的類寫一個拷貝構造函數和賦值運算符.不要讓兩個對象內部的指針指向同一區域,而應該重新分配內存.難道是微軟犯了錯?

  原來這里還有一個"寫時復制"和"引用計數"的概念.CString類的用途很廣,這樣有可能在系統內部產生大量的CString臨時對象.這時為了優化效率,就采用在系統軟件內部廣泛使用的"寫時復制"概念.即當從一個CString產生另一個CString並不復制它的字符緩沖區內容,而只是將字符緩沖區的"引用計數"加1.當需要改寫字符緩沖區內的內容時,才分配內存,並復制內容.以后我會給出一個"寫時復制"和"引用計數"的例子我們回到主題上來,當我們需要將CString轉換成char *時,我們應該怎么做呢?其時只是麻煩一點,如下所示:

  CString str("aaaaaaa");
  strcpy(str.GetBuffer(10),"aa");
  str.ReleaseBuffer();

       注:將CString-->char*,並修改字符串內容。
  當我們需要字符數組時調用GetBuffer(int n),其中n為我們需要的字符數組的長度.使用完成后一定要馬上調用ReleaseBuffer();還有很重要的一點就是,在能使用const char *的地方,就不要使用char *

8.UNICODE下寬字符的CString轉換為const char *和char到WCHAR的相互轉換 

UNICODE環境下TCHAR等價於WCHAR,ANSI環境下TCHAR等價於CHAR

//CString轉換為char*類型
CString theString( "This is a test" ); int sizeOfString = (theString.GetLength() + 1); LPTSTR lpsz = new TCHAR[ sizeOfString ]; _tcscpy_s(lpsz, sizeOfString, theString); 最后再轉換一下lpsz為const型的 LPTSTR在UNICODE環境下編譯是wchar_t類型
從WCHAR到char的轉換:

int WideCharToMultiByte(

  UINT CodePage,

  DWORD dwFlags,

  LPCWSTR lpWideCharStr,

  int cchWideChar,

  LPSTR lpMultiByteStr,

  int cbMultiByte,

  LPCSTR lpDefaultChar,

  LPBOOL lpUsedDefaultChar

);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM