• <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>

            road420

            導(dǎo)航

            <2025年6月>
            25262728293031
            1234567
            891011121314
            15161718192021
            22232425262728
            293012345

            統(tǒng)計(jì)

            常用鏈接

            留言簿(2)

            隨筆檔案

            文章檔案

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            #

            #define

            今天整理了一些#define的用法,與大家共享!
            1.簡(jiǎn)單的define定義

            #define MAXTIME 1000 

            一個(gè)簡(jiǎn)單的MAXTIME就定義好了,它代表1000,如果在程序里面寫(xiě)

            if(i<MAXTIME){.........}

            編譯器在處理這個(gè)代碼之前會(huì)對(duì)MAXTIME進(jìn)行處理替換為1000。

            這樣的定義看起來(lái)類(lèi)似于普通的常量定義CONST,但也有著不同,因?yàn)閐efine的定義更像是簡(jiǎn)單的文本替換,而不是作為一個(gè)量來(lái)使用,這個(gè)問(wèn)題在下面反映的尤為突出。

            2.define的“函數(shù)定義”

            define可以像函數(shù)那樣接受一些參數(shù),如下

            #define max(x,y) (x)>(y)?(x):(y);

            這個(gè)定義就將返回兩個(gè)數(shù)中較大的那個(gè),看到了嗎?因?yàn)檫@個(gè)“函數(shù)”沒(méi)有類(lèi)型檢查,就好像一個(gè)函數(shù)模板似的,當(dāng)然,它絕對(duì)沒(méi)有模板那么安全就是了??梢宰鳛橐粋€(gè)簡(jiǎn)單的模板來(lái)使用而已。

            但是這樣做的話存在隱患,例子如下:
            #define Add(a,b) a+b;
            在一般使用的時(shí)候是沒(méi)有問(wèn)題的,但是如果遇到如:c * Add(a,b) * d的時(shí)候就會(huì)出現(xiàn)問(wèn)題,代數(shù)式的本意是a+b然后去和c,d相乘,但是因?yàn)槭褂昧薲efine(它只是一個(gè)簡(jiǎn)單的替換),所以式子實(shí)際上變成了
            c*a + b*d

            另外舉一個(gè)例子:
            #define pin (int*);
            pin a,b;
            本意是a和b都是int型指針,但是實(shí)際上變成int* a,b;
            a是int型指針,而b是int型變量。
            這是應(yīng)該使用typedef來(lái)代替define,這樣a和b就都是int型指針了。

            所以我們?cè)诙x的時(shí)候,養(yǎng)成一個(gè)良好的習(xí)慣,建議所有的層次都要加括號(hào)。

            3.宏的單行定義
            #define A(x) T_##x
            #define B(x) #@x

            #define C(x) #x
            我們假設(shè):x=1,則有:
            A(1)------〉T_1
            B(1)------〉'1'
            C(1)------〉"1"

            (這里參考了 hustli的文章)

            3.define的多行定義

            define可以替代多行的代碼,例如MFC中的宏定義(非常的經(jīng)典,雖然讓人看了惡心)

            #define MACRO(arg1, arg2) do { \
            /* declarations */ \
            stmt1; \
            stmt2; \
            /* ... */ \
            } while(0) /* (no trailing ; ) */

            #define DECLARE_RTTI(thisClass, superClass)\
              virtual const char* GetClassName() const\
              {return #thisClass;}\
              static int isTypeOf(const char* type)\
              {\
               if(!strcmp(#thisClass, type)\
                return 1;\
               return superClass::isTypeOf(type);\
               return 0;\
              }\
              virtual int isA(const char* type)\
              {\
               return thisClass::isTypeOf(type);\
              }\
              static thisClass* SafeDownCast(DitkObject* o)\
              {\
               if(o&&o->isA(#thisClass))\
                return static_cast<thisClass*>(o);\
               return NULL;\
              }

            關(guān)鍵是要在每一個(gè)換行的時(shí)候加上一個(gè)"\" 

            摘抄自http://www.blog.edu.cn/user1/16293/archives/2005/115370.shtml 修補(bǔ)了幾個(gè)bug

            4.在大規(guī)模的開(kāi)發(fā)過(guò)程中,特別是跨平臺(tái)和系統(tǒng)的軟件里,define最重要的功能是條件編譯。

            就是:
            #ifdef WINDOWS
            ......
            ......
            #endif
            #ifdef LINUX
            ......
            ......
            #endif

            可以在編譯的時(shí)候通過(guò)#define設(shè)置編譯環(huán)境

            5.如何定義宏、取消宏

            //定義宏
            #define [MacroName] [MacroValue]
            //取消宏
            #undef [MacroName]
            普通宏
            #define PI (3.1415926)

            帶參數(shù)的宏
            #define max(a,b) ((a)>(b)? (a),(b))
            關(guān)鍵是十分容易產(chǎn)生錯(cuò)誤,包括機(jī)器和人理解上的差異等等。

            6.條件編譯
            #ifdef XXX…(#else) …#endif
            例如 #ifdef DV22_AUX_INPUT
            #define AUX_MODE 3 
            #else
            #define AUY_MODE 3
            #endif
            #ifndef XXX … (#else) … #endif

            7.頭文件(.h)可以被頭文件或C文件包含;
            重復(fù)包含(重復(fù)定義)
            由于頭文件包含可以嵌套,那么C文件就有可能包含多次同一個(gè)頭文件,就可能出現(xiàn)重復(fù)定義的問(wèn)題的。
            通過(guò)條件編譯開(kāi)關(guān)來(lái)避免重復(fù)包含(重復(fù)定義)
            例如
            #ifndef __headerfileXXX__
            #define __headerfileXXX__

            文件內(nèi)容

            #endif

            posted @ 2008-07-09 08:48 深邃者 閱讀(268) | 評(píng)論 (0)編輯 收藏

            回調(diào)函數(shù)

            簡(jiǎn)介

              對(duì)于很多初學(xué)者來(lái)說(shuō),往往覺(jué)得回調(diào)函數(shù)很神秘,很想知道回調(diào)函數(shù)的工作原理。本文將要解釋什么是回調(diào)函數(shù)、它們有什么好處、為什么要使用它們等等問(wèn)題,在開(kāi)始之前,假設(shè)你已經(jīng)熟知了函數(shù)指針。

              什么是回調(diào)函數(shù)?

              簡(jiǎn)而言之,回調(diào)函數(shù)就是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用為調(diào)用它所指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù)。

              為什么要使用回調(diào)函數(shù)?

              因?yàn)榭梢园颜{(diào)用者與被調(diào)用者分開(kāi)。調(diào)用者不關(guān)心誰(shuí)是被調(diào)用者,所有它需知道的,只是存在一個(gè)具有某種特定原型、某些限制條件(如返回值為int)的被調(diào)用函數(shù)。

              如果想知道回調(diào)函數(shù)在實(shí)際中有什么作用,先假設(shè)有這樣一種情況,我們要編寫(xiě)一個(gè)庫(kù),它提供了某些排序算法的實(shí)現(xiàn),如冒泡排序、快速排序、shell排序、shake排序等等,但為使庫(kù)更加通用,不想在函數(shù)中嵌入排序邏輯,而讓使用者來(lái)實(shí)現(xiàn)相應(yīng)的邏輯;或者,想讓庫(kù)可用于多種數(shù)據(jù)類(lèi)型(int、float、string),此時(shí),該怎么辦呢?可以使用函數(shù)指針,并進(jìn)行回調(diào)。

              回調(diào)可用于通知機(jī)制,例如,有時(shí)要在程序中設(shè)置一個(gè)計(jì)時(shí)器,每到一定時(shí)間,程序會(huì)得到相應(yīng)的通知,但通知機(jī)制的實(shí)現(xiàn)者對(duì)我們的程序一無(wú)所知。而此時(shí),就需有一個(gè)特定原型的函數(shù)指針,用這個(gè)指針來(lái)進(jìn)行回調(diào),來(lái)通知我們的程序事件已經(jīng)發(fā)生。實(shí)際上,SetTimer() API使用了一個(gè)回調(diào)函數(shù)來(lái)通知計(jì)時(shí)器,而且,萬(wàn)一沒(méi)有提供回調(diào)函數(shù),它還會(huì)把一個(gè)消息發(fā)往程序的消息隊(duì)列。

              另一個(gè)使用回調(diào)機(jī)制的API函數(shù)是EnumWindow(),它枚舉屏幕上所有的頂層窗口,為每個(gè)窗口調(diào)用一個(gè)程序提供的函數(shù),并傳遞窗口的處理程序。如果被調(diào)用者返回一個(gè)值,就繼續(xù)進(jìn)行迭代,否則,退出。EnumWindow()并不關(guān)心被調(diào)用者在何處,也不關(guān)心被調(diào)用者用它傳遞的處理程序做了什么,它只關(guān)心返回值,因?yàn)榛诜祷刂担鼘⒗^續(xù)執(zhí)行或退出。

              不管怎么說(shuō),回調(diào)函數(shù)是繼續(xù)自C語(yǔ)言的,因而,在C++中,應(yīng)只在與C代碼建立接口,或與已有的回調(diào)接口打交道時(shí),才使用回調(diào)函數(shù)。除了上述情況,在C++中應(yīng)使用虛擬方法或函數(shù)符(functor),而不是回調(diào)函數(shù)。

              一個(gè)簡(jiǎn)單的回調(diào)函數(shù)實(shí)現(xiàn)

              下面創(chuàng)建了一個(gè)sort.dll的動(dòng)態(tài)鏈接庫(kù),它導(dǎo)出了一個(gè)名為CompareFunction的類(lèi)型--typedef int (__stdcall *CompareFunction)(const byte*, const byte*),它就是回調(diào)函數(shù)的類(lèi)型。另外,它也導(dǎo)出了兩個(gè)方法:Bubblesort()和Quicksort(),這兩個(gè)方法原型相同,但實(shí)現(xiàn)了不同的排序算法。

            void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc);

            void DLLDIR __stdcall Quicksort(byte* array,int size,int elem_size,CompareFunction cmpFunc);

              這兩個(gè)函數(shù)接受以下參數(shù):

              ·byte * array:指向元素?cái)?shù)組的指針(任意類(lèi)型)。

              ·int size:數(shù)組中元素的個(gè)數(shù)。

              ·int elem_size:數(shù)組中一個(gè)元素的大小,以字節(jié)為單位。

              ·CompareFunction cmpFunc:帶有上述原型的指向回調(diào)函數(shù)的指針。

              這兩個(gè)函數(shù)的會(huì)對(duì)數(shù)組進(jìn)行某種排序,但每次都需決定兩個(gè)元素哪個(gè)排在前面,而函數(shù)中有一個(gè)回調(diào)函數(shù),其地址是作為一個(gè)參數(shù)傳遞進(jìn)來(lái)的。對(duì)編寫(xiě)者來(lái)說(shuō),不必介意函數(shù)在何處實(shí)現(xiàn),或它怎樣被實(shí)現(xiàn)的,所需在意的只是兩個(gè)用于比較的元素的地址,并返回以下的某個(gè)值(庫(kù)的編寫(xiě)者和使用者都必須遵守這個(gè)約定):

              ·-1:如果第一個(gè)元素較小,那它在已排序好的數(shù)組中,應(yīng)該排在第二個(gè)元素前面。

              ·0:如果兩個(gè)元素相等,那么它們的相對(duì)位置并不重要,在已排序好的數(shù)組中,誰(shuí)在前面都無(wú)所謂。

              ·1:如果第一個(gè)元素較大,那在已排序好的數(shù)組中,它應(yīng)該排第二個(gè)元素后面。

              基于以上約定,函數(shù)Bubblesort()的實(shí)現(xiàn)如下,Quicksort()就稍微復(fù)雜一點(diǎn):

            void DLLDIR __stdcall Bubblesort(byte* array,int size,int elem_size,CompareFunction cmpFunc)
            {
             for(int i=0; i < size; i++)
             {
              for(int j=0; j < size-1; j++)
              {
               //回調(diào)比較函數(shù)
               if(1 == (*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size))
               {
                //兩個(gè)相比較的元素相交換
                byte* temp = new byte[elem_size];
                memcpy(temp, array+j*elem_size, elem_size);
                memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);
                memcpy(array+(j+1)*elem_size, temp, elem_size);
                delete [] temp;
               }
              }
             }
            }

              注意:因?yàn)閷?shí)現(xiàn)中使用了memcpy(),所以函數(shù)在使用的數(shù)據(jù)類(lèi)型方面,會(huì)有所局限。

              對(duì)使用者來(lái)說(shuō),必須有一個(gè)回調(diào)函數(shù),其地址要傳遞給Bubblesort()函數(shù)。下面有二個(gè)簡(jiǎn)單的示例,一個(gè)比較兩個(gè)整數(shù),而另一個(gè)比較兩個(gè)字符串:

            int __stdcall CompareInts(const byte* velem1, const byte* velem2)
            {
             int elem1 = *(int*)velem1;
             int elem2 = *(int*)velem2;

             if(elem1 < elem2)
              return -1;
             if(elem1 > elem2)
              return 1;

             return 0;
            }

            int __stdcall CompareStrings(const byte* velem1, const byte* velem2)
            {
             const char* elem1 = (char*)velem1;
             const char* elem2 = (char*)velem2;
             return strcmp(elem1, elem2);
            }

              下面另有一個(gè)程序,用于測(cè)試以上所有的代碼,它傳遞了一個(gè)有5個(gè)元素的數(shù)組給Bubblesort()和Quicksort(),同時(shí)還傳遞了一個(gè)指向回調(diào)函數(shù)的指針。

            int main(int argc, char* argv[])
            {
             int i;
             int array[] = {5432, 4321, 3210, 2109, 1098};

             cout << "Before sorting ints with Bubblesort\n";
             for(i=0; i < 5; i++)
              cout << array[i] << '\n';

             Bubblesort((byte*)array, 5, sizeof(array[0]), &CompareInts);

             cout << "After the sorting\n";
             for(i=0; i < 5; i++)
              cout << array[i] << '\n';

             const char str[5][10] = {"estella","danielle","crissy","bo","angie"};

             cout << "Before sorting strings with Quicksort\n";
             for(i=0; i < 5; i++)
              cout << str[i] << '\n';

             Quicksort((byte*)str, 5, 10, &CompareStrings);

             cout << "After the sorting\n";
             for(i=0; i < 5; i++)
              cout << str[i] << '\n';

             return 0;
            }

              如果想進(jìn)行降序排序(大元素在先),就只需修改回調(diào)函數(shù)的代碼,或使用另一個(gè)回調(diào)函數(shù),這樣編程起來(lái)靈活性就比較大了。

            調(diào)用約定

              上面的代碼中,可在函數(shù)原型中找到__stdcall,因?yàn)樗噪p下劃線打頭,所以它是一個(gè)特定于編譯器的擴(kuò)展,說(shuō)到底也就是微軟的實(shí)現(xiàn)。任何支持開(kāi)發(fā)基于Win32的程序都必須支持這個(gè)擴(kuò)展或其等價(jià)物。以__stdcall標(biāo)識(shí)的函數(shù)使用了標(biāo)準(zhǔn)調(diào)用約定,為什么叫標(biāo)準(zhǔn)約定呢,因?yàn)樗械腤in32 API(除了個(gè)別接受可變參數(shù)的除外)都使用它。標(biāo)準(zhǔn)調(diào)用約定的函數(shù)在它們返回到調(diào)用者之前,都會(huì)從堆棧中移除掉參數(shù),這也是Pascal的標(biāo)準(zhǔn)約定。但在C/C++中,調(diào)用約定是調(diào)用者負(fù)責(zé)清理堆棧,而不是被調(diào)用函數(shù);為強(qiáng)制函數(shù)使用C/C++調(diào)用約定,可使用__cdecl。另外,可變參數(shù)函數(shù)也使用C/C++調(diào)用約定。

              Windows操作系統(tǒng)采用了標(biāo)準(zhǔn)調(diào)用約定(Pascal約定),因?yàn)槠淇蓽p小代碼的體積。這點(diǎn)對(duì)早期的Windows來(lái)說(shuō)非常重要,因?yàn)槟菚r(shí)它運(yùn)行在只有640KB內(nèi)存的電腦上。

              如果你不喜歡__stdcall,還可以使用CALLBACK宏,它定義在windef.h中:

            #define CALLBACK __stdcallor

            #define CALLBACK PASCAL //而PASCAL在此被#defined成__stdcall

              作為回調(diào)函數(shù)的C++方法

              因?yàn)槠綍r(shí)很可能會(huì)使用到C++編寫(xiě)代碼,也許會(huì)想到把回調(diào)函數(shù)寫(xiě)成類(lèi)中的一個(gè)方法,但先來(lái)看看以下的代碼:

            class CCallbackTester
            {
             public:
             int CALLBACK CompareInts(const byte* velem1, const byte* velem2);
            };

            Bubblesort((byte*)array, 5, sizeof(array[0]),
            &CCallbackTester::CompareInts);

              如果使用微軟的編譯器,將會(huì)得到下面這個(gè)編譯錯(cuò)誤:

            error C2664: 'Bubblesort' : cannot convert parameter 4 from 'int (__stdcall CCallbackTester::*)(const unsigned char *,const unsigned char *)' to 'int (__stdcall *)(const unsigned char *,const unsigned char *)' There is no context in which this conversion is possible

              這是因?yàn)榉庆o態(tài)成員函數(shù)有一個(gè)額外的參數(shù):this指針,這將迫使你在成員函數(shù)前面加上static。當(dāng)然,還有幾種方法可以解決這個(gè)問(wèn)題,但限于篇幅,就不再論述了。

            posted @ 2008-07-05 10:11 深邃者 閱讀(352) | 評(píng)論 (1)編輯 收藏

            內(nèi)存分區(qū)

            五大內(nèi)存分區(qū)
                在C++中,內(nèi)存分成5個(gè)區(qū),他們分別是堆、棧、自由存儲(chǔ)區(qū)、全局/靜態(tài)存儲(chǔ)區(qū)和常量存儲(chǔ)區(qū)。
                棧,就是那些由編譯器在需要的時(shí)候分配,在不需要的時(shí)候自動(dòng)清楚的變量的存儲(chǔ)區(qū)。里面的變量通常是局部變量、函數(shù)參數(shù)等。
                堆,就是那些由new分配的內(nèi)存塊,他們的釋放編譯器不去管,由我們的應(yīng)用程序去控制,一般一個(gè)new就要對(duì)應(yīng)一個(gè)delete。如果程序員沒(méi)有釋放掉,那么在程序結(jié)束后,操作系統(tǒng)會(huì)自動(dòng)回收。
                自由存儲(chǔ)區(qū),就是那些由malloc等分配的內(nèi)存塊,他和堆是十分相似的,不過(guò)它是用free來(lái)結(jié)束自己的生命的。
                全局/靜態(tài)存儲(chǔ)區(qū),全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中,在以前的C語(yǔ)言中,全局變量又分為初始化的和未初始化的,在C++里面沒(méi)有這個(gè)區(qū)分了,他們共同占用同一塊內(nèi)存區(qū)。
                常量存儲(chǔ)區(qū),這是一塊比較特殊的存儲(chǔ)區(qū),他們里面存放的是常量,不允許修改(當(dāng)然,你要通過(guò)非正當(dāng)手段也可以修改,而且方法很多)
            明確區(qū)分堆與棧
                在bbs上,堆與棧的區(qū)分問(wèn)題,似乎是一個(gè)永恒的話題,由此可見(jiàn),初學(xué)者對(duì)此往往是混淆不清的,所以我決定拿他第一個(gè)開(kāi)刀。
                首先,我們舉一個(gè)例子:
                void f() { int* p=new int[5]; }
                這條短短的一句話就包含了堆與棧,看到new,我們首先就應(yīng)該想到,我們分配了一塊堆內(nèi)存,那么指針p呢?他分配的是一塊棧內(nèi)存,所以這句話的意思就是:在棧內(nèi)存中存放了一個(gè)指向一塊堆內(nèi)存的指針p。在程序會(huì)先確定在堆中分配內(nèi)存的大小,然后調(diào)用operator new分配內(nèi)存,然后返回這塊內(nèi)存的首地址,放入棧中,他在VC6下的匯編代碼如下:
                00401028   push        14h
                0040102A   call        operator new (00401060)
                0040102F   add         esp,4
                00401032   mov         dword ptr [ebp-8],eax
                00401035   mov         eax,dword ptr [ebp-8]
                00401038   mov         dword ptr [ebp-4],eax
                這里,我們?yōu)榱撕?jiǎn)單并沒(méi)有釋放內(nèi)存,那么該怎么去釋放呢?是delete p么?澳,錯(cuò)了,應(yīng)該是delete []p,這是為了告訴編譯器:我刪除的是一個(gè)數(shù)組,VC6就會(huì)根據(jù)相應(yīng)的Cookie信息去進(jìn)行釋放內(nèi)存的工作。
                好了,我們回到我們的主題:堆和棧究竟有什么區(qū)別?
                主要的區(qū)別由以下幾點(diǎn):
                1、管理方式不同;
                2、空間大小不同;
                3、能否產(chǎn)生碎片不同;
                4、生長(zhǎng)方向不同;
                5、分配方式不同;
                6、分配效率不同;
                管理方式:對(duì)于棧來(lái)講,是由編譯器自動(dòng)管理,無(wú)需我們手工控制;對(duì)于堆來(lái)說(shuō),釋放工作由程序員控制,容易產(chǎn)生memory leak。
                空間大?。阂话銇?lái)講在32位系統(tǒng)下,堆內(nèi)存可以達(dá)到4G的空間,從這個(gè)角度來(lái)看堆內(nèi)存幾乎是沒(méi)有什么限制的。但是對(duì)于棧來(lái)講,一般都是有一定的空間大小的,例如,在VC6下面,默認(rèn)的棧空間大小是1M(好像是,記不清楚了)。當(dāng)然,我們可以修改:   
                打開(kāi)工程,依次操作菜單如下:Project->Setting->Link,在Category 中選中Output,然后在Reserve中設(shè)定堆棧的最大值和commit。
            注意:reserve最小值為4Byte;commit是保留在虛擬內(nèi)存的頁(yè)文件里面,它設(shè)置的較大會(huì)使棧開(kāi)辟較大的值,可能增加內(nèi)存的開(kāi)銷(xiāo)和啟動(dòng)時(shí)間。
                碎片問(wèn)題:對(duì)于堆來(lái)講,頻繁的new/delete勢(shì)必會(huì)造成內(nèi)存空間的不連續(xù),從而造成大量的碎片,使程序效率降低。對(duì)于棧來(lái)講,則不會(huì)存在這個(gè)問(wèn)題,因?yàn)闂J窍冗M(jìn)后出的隊(duì)列,他們是如此的一一對(duì)應(yīng),以至于永遠(yuǎn)都不可能有一個(gè)內(nèi)存塊從棧中間彈出,在他彈出之前,在他上面的后進(jìn)的棧內(nèi)容已經(jīng)被彈出,詳細(xì)的可以參考數(shù)據(jù)結(jié)構(gòu),這里我們就不再一一討論了。
                生長(zhǎng)方向:對(duì)于堆來(lái)講,生長(zhǎng)方向是向上的,也就是向著內(nèi)存地址增加的方向;對(duì)于棧來(lái)講,它的生長(zhǎng)方向是向下的,是向著內(nèi)存地址減小的方向增長(zhǎng)。
                分配方式:堆都是動(dòng)態(tài)分配的,沒(méi)有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配是編譯器完成的,比如局部變量的分配。動(dòng)態(tài)分配由alloca函數(shù)進(jìn)行分配,但是棧的動(dòng)態(tài)分配和堆是不同的,他的動(dòng)態(tài)分配是由編譯器進(jìn)行釋放,無(wú)需我們手工實(shí)現(xiàn)。
                分配效率:棧是機(jī)器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)會(huì)在底層對(duì)棧提供支持:分配專(zhuān)門(mén)的寄存器存放棧的地址,壓棧出棧都有專(zhuān)門(mén)的指令執(zhí)行,這就決定了棧的效率比較高。堆則是C/C++函數(shù)庫(kù)提供的,它的機(jī)制是很復(fù)雜的,例如為了分配一塊內(nèi)存,庫(kù)函數(shù)會(huì)按照一定的算法(具體的算法可以參考數(shù)據(jù)結(jié)構(gòu)/操作系統(tǒng))在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒(méi)有足夠大小的空間(可能是由于內(nèi)存碎片太多),就有可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,這樣就有機(jī)會(huì)分到足夠大小的內(nèi)存,然后進(jìn)行返回。顯然,堆的效率比棧要低得多。
                從這里我們可以看到,堆和棧相比,由于大量new/delete的使用,容易造成大量的內(nèi)存碎片;由于沒(méi)有專(zhuān)門(mén)的系統(tǒng)支持,效率很低;由于可能引發(fā)用戶(hù)態(tài)和核心態(tài)的切換,內(nèi)存的申請(qǐng),代價(jià)變得更加昂貴。所以棧在程序中是應(yīng)用最廣泛的,就算是函數(shù)的調(diào)用也利用棧去完成,函數(shù)調(diào)用過(guò)程中的參數(shù),返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。
                雖然棧有如此眾多的好處,但是由于和堆相比不是那么靈活,有時(shí)候分配大量的內(nèi)存空間,還是用堆好一些。
                無(wú)論是堆還是棧,都要防止越界現(xiàn)象的發(fā)生(除非你是故意使其越界),因?yàn)樵浇绲慕Y(jié)果要么是程序崩潰,要么是摧毀程序的堆、棧結(jié)構(gòu),產(chǎn)生以想不到的結(jié)果,就算是在你的程序運(yùn)行過(guò)程中,沒(méi)有發(fā)生上面的問(wèn)題,你還是要小心,說(shuō)不定什么時(shí)候就崩掉,那時(shí)候debug可是相當(dāng)困難的:)
                對(duì)了,還有一件事,如果有人把堆棧合起來(lái)說(shuō),那它的意思是棧,可不是堆,呵呵,清楚了?
            static用來(lái)控制變量的存儲(chǔ)方式和可見(jiàn)性
                   函數(shù)內(nèi)部定義的變量,在程序執(zhí)行到它的定義處時(shí),編譯器為它在棧上分配空間,函數(shù)在棧上分配的空間在此函數(shù)執(zhí)行結(jié)束時(shí)會(huì)釋放掉,這樣就產(chǎn)生了一個(gè)問(wèn)題: 如果想將函數(shù)中此變量的值保存至下一次調(diào)用時(shí),如何實(shí)現(xiàn)? 最容易想到的方法是定義一個(gè)全局的變量,但定義為一個(gè)全局變量有許多缺點(diǎn),最明顯的缺點(diǎn)是破壞了此變量的訪問(wèn)范圍(使得在此函數(shù)中定義的變量,不僅僅受此函數(shù)控制)。

                   需要一個(gè)數(shù)據(jù)對(duì)象為整個(gè)類(lèi)而非某個(gè)對(duì)象服務(wù),同時(shí)又力求不破壞類(lèi)的封裝性,即要求此成員隱藏在類(lèi)的內(nèi)部,對(duì)外不可見(jiàn)。

                   static的內(nèi)部機(jī)制:
                   靜態(tài)數(shù)據(jù)成員要在程序一開(kāi)始運(yùn)行時(shí)就必須存在。因?yàn)楹瘮?shù)在程序運(yùn)行中被調(diào)用,所以靜態(tài)數(shù)據(jù)成員不能在任何函數(shù)內(nèi)分配空間和初始化。
                   這樣,它的空間分配有三個(gè)可能的地方,一是作為類(lèi)的外部接口的頭文件,那里有類(lèi)聲明;二是類(lèi)定義的內(nèi)部實(shí)現(xiàn),那里有類(lèi)的成員函數(shù)定義;三是應(yīng)用程序的main()函數(shù)前的全局?jǐn)?shù)據(jù)聲明和定義處。
                  靜態(tài)數(shù)據(jù)成員要實(shí)際地分配空間,故不能在類(lèi)的聲明中定義(只能聲明數(shù)據(jù)成員)。類(lèi)聲明只聲明一個(gè)類(lèi)的“尺寸和規(guī)格”,并不進(jìn)行實(shí)際的內(nèi)存分配,所以在類(lèi)聲明中寫(xiě)成定義是錯(cuò)誤的。它也不能在頭文件中類(lèi)聲明的外部定義,因?yàn)槟菚?huì)造成在多個(gè)使用該類(lèi)的源文件中,對(duì)其重復(fù)定義。
                  static被引入以告知編譯器,將變量存儲(chǔ)在程序的靜態(tài)存儲(chǔ)區(qū)而非棧上空間,靜態(tài)
            數(shù)據(jù)成員按定義出現(xiàn)的先后順序依次初始化,注意靜態(tài)成員嵌套時(shí),要保證所嵌套的成員已經(jīng)初始化了。消除時(shí)的順序是初始化的反順序。

                   static的優(yōu)勢(shì):
                   可以節(jié)省內(nèi)存,因?yàn)樗撬袑?duì)象所公有的,因此,對(duì)多個(gè)對(duì)象來(lái)說(shuō),靜態(tài)數(shù)據(jù)成員只存儲(chǔ)一處,供所有對(duì)象共用。靜態(tài)數(shù)據(jù)成員的值對(duì)每個(gè)對(duì)象都是一樣,但它的值是可以更新的。只要對(duì)靜態(tài)數(shù)據(jù)成員的值更新一次,保證所有對(duì)象存取更新后的相同的值,這樣可以提高時(shí)間效率。

                    引用靜態(tài)數(shù)據(jù)成員時(shí),采用如下格式:
                     <類(lèi)名>::<靜態(tài)成員名>
                如果靜態(tài)數(shù)據(jù)成員的訪問(wèn)權(quán)限允許的話(即public的成員),可在程序中,按上述格式
            來(lái)引用靜態(tài)數(shù)據(jù)成員。

                   PS:
                  (1)類(lèi)的靜態(tài)成員函數(shù)是屬于整個(gè)類(lèi)而非類(lèi)的對(duì)象,所以它沒(méi)有this指針,這就導(dǎo)致
            了它僅能訪問(wèn)類(lèi)的靜態(tài)數(shù)據(jù)和靜態(tài)成員函數(shù)。
                  (2)不能將靜態(tài)成員函數(shù)定義為虛函數(shù)。
                  (3)由于靜態(tài)成員聲明于類(lèi)中,操作于其外,所以對(duì)其取地址操作,就多少有些特殊
            ,變量地址是指向其數(shù)據(jù)類(lèi)型的指針 ,函數(shù)地址類(lèi)型是一個(gè)“nonmember函數(shù)指針”。

                  (4)由于靜態(tài)成員函數(shù)沒(méi)有this指針,所以就差不多等同于nonmember函數(shù),結(jié)果就
            產(chǎn)生了一個(gè)意想不到的好處:成為一個(gè)callback函數(shù),使得我們得以將C++和C-based X W
            indow系統(tǒng)結(jié)合,同時(shí)也成功的應(yīng)用于線程函數(shù)身上。
                  (5)static并沒(méi)有增加程序的時(shí)空開(kāi)銷(xiāo),相反她還縮短了子類(lèi)對(duì)父類(lèi)靜態(tài)成員的訪問(wèn)
            時(shí)間,節(jié)省了子類(lèi)的內(nèi)存空間。
                  (6)靜態(tài)數(shù)據(jù)成員在<定義或說(shuō)明>時(shí)前面加關(guān)鍵字static。
                  (7)靜態(tài)數(shù)據(jù)成員是靜態(tài)存儲(chǔ)的,所以必須對(duì)它進(jìn)行初始化。
                  (8)靜態(tài)成員初始化與一般數(shù)據(jù)成員初始化不同:
                  初始化在類(lèi)體外進(jìn)行,而前面不加static,以免與一般靜態(tài)變量或?qū)ο笙嗷煜?br>      初始化時(shí)不加該成員的訪問(wèn)權(quán)限控制符private,public等;
                       初始化時(shí)使用作用域運(yùn)算符來(lái)標(biāo)明它所屬類(lèi);
                       所以我們得出靜態(tài)數(shù)據(jù)成員初始化的格式:
                     <數(shù)據(jù)類(lèi)型><類(lèi)名>::<靜態(tài)數(shù)據(jù)成員名>=<值>
                  (9)為了防止父類(lèi)的影響,可以在子類(lèi)定義一個(gè)與父類(lèi)相同的靜態(tài)變量,以屏蔽父類(lèi)的影響。這里有一點(diǎn)需要注意:我們說(shuō)靜態(tài)成員為父類(lèi)和子類(lèi)共享,但我們有重復(fù)定義了靜態(tài)成員,這會(huì)不會(huì)引起錯(cuò)誤呢?不會(huì),我們的編譯器采用了一種絕妙的手法:name-mangling 用以生成唯一的標(biāo)志。

            posted @ 2008-07-05 10:05 深邃者 閱讀(169) | 評(píng)論 (0)編輯 收藏

            WM_DESTROY 和 WM_NCDESTROY

            WM_DESTROY 和 WM_NCDESTROY 消息之間有什么區(qū)別?

            原文鏈接 What is the difference between WM_DESTROY and WM_NCDESTROY?

            在窗口銷(xiāo)毀時(shí)有兩個(gè)緊密關(guān)聯(lián)的 windows 消息, 就是 WM_DESTROY 和 WM_NCDESTROY. 它們有何區(qū)別?

            區(qū)別就是 WM_DESTROY 消息是在窗口銷(xiāo)毀動(dòng)作序列中的開(kāi)始被發(fā)送的, 而 WM_NCDESTROY 消息是在結(jié)尾. 這在你的窗口擁有子窗口時(shí)是個(gè)重大區(qū)別. 如果你有一個(gè)帶子窗口的父窗口, 那么消息的發(fā)送序列 (在沒(méi)有怪誕行為影響的前提下) 就像這樣:

            hwnd = parent, uMsg = WM_DESTROY
            hwnd = child, uMsg = WM_DESTROY
            hwnd = child, uMsg = WM_NCDESTROY
            hwnd = parent, uMsg = WM_NCDESTROY

            注意, 父窗口是在子窗口被銷(xiāo)毀之前收到 WM_DESTROY 消息, 在子窗口被銷(xiāo)毀之后收到 WM_NCDESTROY 消息.

            兩個(gè)銷(xiāo)毀消息, 一個(gè)在開(kāi)頭, 一個(gè)在結(jié)尾, 這意味著, 對(duì)于你自己的模塊, 你可以通過(guò)處理相應(yīng)的消息來(lái)執(zhí)行清理操作.
            例如, 如果有些東西必須在開(kāi)頭清理, 那么你可以使用 WM_DESTROY 消息.

            WM_NCDESTROY 消息是你窗口將會(huì)收到的最后一個(gè)消息 (在沒(méi)有怪誕行為影響的前提下), 因此, 這里是做 "最終清理" 的最佳場(chǎng)所.
            這就是為什么我們的 new scratch 程序會(huì)一直等到 WM_NCDESTROY 銷(xiāo)毀它的實(shí)例變量, 才會(huì)返回.

            與這兩個(gè)銷(xiāo)毀消息配對(duì)的, 是 WM_CREATE 和 WM_NCCREATE 這兩個(gè)類(lèi)似的消息. 與 WM_NCDESTROY 是你窗口收到的最后一條消息類(lèi)似,
            WM_NCCREATE 消息是第一條消息, 這是一個(gè)創(chuàng)建你自己的實(shí)例變量的好地方. 需要注意的是, 如果你導(dǎo)致 WM_NCCREATE 消息返回失敗,
            那么所有你將收到的消息就只有 WM_NCDESTROY 了; 不會(huì)有 WM_DESTROY 消息了, 因?yàn)槟愀揪蜎](méi)有收到相應(yīng)的 WM_CREATE 消息.

            那么什么是我一直在暗示的 "怪誕行為" 呢? 下一次 (When the normal window destruction messages are thrown for a loop) 我們?cè)僬f(shuō)

            posted @ 2008-01-23 18:21 深邃者 閱讀(365) | 評(píng)論 (0)編輯 收藏

            懸掛指針

            懸掛指針與boost::weak_ptr

               與內(nèi)存泄露相比,C++最令人頭痛的問(wèn)題是內(nèi)存越界,而內(nèi)存越界很多情況下是由于懸掛指針引起的?! ?br>  假設(shè)一個(gè)指針變量:
              Object * ptr;
              使用ptr時(shí),我們除了要判斷ptr是否為0以外,還要懷疑它指向的對(duì)象是否有效,是不是已經(jīng)在別的地方被銷(xiāo)毀了。我們希望當(dāng)它指向的對(duì)象被銷(xiāo)毀時(shí),ptr被自動(dòng)置為0。
              顯然,C++沒(méi)有這種機(jī)制,但是,可以借助于boost::weak_ptr做到這一點(diǎn)。

            inline void null_deleter(void const *
            {
            }

            class X
            {
            private:

                shared_ptr
            <X> this_;
                
            int i_;

            public:

                
            explicit X(int i): this_(this, &null_deleter), i_(i)
                {
                }

                X(X 
            const & rhs): this_(this, &null_deleter), i_(rhs.i_)
                {
                }

                X 
            & operator=(X const & rhs)
                {
                    i_ 
            = rhs.i_;
                }

                weak_ptr
            <X> weak_this() const { return this_; }
            };


             

            定義變量:
            weak_ptr<X> ptr = x.weak_this();  // x為一個(gè)X 對(duì)象 

            則當(dāng) x 被銷(xiāo)毀時(shí),ptr 被自動(dòng)置為無(wú)效。使用方法如下:

            if ( shard_ptr<X>  safePtr  = ptr.lock() )  safePtr->do_something();

            這種辦法用于單線程中,因?yàn)?x  對(duì)象可能是基于棧分配的。如果需要在多線程中訪問(wèn)X對(duì)象,那么最好的辦法還是使用shared_ptr 來(lái)管理對(duì)象的生命期。這樣的話,對(duì)于safePtr, 可以保證在 safePtr 的生命期內(nèi),它所指向的對(duì)象不會(huì)被其它線程刪除。

            posted @ 2007-11-09 19:28 深邃者 閱讀(553) | 評(píng)論 (0)編輯 收藏

            CString

            1. CString實(shí)現(xiàn)的機(jī)制.

            CString是通過(guò)“引用”來(lái)管理串的,“引用”這個(gè)詞我相信大家并不陌生,象Window內(nèi)核對(duì)象、COM對(duì)象等都是通過(guò)引用來(lái)實(shí)現(xiàn)的。而CString也是通過(guò)這樣的機(jī)制來(lái)管理分配的內(nèi)存塊。實(shí)際上CString對(duì)象只有一個(gè)指針成員變量,所以任何CString實(shí)例的長(zhǎng)度只有4字節(jié).

            即: int len = sizeof(CString);//len等于4

            這個(gè)指針指向一個(gè)相關(guān)的引用內(nèi)存塊,如圖: CString str("abcd");

            ‘A’

            ‘B’

            ‘C’

            ‘D’

            0

            0x04040404 head部,為引用內(nèi)存塊相關(guān)信息

            str 0x40404040

            正因?yàn)槿绱?,一個(gè)這樣的內(nèi)存塊可被多個(gè)CString所引用,例如下列代碼:

            CString str("abcd");

            CString a = str;

            CString b(str);

            CString c;

            c = b;

            上面代碼的結(jié)果是:上面四個(gè)對(duì)象(str,a,b,c)中的成員變量指針有相同的值,都為0x40404040.而這塊內(nèi)存塊怎么知道有多少個(gè)CString引用它呢?同樣,它也會(huì)記錄一些信息。如被引用數(shù),串長(zhǎng)度,分配內(nèi)存長(zhǎng)度。

            這塊引用內(nèi)存塊的結(jié)構(gòu)定義如下:

            struct CStringData

            {

            long nRefs; //表示有多少個(gè)CString 引用它. 4

            int nDataLength; //串實(shí)際長(zhǎng)度. 4

            int nAllocLength; //總共分配的內(nèi)存長(zhǎng)度(不計(jì)這頭部的12字節(jié)). 4

            };

            由于有了這些信息,CString就能正確地分配、管理、釋放引用內(nèi)存塊。

            如果你想在調(diào)試程序的時(shí)候獲得這些信息。可以在Watch窗口鍵入下列表達(dá)式:

            (CStringData*)((CStringData*)(this->m_pchData)-1)或

            (CStringData*)((CStringData*)(str.m_pchData)-1)//str為指CString實(shí)例

            正因?yàn)椴捎昧诉@樣的好機(jī)制,使得CString在大量拷貝時(shí),不僅效率高,而且分配內(nèi)存少。

            2.LPCTSTR 與 GetBuffer(int nMinBufLength)

            這兩個(gè)函數(shù)提供了與標(biāo)準(zhǔn)C的兼容轉(zhuǎn)換。在實(shí)際中使用頻率很高,但卻是最容易出錯(cuò)的地方。這兩個(gè)函數(shù)實(shí)際上返回的都是指針,但它們有何區(qū)別呢?以及調(diào)用它們后,幕后是做了怎樣的處理過(guò)程呢?

            (1) LPCTSTR 它的執(zhí)行過(guò)程其實(shí)很簡(jiǎn)單,只是返回引用內(nèi)存塊的串地址。 它是作為操作符重載提供的,所以在代碼中有時(shí)可以隱式轉(zhuǎn)換,而有時(shí)卻需強(qiáng)制轉(zhuǎn)制。如:

            CString str;

            const char* p = (LPCTSTR)str;

            //假設(shè)有這樣的一個(gè)函數(shù),Test(const char* p); 你就可以這樣調(diào)用

            Test(str);//這里會(huì)隱式轉(zhuǎn)換為L(zhǎng)PCTSTR

            (2) GetBuffer(int nMinBufLength) 它類(lèi)似,也會(huì)返回一個(gè)指針,不過(guò)它有點(diǎn)差別,返回的是LPTSTR

            (3) 這兩者到底有何不同呢?我想告訴大家,其本質(zhì)上完全不一樣,一般說(shuō)LPCTSTR轉(zhuǎn)換后只應(yīng)該當(dāng)常量使用,或者做函數(shù)的入?yún)?;而GetBuffer(...)取出指針后,可以通過(guò)這個(gè)指針來(lái)修改里面的內(nèi)容,或者做函數(shù)的出參。為什么呢?也許經(jīng)常有這樣的代碼:

            CString str("abcd");

            char* p = (char*)(const char*)str;

            p[2] = 'z';

            其實(shí),也許有這樣的代碼后,你的程序并沒(méi)有錯(cuò),而且程序也運(yùn)行得挺好。但它卻是非常危險(xiǎn)的。再看

            CString str("abcd");

            CString test = str;

            ....

            char* p = (char*)(const char*)str;

            p[2] = 'z';

            strcpy(p, "akfjaksjfakfakfakj");//這下完蛋了

            你知道此時(shí),test中的值是多少嗎?答案是"abzd"。它也跟著改變了,這不是你所期望發(fā)生的。但為什么會(huì)這樣呢?你稍微想想就會(huì)明白,前面說(shuō)過(guò),因?yàn)镃String是指向引用塊的,str與test指向同一塊地方,當(dāng)你p[2]='z'后,當(dāng)然test也會(huì)隨著改變。所以用它做LPCTSTR做轉(zhuǎn)換后,你只能去讀這塊數(shù)據(jù),千萬(wàn)別去改變它的內(nèi)容。

            假如我想直接通過(guò)指針去修改數(shù)據(jù)的話,那怎樣辦呢?就是用GetBuffer(...).看下述代碼:

            CString str("abcd");

            CString test = str;

            ....

            char* p = str.GetBuffer(20);

            p[2] = 'z'; // 執(zhí)行到此,現(xiàn)在test中值卻仍是"abcd"

            strcpy(p, "akfjaksjfakfakfakj"); // 執(zhí)行到此,現(xiàn)在test中值還是"abcd"

            為什么會(huì)這樣?其實(shí)GetBuffer(20)調(diào)用時(shí),它實(shí)際上另外建立了一塊新內(nèi)塊存,并分配20字節(jié)長(zhǎng)度的buffer,而原來(lái)的內(nèi)存塊引用計(jì)數(shù)也相應(yīng)減1. 所以執(zhí)行代碼后str與test是指向了兩塊不同的地方,所以相安無(wú)事。

            (4) 不過(guò)這里還有一點(diǎn)注意事項(xiàng):就是str.GetBuffer(20)后,str的分配長(zhǎng)度為20,即指針p它所指向的buffer只有20字節(jié)長(zhǎng),給它賦值時(shí),切不可超過(guò),否則災(zāi)難離你不遠(yuǎn)了;如果指定長(zhǎng)度小于原來(lái)串長(zhǎng)度,如GetBuffer(1),實(shí)際上它會(huì)分配4個(gè)字節(jié)長(zhǎng)度(即原來(lái)串長(zhǎng)度);另外,當(dāng)調(diào)用GetBuffer(...)后并改變其內(nèi)容,一定要記得調(diào)用ReleaseBuffer(),這個(gè)函數(shù)會(huì)根據(jù)串內(nèi)容來(lái)更新引用內(nèi)存塊的頭部信息。

            (5) 最后還有一注意事項(xiàng),看下述代碼:

            char* p = NULL;

            const char* q = NULL;

            {

            CString str = "abcd";

            q = (LPCTSTR)str;

            p = str.GetBuffer(20);

            AfxMessageBox(q);// 合法的

            strcpy(p, "this is test");//合法的,

            }

            AfxMessageBox(q);// 非法的,可能完蛋

            strcpy(p, "this is test");//非法的,可能完蛋

            這里要說(shuō)的就是,當(dāng)返回這些指針后, 如果CString對(duì)象生命結(jié)束,這些指針也相應(yīng)無(wú)效。

            3.拷貝 & 賦值 & "引用內(nèi)存塊" 什么時(shí)候釋放?

            下面演示一段代碼執(zhí)行過(guò)程

            void Test()

            {

            CString str("abcd");

            //str指向一引用內(nèi)存塊(引用內(nèi)存塊的引用計(jì)數(shù)為1,長(zhǎng)度為4,分配長(zhǎng)度為4)

            CString a;

            //a指向一初始數(shù)據(jù)狀態(tài),

            a = str;

            //a與str指向同一引用內(nèi)存塊(引用內(nèi)存塊的引用計(jì)數(shù)為2,長(zhǎng)度為4,分配長(zhǎng)度為4)

            CString b(a);

            //a、b與str指向同一引用內(nèi)存塊(引用內(nèi)存塊的引用計(jì)數(shù)為3,長(zhǎng)度為4,分配長(zhǎng)度為4)

            {

            LPCTSTR temp = (LPCTSTR)a;

            //temp指向引用內(nèi)存塊的串首地址。(引用內(nèi)存塊的引用計(jì)數(shù)為3,長(zhǎng)度為4,分配長(zhǎng)度為4)

            CString d = a;

            //a、b、d與str指向同一引用內(nèi)存塊(引用內(nèi)存塊的引用計(jì)數(shù)為4, 長(zhǎng)度為4,分配長(zhǎng)度為4)

            b = "testa";

            //這條語(yǔ)句實(shí)際是調(diào)用CString::operator=(CString&)函數(shù)。 b指向一新分配的引用內(nèi)存塊。(新分配的引用內(nèi)存塊的 引用計(jì)數(shù)為1, 長(zhǎng)度為5, 分配長(zhǎng)度為5)

            //同時(shí)原引用內(nèi)存塊引用計(jì)數(shù)減1. a、d與str仍指向原 引用內(nèi)存塊(引用內(nèi)存塊的引用計(jì)數(shù)為3,長(zhǎng)度為4,分配長(zhǎng)度為4)

            }

            //由于d生命結(jié)束,調(diào)用析構(gòu)函數(shù),導(dǎo)至引用計(jì)數(shù)減1(引用內(nèi)存塊的引用計(jì)數(shù)為2,長(zhǎng)度為4,分配長(zhǎng)度為4)

            LPTSTR temp = a.GetBuffer(10);

            //此語(yǔ)句也會(huì)導(dǎo)致重新分配新內(nèi)存塊。temp指向新分配引用內(nèi)存塊的串首地址(新 分配的引用內(nèi)存塊的引用計(jì)數(shù)為1,長(zhǎng)度為0,分配長(zhǎng)度為10)

            //同時(shí)原引用內(nèi)存塊引用計(jì)數(shù)減1. 只有str仍 指向原引用內(nèi)存塊 (引用內(nèi)存塊的引用計(jì)數(shù)為1, 長(zhǎng)度為4, 分配長(zhǎng)度為4)

            strcpy(temp, "temp");

            //a指向的引用內(nèi)存塊的引用計(jì)數(shù)為1,長(zhǎng)度為0,分配長(zhǎng)度為10 a.ReleaseBuffer();//注意:a指向的引用內(nèi)存塊的引用計(jì)數(shù)為1,長(zhǎng)度為4,分配長(zhǎng)度為10

            }

            //執(zhí)行到此,所有的局部變量生命周期都已結(jié)束。對(duì)象str a b 各自調(diào)用自己的析構(gòu)構(gòu)

            //函數(shù),所指向的引用內(nèi)存塊也相應(yīng)減1

            //注意,str a b 所分別指向的引用內(nèi)存塊的計(jì)數(shù)均為0,這導(dǎo)致所分配的內(nèi)存塊釋放

            通過(guò)觀察上面執(zhí)行過(guò)程,我們會(huì)發(fā)現(xiàn)CString雖然可以多個(gè)對(duì)象指向同一引用內(nèi)塊存,但是它們?cè)谶M(jìn)行各種拷貝、賦值及改變串內(nèi)容時(shí),它的處理是很智能并且非常安全的,完全做到了互不干涉、互不影響。當(dāng)然必須要求你的代碼使用正確恰當(dāng),特別是實(shí)際使用中會(huì)有更復(fù)雜的情況,如做函數(shù)參數(shù)、引用、及有時(shí)需保存到CStringList當(dāng)中,如果哪怕有一小塊地方使用不當(dāng),其結(jié)果也會(huì)導(dǎo)致發(fā)生不可預(yù)知的錯(cuò)誤

            5 FreeExtra()的作用

            看這段代碼

            (1) CString str("test");

            (2) LPTSTR temp = str.GetBuffer(50);

            (3) strcpy(temp, "there are 22 character");

            (4) str.ReleaseBuffer();

            (5) str.FreeExtra();

            上面代碼執(zhí)行到第(4)行時(shí),大家都知道str指向的引用內(nèi)存塊計(jì)數(shù)為1,長(zhǎng)度為22,分配長(zhǎng)度為50. 那么執(zhí)行str.FreeExtra()時(shí),它會(huì)釋放所分配的多余的內(nèi)存。(引用內(nèi)存塊計(jì)數(shù)為1,長(zhǎng)度為22,分配長(zhǎng)度為22)

            6 Format(...) 與 FormatV(...)

            這條語(yǔ)句在使用中是最容易出錯(cuò)的。因?yàn)樗罡挥屑记尚?,也相?dāng)靈活。在這里,我沒(méi)打算對(duì)它細(xì)細(xì)分析,實(shí)際上sprintf(...)怎么用,它就怎么用。我只提醒使用時(shí)需注意一點(diǎn):就是它的參數(shù)的特殊性,由于編譯器在編譯時(shí)并不能去校驗(yàn)格式串參數(shù)與對(duì)應(yīng)的變?cè)念?lèi)型及長(zhǎng)度。所以你必須要注意,兩者一定要對(duì)應(yīng)上,

            否則就會(huì)出錯(cuò)。如:

            CString str;

            int a = 12;

            str.Format("first:%l, second: %s", a, "error");//result?試試

            7 LockBuffer() 與 UnlockBuffer()

            顧名思議,這兩個(gè)函數(shù)的作用就是對(duì)引用內(nèi)存塊進(jìn)行加鎖及解鎖。但使用它有什么作用及執(zhí)行過(guò)它后對(duì)CString串有什么實(shí)質(zhì)上的影響。其實(shí)挺簡(jiǎn)單,看下面代碼:

            (1) CString str("test");

            (2) str.LockBuffer();

            (3) CString temp = str;

            (4) str.UnlockBuffer();

            (5) str.LockBuffer();

            (6) str = "error";

            (7) str.ReleaseBuffer();

            執(zhí)行完(3)后,與通常情況下不同,temp與str并不指向同一引用內(nèi)存塊。你可以在watch窗口用這個(gè)表達(dá)式(CStringData*)((CStringData*)(str.m_pchData)-1)看看。

            其實(shí)在msdn中有說(shuō)明:

            While in a locked state, the string is protected in two ways:

            No other string can get a reference to the data in the locked string, even if that string is assigned to the locked string.

            The locked string will never reference another string, even if that other string is copied to the locked string.

            8 CString 只是處理串嗎?

            不對(duì),CString不只是能操作串,而且還能處理內(nèi)存塊數(shù)據(jù)。功能完善吧!看這段代碼

            char p[20];

            for(int loop=0; loop<sizeof(p); loop++)

            {

            p[loop] = 10-loop;

            }

            CString str((LPCTSTR)p, 20);

            char temp[20];

            memcpy(temp, str, str.GetLength());

            str完全能夠轉(zhuǎn)載內(nèi)存塊p到內(nèi)存塊temp中。所以能用CString來(lái)處理二進(jìn)制數(shù)據(jù)

            8 AllocSysString()與SetSysString(BSTR*)

            這兩個(gè)函數(shù)提供了串與BSTR的轉(zhuǎn)換。使用時(shí)須注意一點(diǎn):當(dāng)調(diào)用AllocSysString()后,須調(diào)用它SysFreeString(...)

            9 參數(shù)的安全檢驗(yàn)

            在MFC中提供了多個(gè)宏來(lái)進(jìn)行參數(shù)的安全檢查,如:ASSERT. 其中在CString中也不例外,有許多這樣的參數(shù)檢驗(yàn),其實(shí)這也說(shuō)明了代碼的安全性高,可有時(shí)我們會(huì)發(fā)現(xiàn)這很煩,也導(dǎo)致Debug與Release版本不一樣,如有時(shí)程序Debug通正常,而Release則程序崩潰;而有時(shí)恰相反,Debug不行,Release行。其實(shí)我個(gè)人認(rèn)為,我們對(duì)CString的使用過(guò)程中,應(yīng)力求代碼質(zhì)量高,不能在Debug版本中出現(xiàn)任何斷言框,哪怕release運(yùn)行似乎看起來(lái)一切正常。但很不安全。如下代碼:

            (1) CString str("test");

            (2) str.LockBuffer();

            (3) LPTSTR temp = str.GetBuffer(10);

            (4) strcpy(temp, "error");

            (5) str.ReleaseBuffer();

            (6) str.ReleaseBuffer();//執(zhí)行到此時(shí),Debug版本會(huì)彈出錯(cuò)框

            10 CString的異常處理

            我只想強(qiáng)調(diào)一點(diǎn):只有分配內(nèi)存時(shí),才有可能導(dǎo)致拋出CMemoryException.

            同樣,在msdn中的函數(shù)聲明中,注有throw( CMemoryException)的函數(shù)都有重新分配或調(diào)整內(nèi)存的可能。

            11 跨模塊時(shí)的Cstring。即一個(gè)DLL的接口函數(shù)中的參數(shù)為CString&時(shí),它會(huì)發(fā)生怎樣的現(xiàn)象。解答我遇到的問(wèn)題。我的問(wèn)題原來(lái)已經(jīng)發(fā)貼,地址為:

            http://www.csdn.net/expert/topic/741/741921.xml?temp=.2283136

            構(gòu)造一個(gè)這樣CString對(duì)象時(shí),如CString str,你可知道此時(shí)的str所指向的引用內(nèi)存塊嗎?也許你會(huì)認(rèn)為它指向NULL。其實(shí)不對(duì),如果這樣的話,CString所采用的引用機(jī)制管理內(nèi)存塊就會(huì)有麻煩了,所以CString在構(gòu)造一個(gè)空串的對(duì)象時(shí),它會(huì)指向一個(gè)固定的初始化地址,這塊數(shù)據(jù)的聲明如下:

            AFX_STATIC_DATA int _afxInitData[] = {-1,0,0,0};

            簡(jiǎn)要描述概括一下:當(dāng)某個(gè)CString對(duì)象串置空的話,如Empty(),CString a等,它的成員變量m_pchData就會(huì)指向_afxInitData這個(gè)變量的地址。當(dāng)這個(gè)CString對(duì)象生命周期結(jié)束時(shí),正常情況下它會(huì)去對(duì)所指向的引用內(nèi)存塊計(jì)數(shù)減1,如果引用計(jì)數(shù)為0(即沒(méi)有任何CString引用它時(shí)),則釋放這塊引用內(nèi)存。而現(xiàn)在的情況是如果CString所指向的引用內(nèi)存塊是初始化內(nèi)存塊時(shí),則不會(huì)釋放任何內(nèi)存。

            說(shuō)了這么多,這與我遇到的問(wèn)題有什么關(guān)系呢?其實(shí)關(guān)系大著呢?其真正原因就是如果exe模塊與dll模塊有一個(gè)是static編譯連接的話。那么這個(gè)CString初始化數(shù)據(jù)在exe模塊與dll模塊中有不同的地址,因?yàn)閟tatic連接則會(huì)在本模塊中有一份源代碼的拷貝。另外一種情況,如果兩個(gè)模塊都是share連接的,CString的實(shí)現(xiàn)代碼則在另一個(gè)單獨(dú)的dll中實(shí)現(xiàn),而AFX_STATIC_DATA指定變量只裝一次,所以?xún)蓚€(gè)模塊中_afxInitData有相同的地址。

            現(xiàn)在問(wèn)題完全明白了吧!你可以自己去演示一下。

            __declspec (dllexport) void test(CString& str)

            {

            str = "abdefakdfj";//如果是static連接,并且傳入的str為空串的話,這里出錯(cuò)。

            }

            posted @ 2007-07-24 17:11 深邃者 閱讀(582) | 評(píng)論 (0)編輯 收藏

            指針

            指針高級(jí)--<高質(zhì)量編程>

            //  execise2.cpp : Defines the entry point for the console application.
            //

            #include 
            " stdafx.h "
            #include 
            < iostream >
            using   namespace  std;
            // -----------------------------------------------
            void  GetMemory1( char   * p)
            {
                p 
            =  ( char   * )malloc( 100 );
            }

            void  Test1( void )
            {
                
            char   * str  =  NULL;
                GetMemory1( str );
                strcpy(str, 
            " hello world " );
                printf(str);
            }

            // -----------------------------------------------
            char   * GetMemory2( void )
            {   
                
            char  p[]  =   " hello world " ;
                
            return  p;
            }

            void  Test2( void )
            {
                
            char   * str  =  NULL;
                str 
            =  GetMemory2();  
                printf(str);
            }

            // -----------------------------------------------
            void  GetMemory3( char   ** p,  int  num)
            {
                
            * =  ( char   * )malloc(num);
            }

            void  Test3( void )
            {
                
            char   * str  =  NULL;
                GetMemory3(
            & str,  100 );
                strcpy(str, 
            " hello " );  
                printf(str);    
            }

            // -----------------------------------------------
            void  Test4( void )
            {
                
            char   * str  =  ( char   * ) malloc( 100 );
                strcpy(str, 
            " hello " );
                free(str);      
                
            if (str  !=  NULL)
                
            {
                    strcpy(str, 
            " world " ); 
                    printf(str);
                }

            }

            // -----------------------------------------------
            main()
            {
                
            // -----------------------------------------------
                
            // 請(qǐng)問(wèn)運(yùn)行Test1函數(shù)會(huì)有什么樣的結(jié)果?
                
            //
                
            // 答:程序崩潰。
                
            //
                
            // 因?yàn)镚etMemory并不能傳遞動(dòng)態(tài)內(nèi)存,
                
            //
                
            // Test函數(shù)中的 str一直都是 NULL。
                
            //
                
            // strcpy(str, "hello world");將使程序崩潰。
                Test1();
                
            // -----------------------------------------------
                
            //     請(qǐng)問(wèn)運(yùn)行Test2函數(shù)會(huì)有什么樣的結(jié)果?
                
            //
                
            // 答:可能是亂碼。
                
            //
                
            // 因?yàn)镚etMemory返回的是指向“棧內(nèi)存”的指針,
                
            // 該指針的地址不是 NULL,但其原現(xiàn)的內(nèi)容已經(jīng)被清除,新內(nèi)容不可知。
                Test2();
                
            // -----------------------------------------------
                
            //     請(qǐng)問(wèn)運(yùn)行Test3函數(shù)會(huì)有什么樣的結(jié)果?
                
            //
                
            // 答:
                
            //
                
            // (1)能夠輸出hello
                
            //
                
            // (2)內(nèi)存泄漏
                Test3();
                
            // -----------------------------------------------
                
            //     請(qǐng)問(wèn)運(yùn)行Test函數(shù)會(huì)有什么樣的結(jié)果?
                
            //
                
            // 答:篡改動(dòng)態(tài)內(nèi)存區(qū)的內(nèi)容,后果難以預(yù)料,非常危險(xiǎn)。
                
            //
                
            // 因?yàn)閒ree(str);之后,str成為野指針,
                
            //
                
            // if(str != NULL)語(yǔ)句不起作用。
                Test4();
                
            // -----------------------------------------------
            }

            posted @ 2007-07-24 16:48 深邃者 閱讀(125) | 評(píng)論 (0)編輯 收藏

            調(diào)試Release版本

            很多時(shí)候程序的 Debug 版本運(yùn)行沒(méi)有任何問(wèn)題,但是一旦發(fā)布 Release 版本后,運(yùn)行就出錯(cuò),著實(shí)讓人郁悶。大家知道,VC++ 中 Release 版本是對(duì)無(wú)法對(duì)源代碼進(jìn)行調(diào)試的。一般的做法是在懷疑有錯(cuò)誤的代碼前后插入MessageBox 函數(shù),在函數(shù)中顯示可能導(dǎo)致錯(cuò)誤的變量的值?;蛘卟迦雽?xiě)文件的語(yǔ)句,輸出可能導(dǎo)致錯(cuò)誤的變量的值到一個(gè)記錄文件。其實(shí),除了上面講的這個(gè)辦法之外,還有其它的途徑來(lái)調(diào)試 Release 版本的。下面就結(jié)合自己的經(jīng)驗(yàn)和網(wǎng)上查找的一些資料給出調(diào)試 Release 版本的兩個(gè)方法:

            方法一、利用 *.PDB 符號(hào)文件調(diào)試 Release 版本
            在 VCKBASE 的在線雜志中有一篇參考文章:符號(hào)文件——Windows 應(yīng)用程序調(diào)試必備(http://www.vckbase.com/document/viewdoc/?id=1710),文章談到了如何產(chǎn)生 Release 版本二進(jìn)制文件對(duì)應(yīng)的 PDB 文件的問(wèn)題。有了 PDB 文件后,就可以調(diào)試 Release 了,方法是:
                1、在Project Settings里選Settings For為All Configurations。
                2、在C/C++標(biāo)簽中,Debug info 選 Program Database。
                3、在Link 標(biāo)簽中,Category選 Debug,選中Debug info 復(fù)選框和Microsoft format。
            進(jìn)行了上述設(shè)置后,我們就可以像在調(diào)試版本中那樣設(shè)置斷點(diǎn)進(jìn)行測(cè)試了,由于代碼優(yōu)化,有些變量觀察不到,行的運(yùn)行順序可能也會(huì)不同。
            有一點(diǎn)需要注意:ASSERT宏在 Release 版本中不起作用,在 Release 版本中應(yīng)該使用 VERIFY 來(lái)代替 ASSERT 進(jìn)行調(diào)試。如果發(fā)行版本運(yùn)行有問(wèn)題,可以先禁止所有代碼優(yōu)化再進(jìn)行調(diào)試。

            方法二、在需要加斷點(diǎn)的地方添加如下匯編語(yǔ)句:
                __asm int 3

            不過(guò)調(diào)試的時(shí)候無(wú)法顯示C程序,只有asm代碼。
                
            此處 int 3 是專(zhuān)門(mén)用來(lái)設(shè)置斷點(diǎn)的,是 CPU 定義的,Windows 和 DOS 下的大多數(shù)調(diào)試器都采用這種方法。

            posted @ 2007-07-24 16:31 深邃者 閱讀(236) | 評(píng)論 (0)編輯 收藏

            LINK錯(cuò)誤

            vc編譯鏈接錯(cuò)誤--LNK2001,LNK2019,

            --  LINK2001
            學(xué)習(xí)VC++時(shí)經(jīng)常會(huì)遇到鏈接錯(cuò)誤LNK2001,該錯(cuò)誤非常討厭,因?yàn)閷?duì)于
            編程者來(lái)說(shuō),最好改的錯(cuò)誤莫過(guò)于編譯錯(cuò)誤,而一般說(shuō)來(lái)發(fā)生連接錯(cuò)誤時(shí),
            編譯都已通過(guò)。產(chǎn)生連接錯(cuò)誤的原因非常多,尤其LNK2001錯(cuò)誤,常常使人不
            明其所以然。如果不深入地學(xué)習(xí)和理解VC++,要想改正連接錯(cuò)誤LNK2001非
            常困難。
              初學(xué)者在學(xué)習(xí)VC++的過(guò)程中,遇到的LNK2001錯(cuò)誤的錯(cuò)誤消息主要為:
              unresolved external symbol “symbol”(不確定的外部“符號(hào)”)。
              如果連接程序不能在所有的庫(kù)和目標(biāo)文件內(nèi)找到所引用的函數(shù)、變量或
            標(biāo)簽,將產(chǎn)生此錯(cuò)誤消息。一般來(lái)說(shuō),發(fā)生錯(cuò)誤的原因有兩個(gè):一是所引用
            的函數(shù)、變量不存在、拼寫(xiě)不正確或者使用錯(cuò)誤;其次可能使用了不同版本
            的連接庫(kù)。
              以下是可能產(chǎn)生LNK2001錯(cuò)誤的原因:
              一.由于編碼錯(cuò)誤導(dǎo)致的LNK2001。
              1.不相匹配的程序代碼或模塊定義(.DEF)文件能導(dǎo)致LNK2001。例如,
            如果在C++ 源文件內(nèi)聲明了一變量“var1”,卻試圖在另一文件內(nèi)以變量
            “VAR1”訪問(wèn)該變量,將發(fā)生該錯(cuò)誤。
              2.如果使用的內(nèi)聯(lián)函數(shù)是在.CPP文件內(nèi)定義的,而不是在頭文件內(nèi)定
            義將導(dǎo)致LNK2001錯(cuò)誤。
              3.調(diào)用函數(shù)時(shí)如果所用的參數(shù)類(lèi)型同函數(shù)聲明時(shí)的類(lèi)型不符將會(huì)產(chǎn)生
            LNK2001。
              4.試圖從基類(lèi)的構(gòu)造函數(shù)或析構(gòu)函數(shù)中調(diào)用虛擬函數(shù)時(shí)將會(huì)導(dǎo)致LNK2001。
              5.要注意函數(shù)和變量的可公用性,只有全局變量、函數(shù)是可公用的。
              靜態(tài)函數(shù)和靜態(tài)變量具有相同的使用范圍限制。當(dāng)試圖從文件外部訪問(wèn)
            任何沒(méi)有在該文件內(nèi)聲明的靜態(tài)變量時(shí)將導(dǎo)致編譯錯(cuò)誤或LNK2001。
              函數(shù)內(nèi)聲明的變量(局部變量) 只能在該函數(shù)的范圍內(nèi)使用。
              C++ 的全局常量只有靜態(tài)連接性能。這不同于C,如果試圖在C++的
            多個(gè)文件內(nèi)使用全局變量也會(huì)產(chǎn)生LNK2001錯(cuò)誤。一種解決的方法是需要時(shí)在
            頭文件中加入該常量的初始化代碼,并在.CPP文件中包含該頭文件;另一種
            方法是使用時(shí)給該變量賦以常數(shù)。
              二.由于編譯和鏈接的設(shè)置而造成的LNK2001
              1.如果編譯時(shí)使用的是/NOD(/NODEFAULTLIB)選項(xiàng),程序所需要的運(yùn)行
            庫(kù)和MFC庫(kù)在連接時(shí)由編譯器寫(xiě)入目標(biāo)文件模塊, 但除非在文件中明確包含
            這些庫(kù)名,否則這些庫(kù)不會(huì)被鏈接進(jìn)工程文件。在這種情況下使用/NOD將導(dǎo)
            致錯(cuò)誤LNK2001。
              2.如果沒(méi)有為wWinMainCRTStartup設(shè)定程序入口,在使用Unicode和MFC
            時(shí)將得到“unresolved external on _WinMain@16”的LNK2001錯(cuò)誤信息。
              3.使用/MD選項(xiàng)編譯時(shí),既然所有的運(yùn)行庫(kù)都被保留在動(dòng)態(tài)鏈接庫(kù)之內(nèi),
            源文件中對(duì)“func”的引用,在目標(biāo)文件里即對(duì)“__imp__func” 的引用。
            如果試圖使用靜態(tài)庫(kù)LIBC.LIB或LIBCMT.LIB進(jìn)行連接,將在__imp__func上發(fā)
            生LNK2001;如果不使用/MD選項(xiàng)編譯,在使用MSVCxx.LIB連接時(shí)也會(huì)發(fā)生LNK2001。
              4.使用/ML選項(xiàng)編譯時(shí),如用LIBCMT.LIB鏈接會(huì)在_errno上發(fā)生LNK2001。
              5.當(dāng)編譯調(diào)試版的應(yīng)用程序時(shí),如果采用發(fā)行版模態(tài)庫(kù)進(jìn)行連接也會(huì)產(chǎn)
            生LNK2001;同樣,使用調(diào)試版模態(tài)庫(kù)連接發(fā)行版應(yīng)用程序時(shí)也會(huì)產(chǎn)生相同的
            問(wèn)題。
              6.不同版本的庫(kù)和編譯器的混合使用也能產(chǎn)生問(wèn)題,因?yàn)樾掳娴膸?kù)里可
            能包含早先的版本沒(méi)有的符號(hào)和說(shuō)明。
              7.在不同的模塊使用內(nèi)聯(lián)和非內(nèi)聯(lián)的編譯選項(xiàng)能夠?qū)е翷NK2001。如果
            創(chuàng)建C++庫(kù)時(shí)打開(kāi)了函數(shù)內(nèi)聯(lián)(/Ob1或/Ob2),但是在描述該函數(shù)的相應(yīng)頭
            文件里卻關(guān)閉了函數(shù)內(nèi)聯(lián)(沒(méi)有inline關(guān)鍵字),這時(shí)將得到該錯(cuò)誤信息。
            為避免該問(wèn)題的發(fā)生,應(yīng)該在相應(yīng)的頭文件中用inline關(guān)鍵字標(biāo)志內(nèi)聯(lián)函數(shù)。
              8.不正確的/SUBSYSTEM或/ENTRY設(shè)置也能導(dǎo)致LNK2001。
              其實(shí),產(chǎn)生LNK2001的原因還有很多,以上的原因只是一部分而已,對(duì)初
            學(xué)者來(lái)說(shuō)這些就夠理解一陣子了。但是,分析錯(cuò)誤原因的目的是為了避免錯(cuò)
            誤的發(fā)生。LNK2001錯(cuò)誤雖然比較困難,但是只要注意到了上述問(wèn)題,還是能
            夠避免和予以解決的。

            LNK2019
            函數(shù)只有申明,沒(méi)有實(shí)現(xiàn)時(shí),或是DLL中的東東沒(méi)有export啊

            posted @ 2007-07-24 16:29 深邃者 閱讀(204) | 評(píng)論 (0)編輯 收藏

            內(nèi)存檢測(cè)

            1)工程是MFC工程,或是工程的設(shè)置中有Use MFC in a Shared DLL,
            2)很多地方說(shuō)是要定義以下宏
            #ifdef _DEBUG
            #define new DEBUG_NEW
            #endif
            但是我發(fā)現(xiàn)只要include <afx.h> 即可。(大家可以檢測(cè)一下)
            3)可以在F5運(yùn)行程序后,在output窗口中看到如下的內(nèi)存泄露的顯示。(只在debug下有用哦)
            4)如果有泄露,則顯示如下:
            Detected memory leaks!
            Dumping objects ->
            {214} normal block at 0x00D91618, 4 bytes long.
             Data: <    > 00 00 00 00
            {208} normal block at 0x00D914D0, 4 bytes long.
             Data: <    > 00 00 00 00
            {207} normal block at 0x00D91490, 4 bytes long.
             Data: <    > D0 14 D9 00
            {205} normal block at 0x00D91410, 4 bytes long.
             Data: <    > 00 00 00 00
            {204} normal block at 0x003AFFD8, 4 bytes long.
             Data: <    > 10 14 D9 00
            {203} normal block at 0x003AFF98, 4 bytes long.
             Data: <    > 00 00 00 00
            {202} normal block at 0x003AFF58, 4 bytes long.
             Data: <  : > 98 FF 3A 00
            {200} normal block at 0x003AFF18, 4 bytes long.
             Data: <    > 00 00 00 00
            Object dump complete.

            posted @ 2007-07-24 15:08 深邃者 閱讀(175) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共5頁(yè): 1 2 3 4 5 
            久久精品国产第一区二区三区| 久久人人爽人人爽人人片av麻烦| 中文字幕无码精品亚洲资源网久久 | 91亚洲国产成人久久精品网址| 久久91精品国产91久久麻豆| 国产精品无码久久四虎| 久久AⅤ人妻少妇嫩草影院| 一级A毛片免费观看久久精品| 色欲久久久天天天综合网| 97精品伊人久久大香线蕉app| 成人国内精品久久久久影院VR| 久久亚洲AV无码精品色午夜| 久久久噜噜噜www成人网| 欧美久久久久久午夜精品| 99久久99久久久精品齐齐| 久久亚洲AV永久无码精品| 99久久99久久精品免费看蜜桃 | 久久精品九九亚洲精品| 国产99久久九九精品无码| 伊人 久久 精品| 国产福利电影一区二区三区久久久久成人精品综合 | 久久夜色精品国产亚洲| 一级a性色生活片久久无少妇一级婬片免费放 | 亚洲欧美一区二区三区久久| 一本一本久久aa综合精品| 欧美久久一级内射wwwwww.| 久久久久久久综合日本亚洲 | 热久久国产精品| 国产精品免费福利久久| 亚洲AV无一区二区三区久久| 国产69精品久久久久APP下载| 久久精品国产亚洲av瑜伽| 免费观看久久精彩视频| 久久66热人妻偷产精品9| 性做久久久久久久| 国产成人精品综合久久久| 久久婷婷五月综合色奶水99啪| 亚洲AV伊人久久青青草原| 免费一级欧美大片久久网| 久久福利资源国产精品999| 久久无码AV中文出轨人妻|