• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            tqsheng

            go.....
            隨筆 - 366, 文章 - 18, 評(píng)論 - 101, 引用 - 0
            數(shù)據(jù)加載中……

            CString 中的寫入拷貝的分析

            最近在網(wǎng)上看了一篇文章,是有關(guān)CString 結(jié)構(gòu)的,我把它轉(zhuǎn)載過(guò)來(lái)希望大家看了對(duì)你有幫助理解CString

            CString? 如果你接觸過(guò)vc/mfc,呵呵,這個(gè)名字你一定見過(guò)。那么你就大膽的看下面的一個(gè)例子吧
            ----------------------------------
            一段簡(jiǎn)單的代碼如下:
            ----------------------------------

            void C...Dlg::OnOK() 
            {
            CString str;

            strcpy((LPSTR)(LPCTSTR)str,"Hello!");
            AfxMessageBox(str);

            CString strL = "";
            int n;
            n = strL.GetLength();
            AfxMessageBox(strL);

            CDialog::OnOK();
            }
            說(shuō)明:用(LPSTR)(LPCTSTR)這種怪怪的方法其實(shí)是故意的,目的是為了模仿現(xiàn)實(shí)問(wèn)題。
            現(xiàn)實(shí)是strcpy函數(shù)的調(diào)用在dll中做的,因?yàn)檎{(diào)用dll中函數(shù)沒有參數(shù)類型檢查,因此不用這種顯式轉(zhuǎn)換,而在這里沒有辦法,只是為了模仿strcpy的調(diào)用。
            ----------------------------------
            大家可以跟蹤看一看:

            首先str得到Hello!沒有問(wèn)題
            奇怪在下面,定義了第二個(gè)CString類型的對(duì)象,并賦初值為空字符,但跟蹤觀察發(fā)現(xiàn),strL中同樣也是Hello!指針地址是一樣的,但是奇怪的是GetLength()卻是0。既然長(zhǎng)度為0,但卻有值,而且還可以正常使用,比如用AfxMessageBox顯示。但是如果我賦值不是空字符而是一個(gè)具體的,則沒有問(wèn)題。

            上面的問(wèn)題其實(shí)很簡(jiǎn)單,但我第一次遇到時(shí)卻百思不得其解。所以現(xiàn)在回想著寫下這段文字,目的是針對(duì)那些還沒有注意CString結(jié)構(gòu)的程序員們,至于高手們,呵呵,希望可以指點(diǎn)我一二,不甚感激!

            解決上述問(wèn)題前,先解釋一個(gè)名詞寫入復(fù)制技術(shù)(CopyBeforeWrite):當(dāng)使用一個(gè)CString對(duì)象a來(lái)初始化另一個(gè)CString對(duì)象b時(shí),為了節(jié)省空間,新對(duì)象b并不分配空間,它所要做的只是將自己的指針指向?qū)ο骯的那塊內(nèi)存空間,只有當(dāng)需要修改對(duì)象a或者b中的字符串時(shí),才會(huì)為新對(duì)象b申請(qǐng)內(nèi)存空間。這種技術(shù)其實(shí)就是微軟為了提高效率想出的一個(gè)怪招,既然有,我們就了解一下。
            接下來(lái),就分析一下CString的結(jié)構(gòu)了:
            CString大致可以理解成下面結(jié)構(gòu)
            |                     |                            |
            |    Header    |           Data         |
            |                     |                            |
            也就是說(shuō)CString其實(shí)包括一個(gè)header(數(shù)據(jù)頭)和data(數(shù)據(jù)區(qū))
            CString是對(duì)于原來(lái)標(biāo)準(zhǔn)c中字符串類型的一種的包裝。因?yàn)?,通過(guò)很長(zhǎng)時(shí)間的編程,我們發(fā)現(xiàn),很多程序的bug多和字符串有關(guān),典型的有:緩沖溢出、內(nèi)存泄漏等。而且這些bug都是致命的,會(huì)造成系統(tǒng)的癱瘓。因此c++里就專門的做了一個(gè)類用來(lái)維護(hù)字符串指針。標(biāo)準(zhǔn)c++里的字符串類是string,在microsoft MFC類庫(kù)中使用的是CString類。通過(guò)字符串類,可以大大的避免c中的關(guān)于字符串指針的那些問(wèn)題。

            這里我們簡(jiǎn)單的看看Microsoft MFC中的CString是如何實(shí)現(xiàn)的。當(dāng)然,要看原理,直接把它的代碼拿過(guò)來(lái)分析是最好的。MFC里的關(guān)于CString的類的實(shí)現(xiàn)大部分在strcore.cpp中。

            CString就是對(duì)一個(gè)用來(lái)存放字符串的緩沖區(qū)和對(duì)施加于這個(gè)字符串的操作封裝。也就是說(shuō),CString里需要有一個(gè)用來(lái)存放字符串的緩沖區(qū),并且有一個(gè)指針指向該緩沖區(qū),該指針就是LPTSTR m_pchData。但是有些字符串操作會(huì)增建或減少字符串的長(zhǎng)度,因此為了減少頻繁的申請(qǐng)內(nèi)存或者釋放內(nèi)存,CString會(huì)先申請(qǐng)一個(gè)大的內(nèi)存塊用來(lái)存放字符串。這樣,以后當(dāng)字符串長(zhǎng)度增長(zhǎng)時(shí),如果增加的總長(zhǎng)度不超過(guò)預(yù)先申請(qǐng)的內(nèi)存塊的長(zhǎng)度,就不用再申請(qǐng)內(nèi)存。當(dāng)增加后的字符串長(zhǎng)度超過(guò)預(yù)先申請(qǐng)的內(nèi)存時(shí),CString先釋放原先的內(nèi)存,然后再重新申請(qǐng)一個(gè)更大的內(nèi)存塊。同樣的,當(dāng)字符串長(zhǎng)度減少時(shí),也不釋放多出來(lái)的內(nèi)存空間。而是等到積累到一定程度時(shí),才一次性將多余的內(nèi)存釋放。

            還有,當(dāng)使用一個(gè)CString對(duì)象a來(lái)初始化另一個(gè)CString對(duì)象b時(shí),為了節(jié)省空間,新對(duì)象b并不分配空間,它所要做的只是將自己的指針指向?qū)ο骯的那塊內(nèi)存空間,只有當(dāng)需要修改對(duì)象a或者b中的字符串時(shí),才會(huì)為新對(duì)象b申請(qǐng)內(nèi)存空間,這叫做寫入復(fù)制技術(shù)(CopyBeforeWrite)。上面重點(diǎn)說(shuō)過(guò)了

            這樣,僅僅通過(guò)一個(gè)指針就不能完整的描述這塊內(nèi)存的具體情況,需要更多的信息來(lái)描述。

            首先,需要有一個(gè)變量來(lái)描述當(dāng)前內(nèi)存塊的總的大小。
            其次,需要一個(gè)變量來(lái)描述當(dāng)前內(nèi)存塊已經(jīng)使用的情況。也就是當(dāng)前字符串的長(zhǎng)度
            另外,還需要一個(gè)變量來(lái)描述該內(nèi)存塊被其他CString引用的情況。有一個(gè)對(duì)象引用該內(nèi)存塊,就將該數(shù)值加1。

            CString中專門定義了一個(gè)結(jié)構(gòu)體來(lái)描述這些信息:
            struct CStringData
            {
            long nRefs;             // reference count
            int nDataLength;        // length of data (including terminator)
            int nAllocLength;       // length of allocation
            // TCHAR data[nAllocLength]

            TCHAR* data()           // TCHAR* to managed data
            { return (TCHAR*)(this+1); }
            };

            實(shí)際使用時(shí),該結(jié)構(gòu)體的所占用的內(nèi)存塊大小是不固定的,在CString內(nèi)部的內(nèi)存塊頭部,放置的是該結(jié)構(gòu)體。從該內(nèi)存塊頭部開始的sizeof(CstringData)個(gè)BYTE后才是真正的用于存放字符串的內(nèi)存空間。這種結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu)的申請(qǐng)方法是這樣實(shí)現(xiàn)的:
            pData = (CStringData*) new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)];
            pData->nAllocLength = nLen;
            其中nLen是用于說(shuō)明需要一次性申請(qǐng)的內(nèi)存空間的大小的。

            從代碼中可以很容易的看出,如果想申請(qǐng)一個(gè)256個(gè)TCHAR的內(nèi)存塊用于存放字符串,實(shí)際申請(qǐng)的大小是:
            sizeof(CStringData)個(gè)BYTE + (nLen+1)個(gè)TCHAR

            其中前面sizeof(CstringData)個(gè)BYTE是用來(lái)存放CstringData信息的。后面的nLen+1個(gè)TCHAR才是真正用來(lái)存放字符串的,多出來(lái)的一個(gè)用來(lái)存放’\0’。

            CString中所有的operations的都是針對(duì)這個(gè)緩沖區(qū)的。比如LPTSTR CString::GetBuffer(int nMinBufLength),它的實(shí)現(xiàn)方法是:
            首先通過(guò)CString::GetData()取得CStringData對(duì)象的指針。該指針是通過(guò)存放字符串的指針m_pchData先后偏移sizeof(CstringData),從而得到了CStringData的地址。
            然后根據(jù)參數(shù)nMinBufLength給定的值重新實(shí)例化一個(gè)CStringData對(duì)象,使得新的對(duì)象里的字符串緩沖長(zhǎng)度能夠滿足nMinBufLength。
            然后在重新設(shè)置一下新的CstringData中的一些描述值。C
            最后將新CStringData對(duì)象里的字符串緩沖直接返回給調(diào)用者。

            這些過(guò)程用C++代碼描述就是:
            if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
            {
            // we have to grow the buffer
            CStringData* pOldData = GetData();
            int nOldLen = GetData()->nDataLength;   // AllocBuffer will tromp it
            if (nMinBufLength < nOldLen)
               nMinBufLength = nOldLen;
            AllocBuffer(nMinBufLength);
            memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR));
            GetData()->nDataLength = nOldLen;
            CString::Release(pOldData);
            }
            ASSERT(GetData()->nRefs <= 1);

            // return a pointer to the character storage for this string
            ASSERT(m_pchData != NULL);
            return m_pchData;

            很多時(shí)候,我們經(jīng)常的對(duì)大批量的字符串進(jìn)行互相拷貝修改等,CString 使用了CopyBeforeWrite技術(shù)。使用這種方法,當(dāng)利用一個(gè)CString對(duì)象a實(shí)例化另一個(gè)對(duì)象b的時(shí)候,其實(shí)兩個(gè)對(duì)象的數(shù)值是完全相同的,但是如果簡(jiǎn)單的給兩個(gè)對(duì)象都申請(qǐng)內(nèi)存的話,對(duì)于只有幾個(gè)、幾十個(gè)字節(jié)的字符串還沒有什么,如果是一個(gè)幾K甚至幾M的數(shù)據(jù)量來(lái)說(shuō),是一個(gè)很大的浪費(fèi)。
            因此CString 在這個(gè)時(shí)候只是簡(jiǎn)單的將新對(duì)象b的字符串地址m_pchData直接指向另一個(gè)對(duì)象a的字符串地址m_pchData。所做的額外工作是將對(duì)象a的內(nèi)存應(yīng)用CStringData:: nRefs加一。
            CString::CString(const CString& stringSrc)
            {
            m_pchData = stringSrc.m_pchData;
            InterlockedIncrement(&GetData()->nRefs);
            }

            這樣當(dāng)修改對(duì)象a或?qū)ο骲的字符串內(nèi)容時(shí),首先檢查CStringData:: nRefs的值,如果大于一(等于一,說(shuō)明只有自己一個(gè)應(yīng)用該內(nèi)存空間),說(shuō)明該對(duì)象引用了別的對(duì)象內(nèi)存或者自己的內(nèi)存被別人應(yīng)用,該對(duì)象首先將該應(yīng)用值減一,然后將該內(nèi)存交給其他的對(duì)象管理,自己重新申請(qǐng)一塊內(nèi)存,并將原來(lái)內(nèi)存的內(nèi)容拷貝過(guò)來(lái)。

            其實(shí)現(xiàn)的簡(jiǎn)單代碼是:
            void CString::CopyBeforeWrite()
            {
            if (GetData()->nRefs > 1)
            {
            CStringData* pData = GetData();
            Release();
            AllocBuffer(pData->nDataLength);
            memcpy(m_pchData, pData->data(),
            (pData- >nDataLength+1)*sizeof(TCHAR));
            }
            }
            其中Release 就是用來(lái)判斷該內(nèi)存的被引用情況的。
            void CString::Release()
            {
            if (GetData() != _afxDataNil)
            {
            if (InterlockedDecrement(&GetData()->nRefs) <= 0)
               FreeData(GetData());
            }
            }

            當(dāng)多個(gè)對(duì)象共享同一塊內(nèi)存時(shí),這塊內(nèi)存就屬于多個(gè)對(duì)象,而不在屬于原來(lái)的申請(qǐng)這塊內(nèi)存的那個(gè)對(duì)象了。但是,每個(gè)對(duì)象在其生命結(jié)束時(shí),都首先將這塊內(nèi)存的引用減一,然后再判斷這個(gè)引用值,如果小于等于零時(shí),就將其釋放,否則,將之交給另外的正在引用這塊內(nèi)存的對(duì)象控制。

            CString使用這種數(shù)據(jù)結(jié)構(gòu),對(duì)于大數(shù)據(jù)量的字符串操作,可以節(jié)省很多頻繁申請(qǐng)釋放內(nèi)存的時(shí)間,有助于提升系統(tǒng)性能。

            通過(guò)上面的分析,我們已經(jīng)對(duì)CString的內(nèi)部機(jī)制已經(jīng)有了一個(gè)大致的了解了??偟恼f(shuō)來(lái)MFC中的CString是比較成功的。但是,由于數(shù)據(jù)結(jié)構(gòu)比較復(fù)雜(使用CStringData),所以在使用的時(shí)候就出現(xiàn)了很多的問(wèn)題,最典型的一個(gè)就是用來(lái)描述內(nèi)存塊屬性的屬性值和實(shí)際的值不一致。出現(xiàn)這個(gè)問(wèn)題的原因就是CString為了方便某些應(yīng)用,提供了一些operations,這些operation可以直接返回內(nèi)存塊中的字符串的地址值,用戶可以通過(guò)對(duì)這個(gè)地址值指向的地址進(jìn)行修改,但是,修改后又沒有調(diào)用相應(yīng)的operations1使CStringData中的值來(lái)保持一致。比如,用戶可以首先通過(guò)operations得到字符串地址,然后將一些新的字符增加到這個(gè)字符串中,使得字符串的長(zhǎng)度增加,但是,由于是直接通過(guò)指針修改的,所以描述該字符串長(zhǎng)度的CStringData中的nDataLength卻還是原來(lái)的長(zhǎng)度,因此當(dāng)通過(guò)GetLength獲取字符串長(zhǎng)度時(shí),返回的必然是不正確的。

            存在這些問(wèn)題的operations下面一一介紹。

            1. GetBuffer

            很多錯(cuò)誤用法中最典型的一個(gè)就是CString:: GetBuffer ()了.查了MSDN,里面對(duì)這個(gè)operation的描述是:
            Returns a pointer to the internal character buffer for the CString object. The returned LPTSTR is not const and thus allows direct modification of CString contents。
            這段很清楚的說(shuō)明,對(duì)于這個(gè)operation返回的字符串指針,我們可以直接修改其中的值:
            CString str1("This is the string 1");――――――――――――――――1
            int nOldLen = str1.GetLength();―――――――――――――――――2
            char* pstr1 = str1.GetBuffer( nOldLen );――――――――――――――3
            strcpy( pstr1, "modified" );――――――――――――――――――――4
            int nNewLen = str1.GetLength();―――――――――――――――――5

            通過(guò)設(shè)置斷點(diǎn),我們來(lái)運(yùn)行并跟蹤這段代碼可以看出,當(dāng)運(yùn)行到三處時(shí),str1的值是”This is the string 1”,并且nOldLen的值是20。當(dāng)運(yùn)行到5處時(shí),發(fā)現(xiàn),str1的值變成了”modified”。也就是說(shuō),對(duì)GetBuffer返回的字符串指針,我們將它做為參數(shù)傳遞給strcpy,試圖來(lái)修改這個(gè)字符串指針指向的地址,結(jié)果是修改成功,并且CString對(duì)象str1的值也響應(yīng)的變成了” modified”。但是,我們接著再調(diào)用str1.GetLength()時(shí)卻意外的發(fā)現(xiàn)其返回值仍然是20,但是實(shí)際上此時(shí)str1中的字符串已經(jīng)變成了” modified”,也就是說(shuō)這個(gè)時(shí)候返回的值應(yīng)該是字符串” modified”的長(zhǎng)度8!而不是20。現(xiàn)在CString工作已經(jīng)不正常了!這是怎么回事?

            很顯然,str1工作不正常是在對(duì)通過(guò)GetBuffer返回的指針進(jìn)行一個(gè)字符串拷貝之后的。

            再看MSDN上的關(guān)于這個(gè)operation的說(shuō)明,可以看到里面有這么一段話:
            If you use the pointer returned by GetBuffer to change the string contents, you must call ReleaseBuffer before using any other CString member functions.

            原來(lái)在對(duì)GetBuffer返回的指針使用之后需要調(diào)用ReleaseBuffer,這樣才能使用其他CString的operations。上面的代碼中,我們?cè)?-5處增建一行代碼:str2.ReleaseBuffer(),然后再觀察nNewLen,發(fā)現(xiàn)這個(gè)時(shí)候已經(jīng)是我們想要的值8了。

            從CString的機(jī)理上也可以看出:GetBuffer返回的是CStringData對(duì)象里的字符串緩沖的首地址。根據(jù)這個(gè)地址,我們對(duì)這個(gè)地址里的值進(jìn)行的修改,改變的只是CStringData里的字符串緩沖中的值, CStringData中的其他用來(lái)描述字符串緩沖的屬性的值已經(jīng)不是正確的了。比如此時(shí)CStringData:: nDataLength很顯然還是原來(lái)的值20,但是現(xiàn)在實(shí)際上字符串的長(zhǎng)度已經(jīng)是8了。也就是說(shuō)我們還需要對(duì)CStringData中的其他值進(jìn)行修改。這也就是需要調(diào)用ReleaseBuffer()的原因了。

            正如我們所預(yù)料的,ReleaseBuffer源代碼中顯示的正是我們所猜想的:
            CopyBeforeWrite(); // just in case GetBuffer was not called

            if (nNewLength == -1)
            nNewLength = lstrlen(m_pchData); // zero terminated

            ASSERT(nNewLength <= GetData()->nAllocLength);
            GetData()->nDataLength = nNewLength;
            m_pchData[nNewLength] = '\0';
            其中CopyBeforeWrite是實(shí)現(xiàn)寫拷貝技術(shù)的,這里不管它。

            下面的代碼就是重新設(shè)置CStringData對(duì)象中描述字符串長(zhǎng)度的那個(gè)屬性值的。首先取得當(dāng)前字符串的長(zhǎng)度,然后通過(guò)GetData()取得CStringData的對(duì)象指針,并修改里面的nDataLength成員值。

            但是,現(xiàn)在的問(wèn)題是,我們雖然知道了錯(cuò)誤的原因,知道了當(dāng)修改了GetBuffer返回的指針?biāo)赶虻闹抵笮枰{(diào)用ReleaseBuffer才能使用CString的其他operations時(shí),我們就能避免不在犯這個(gè)錯(cuò)誤了。答案是否定的。這就像雖然每一個(gè)懂一點(diǎn)編程知識(shí)的人都知道通過(guò)new申請(qǐng)的內(nèi)存在使用完以后需要通過(guò)delete來(lái)釋放一樣,道理雖然很簡(jiǎn)單,但是,最后實(shí)際的結(jié)果還是有由于忘記調(diào)用delete而出現(xiàn)了內(nèi)存泄漏。
            實(shí)際工作中,常常是對(duì)GetBuffer返回的值進(jìn)行了修改,但是最后卻忘記調(diào)用ReleaseBuffer來(lái)釋放。而且,由于這個(gè)錯(cuò)誤不象new和delete人人都知道的并重視的,因此也沒有一個(gè)檢查機(jī)制來(lái)專門檢查,所以最終程序中由于忘記調(diào)用ReleaseBuffer而引起的錯(cuò)誤被帶到了發(fā)行版本中。

            要避免這個(gè)錯(cuò)誤,方法很多。但是最簡(jiǎn)單也是最有效的就是避免這種用法。很多時(shí)候,我們并不需要這種用法,我們完全可以通過(guò)其他的安全方法來(lái)實(shí)現(xiàn)。
            比如上面的代碼,我們完全可以這樣寫:
            CString str1("This is the string 1");
            int nOldLen = str1.GetLength();
            str1 = "modified";
            int nNewLen = str1.GetLength();

            但是有時(shí)候確實(shí)需要,比如:
            我們需要將一個(gè)CString對(duì)象中的字符串進(jìn)行一些轉(zhuǎn)換,這個(gè)轉(zhuǎn)換是通過(guò)調(diào)用一個(gè)dll里的函數(shù)Translate來(lái)完成的,但是要命的是,不知道什么原因,這個(gè)函數(shù)的參數(shù)使用的是char*型的:
            DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );
            這個(gè)時(shí)候我們可能就需要這個(gè)方法了:
            CString strDest;
            Int nDestLen = 100;
            DWORD dwRet = Translate( _strSrc.GetBuffer( _strSrc.GetLength() ), 
            strDest.GetBuffer(nDestLen),
            _strSrc.GetLength(), nDestlen );
            _strSrc.ReleaseBuffer();
            strDest.ReleaseBuffer();
            if ( SUCCESSCALL(dwRet) )
            {
            }
            if ( FAILEDCALL(dwRet) )
            {
            }

            的確,這種情況是存在的,但是,我還是建議盡量避免這種用法,如果確實(shí)需要使用,請(qǐng)不要使用一個(gè)專門的指針來(lái)保存GetBuffer返回的值,因?yàn)檫@樣常常會(huì)讓我們忘記調(diào)用ReleaseBuffer。就像上面的代碼,我們可以在調(diào)用GetBuffer之后馬上就調(diào)用ReleaseBuffer來(lái)調(diào)整CString對(duì)象。

            2. LPCTSTR

            關(guān)于LPCTSTR的錯(cuò)誤常常發(fā)生在初學(xué)者身上。
            例如在調(diào)用函數(shù)
            DWORD Translate( char* pSrc, char *pDest, int nSrcLen, int nDestLen );
            時(shí),初學(xué)者常常使用的方法就是:
            int nLen = _strSrc.GetLength();
            DWORD dwRet = Translate( (char*)(LPCTSTR)_strSrc), 
            (char*)(LPCTSTR)_strSrc),
            nLen,
            nLen);
            if ( SUCCESSCALL(dwRet) )
            {
            }
            if ( FAILEDCALL(dwRet) )
            {
            }

            他原本的初衷是將轉(zhuǎn)換后的字符串仍然放在_strSrc中,但是,當(dāng)調(diào)用完Translate以后之后再使用_strSrc時(shí),卻發(fā)現(xiàn)_strSrc已經(jīng)工作不正常了。檢查代碼卻又找不到問(wèn)題到底出在哪里。

            其實(shí)這個(gè)問(wèn)題和第一個(gè)問(wèn)題是一樣的。CString類已經(jīng)將LPCTST重載了。在CString中LPCTST實(shí)際上已經(jīng)是一個(gè)operation了。對(duì)LPCTST的調(diào)用實(shí)際上和GetBuffer是類似的,直接返回CStringData對(duì)象中的字符串緩沖的首地址。
            其C++代碼實(shí)現(xiàn)是:
            _AFX_INLINE CString::operator LPCTSTR() const
            { return m_pchData; }

            因此在使用完以后同樣需要調(diào)用ReleaseBuffer()。
            但是,這個(gè)誰(shuí)又能看出來(lái)呢?

            其實(shí)這個(gè)問(wèn)題的本質(zhì)原因出在類型轉(zhuǎn)換上。LPCTSTR返回的是一個(gè)const char*類型,因此使用這個(gè)指針來(lái)調(diào)用Translate編譯是不能通過(guò)的。對(duì)于一個(gè)初學(xué)者,或者一個(gè)有很長(zhǎng)編程經(jīng)驗(yàn)的人都會(huì)再通過(guò)強(qiáng)行類型轉(zhuǎn)換將const char*轉(zhuǎn)換為char*。最終造成了CString工作不正常,并且這樣也很容易造成緩沖溢出。

            通過(guò)上面對(duì)于CString機(jī)制和一些容易出現(xiàn)的使用錯(cuò)誤的描述,可以使我們更好的使用CString。其實(shí)每次CString賦值,如果buffer相差很大的話,(string每次分配內(nèi)存都是留了余地的,比需求大,除了默認(rèn)初始化)就會(huì)重新分配,但通過(guò)strcpy((LPSTR)(LPCTSTR)str,"Hello!");越過(guò)了重新分配的環(huán)節(jié),說(shuō)通俗點(diǎn)其實(shí)就是內(nèi)存拷貝越界!

            posted on 2011-12-28 22:55 tqsheng 閱讀(676) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            国产成人精品久久一区二区三区av| 国产综合免费精品久久久| 国产高清国内精品福利99久久| 中文字幕人妻色偷偷久久| 亚洲国产精品成人AV无码久久综合影院| 99久久精品费精品国产一区二区| 99久久免费国产精品特黄| 久久午夜免费视频| 久久综合亚洲鲁鲁五月天| 亚洲欧洲精品成人久久奇米网| 手机看片久久高清国产日韩| 久久国产精品波多野结衣AV| 久久精品这里只有精99品| 久久国产精品偷99| 色99久久久久高潮综合影院| 欧美午夜精品久久久久久浪潮| 青青热久久国产久精品| 久久乐国产综合亚洲精品| 2021国产精品久久精品| 国产成人精品综合久久久| 久久受www免费人成_看片中文| 久久精品国产亚洲av麻豆图片 | 欧美亚洲国产精品久久| 2020国产成人久久精品 | 亚洲欧美日韩久久精品第一区| 97精品伊人久久久大香线蕉 | 97精品伊人久久久大香线蕉| 国产午夜精品久久久久九九电影| 久久亚洲高清综合| 99蜜桃臀久久久欧美精品网站| 99久久无码一区人妻a黑| 精品国产91久久久久久久a| 久久精品国产男包| 久久精品国产亚洲AV无码偷窥| 国产精品日韩欧美久久综合| 久久中文字幕精品| 成人国内精品久久久久影院| 日本加勒比久久精品| 91精品国产91久久久久福利| 人人狠狠综合久久亚洲高清| 久久亚洲AV成人无码国产|