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

            #include<iconv.h> 
            #include <stdio.h>

            #include<iconv.h>
            using namespace std;
            int utf8togb2312(const char *sourcebuf,size_t sourcelen,char *destbuf,size_t destlen) {   

            iconv_t cd;   

            if( (cd = iconv_open("gb2312","utf-8")) ==0 )     

                  return -1;  

            memset(destbuf,0,destlen);   

            const char **source = &sourcebuf;   

            char **dest = &destbuf;
            if(-1 == iconv(cd,source,&sourcelen,dest,&destlen))     

                 return -1;   

            iconv_close(cd);   

            return 0;   

            }
            int gb2312toutf8(const char *sourcebuf,size_t sourcelen,char *destbuf,size_t destlen) {   

            iconv_t cd;   if( (cd = iconv_open("utf-8","gb2312")) ==0 )     

            return -1;   memset(destbuf,0,destlen);   

            const char **source = &sourcebuf;   

            char **dest = &destbuf;
              if(-1 == iconv(cd,source,&sourcelen,dest,&destlen))     

            return -1;   
            posted @ 2018-11-06 16:59 sheng 閱讀(338) | 評論 (0)編輯 收藏
                 摘要: 轉(zhuǎn)載自http://blog.csdn.net/u010984552/article/details/51887108為什么需要線程池目前的大多數(shù)網(wǎng)絡(luò)服務(wù)器,包括Web服務(wù)器、Email服務(wù)器以及數(shù)據(jù)庫服務(wù)器等都具有一個共同點,就是單位時間內(nèi)必須處理數(shù)目巨大的連接請求,但處理時間卻相對較短。 傳 統(tǒng)多線程方案中我們采用的服務(wù)器模型則是一旦接受到請求之后,即創(chuàng)建一個新的線程,由該線程執(zhí)行任...  閱讀全文
            posted @ 2016-08-26 16:46 sheng 閱讀(399) | 評論 (0)編輯 收藏

            摘自
            http://blog.csdn.net/hackbuteer1/article/details/7722667

            在 各種計算機(jī)體系結(jié)構(gòu)中,對于字節(jié)、字等的存儲機(jī)制有所不同,因而引發(fā)了計算機(jī) 通信領(lǐng) 域中一個很重要的問題,即通信雙方交流的信息單元(比特、字節(jié)、字、雙字等等)應(yīng)該以什么樣的順序進(jìn)行傳送。如果不達(dá)成一致的規(guī)則,通信雙方將無法進(jìn)行正 確的編/譯碼從而導(dǎo)致通信失敗。目前在各種體系的計算機(jī)中通常采用的字節(jié)存儲機(jī)制主要有兩種:Big-Endian和Little-Endian,下面先從字節(jié)序說起。
            一、什么是字節(jié)序
            字節(jié)序,顧名思義字節(jié)的順序,再多說兩句就是大于一個字節(jié)類型的數(shù)據(jù)在內(nèi)存中的存放順序(一個字節(jié)的數(shù)據(jù)當(dāng)然就無需談順序的問題了)。其實大部分人在實際的開 發(fā)中都很少會直接和字節(jié)序打交道。唯有在跨平臺以及網(wǎng)絡(luò)程序中字節(jié)序才是一個應(yīng)該被考慮的問題。

            在所有的介紹字節(jié)序的文章中都會提到字 節(jié)序分為兩類:Big-Endian和Little-Endian,引用標(biāo)準(zhǔn)的Big-Endian和Little-Endian的定義如下:
            a) Little-Endian就是低位字節(jié)排放在內(nèi)存的低地址端,高位字節(jié)排放在內(nèi)存的高地址端。
            b) Big-Endian就是高位字節(jié)排放在內(nèi)存的低地址端,低位字節(jié)排放在內(nèi)存的高地址端。
            c) 網(wǎng)絡(luò)字節(jié)序:TCP/IP各層協(xié)議將字節(jié)序定義為Big-Endian,因此TCP/IP協(xié)議中使用的字節(jié)序通常稱之為網(wǎng)絡(luò)字節(jié)序。

            1.1 什么是高/低地址端
            首先我們要知道C程序映像中內(nèi)存的空間布局情況:在《C專 家編程》中或者《Unix環(huán)境高級編程》中有關(guān)于內(nèi)存空間布局情況的說明,大致如下圖:
            ----------------------- 最高內(nèi)存地址 0xffffffff
            棧底

            棧頂

            -----------------------

            NULL (空洞)
            -----------------------

            -----------------------
            未初始 化的數(shù)據(jù)
            ----------------------- 統(tǒng)稱數(shù)據(jù)段
            初始化的數(shù)據(jù)
            -----------------------
            正 文段(代碼段)
            ----------------------- 最低內(nèi)存地址 0x00000000
            由圖可以看出,再內(nèi)存分布中,棧是向下增長的,而堆是向上增長的。
            以上圖為例如果我們在棧 上分配一個unsigned char buf[4],那么這個數(shù)組變量在棧上是如何布局的呢?看下圖:
            棧底 (高地址)
            ----------
            buf[3]
            buf[2]
            buf[1]
            buf[0]

            ----------
            棧頂 (低地址)
            其實,我們可以自己在編譯器里面創(chuàng)建一個數(shù)組,然后分別輸出數(shù)組種每個元素的地址,來驗證一下。
            1.2 什么是高/低字節(jié)
            現(xiàn)在我們弄清了高/低地址,接著考慮高/低字節(jié)。有些文章中稱低位字節(jié)為最低有效位,高位字節(jié)為最高有效位。如果我們有一個32位無符號整型0x12345678,那么高位是什么,低位又是什么呢? 其實很簡單。在十進(jìn)制中我們都說靠左邊的是高位,靠右邊的是低位,在其他進(jìn)制也是如此。就拿 0x12345678來說,從高位到低位的字節(jié)依次是0x12、0x34、0x56和0x78。
            高/低地址端和高/低字節(jié)都弄清了。我們再來回顧 一下Big-Endian和Little-Endian的定義,并用圖示說明兩種字節(jié)序:
            以unsigned int value = 0x12345678為例,分別看看在兩種字節(jié)序下其存儲情況,我們可以用unsigned char buf[4]來表示value:
            Big-Endian: 低地址存放高位,如下圖:
            棧底 (高地址)
            ---------------
            buf[3] (0x78) -- 低位
            buf[2] (0x56)
            buf[1] (0x34)
            buf[0] (0x12) -- 高位
            ---------------
            棧頂 (低地址)

            Little-Endian: 低地址存放低位,如下圖:
            棧底 (高地址)
            ---------------
            buf[3] (0x12) -- 高位
            buf[2] (0x34)
            buf[1] (0x56)
            buf[0] (0x78) -- 低位
            --------------
            棧 頂 (低地址)

            二、各種Endian
            2.1 Big-Endian
            計算機(jī)體系結(jié)構(gòu)中一種描述多字節(jié)存儲順序的術(shù)語,在這種機(jī)制中最重要字節(jié)(MSB)存放在最低端的地址 上。采用這種機(jī)制的處理器有IBM3700系列、PDP-10、Mortolora微處理器系列和絕大多數(shù)的RISC處理器。
            +----------+
            | 0x34 |<-- 0x00000021
            +----------+
            | 0x12 |<-- 0x00000020
            +----------+
            圖 1:雙字節(jié)數(shù)0x1234以Big-Endian的方式存在起始地址0x00000020中

            在Big-Endian中,對于bit序列 中的序號編排方式如下(以雙字節(jié)數(shù)0x8B8A為例):
            bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
            +-----------------------------------------+
            val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
            +----------------------------------------+
            圖 2:Big-Endian的bit序列編碼方式
            2.2 Little-Endian
            計算機(jī)體系結(jié)構(gòu)中 一種描述多字節(jié)存儲順序的術(shù)語,在這種機(jī)制中最不重要字節(jié)(LSB)存放在最低端的地址上。采用這種機(jī)制的處理器有PDP-11、VAX、Intel系列微處理器和一些網(wǎng)絡(luò)通信設(shè)備。該術(shù)語除了描述多字節(jié)存儲順序外還常常用來描述一個字節(jié)中各個比特的排放次序。

            +----------+
            | 0x12 |<-- 0x00000021
            +----------+
            | 0x34 |<-- 0x00000020
            +----------+

            圖3:雙字節(jié)數(shù)0x1234以Little-Endian的方式存在起始地址0x00000020中
             在 Little-Endian中,對于bit序列中的序號編排和Big-Endian剛好相反,其方式如下(以雙字節(jié)數(shù)0x8B8A為例):
            bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
            +-----------------------------------------+
            val | 1 0 0 0 1 0 1 1 | 1 0 0 0 1 0 1 0 |
            +-----------------------------------------+
            圖 4:Little-Endian的bit序列編碼方式
            注意:通常我們說的主機(jī)序(Host Order)就是遵循Little-Endian規(guī)則。所以當(dāng)兩臺主機(jī)之間要通過TCP/IP協(xié)議進(jìn)行通信的時候就需要調(diào)用相應(yīng)的函數(shù)進(jìn)行主機(jī)序 (Little-Endian)和網(wǎng)絡(luò)序(Big-Endian)的轉(zhuǎn)換。
            采用 Little-endian模式的CPU對操作數(shù)的存放方式是從低字節(jié)到高字節(jié),而Big-endian模式對操作數(shù)的存放方式是從高字節(jié)到低字節(jié)。 32bit寬的數(shù)0x12345678在Little-endian模式CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)為:
                                                      內(nèi)存地址     0x4000     0x4001     0x4002     0x4003
                                                      存放內(nèi)容     0x78        0x56        0x34         0x12
            而在Big- endian模式CPU內(nèi)存中的存放方式則為:
                                                      內(nèi)存地址     0x4000     0x4001     0x4002     0x4003
                                                      存放內(nèi)容     0x12         0x34        0x56         0x78
            具體的區(qū)別如下:


            三、Big-Endian和Little-Endian優(yōu)缺點
            Big-Endian優(yōu)點:靠首先提取高位字節(jié),你總是可以由看看在偏移位置為0的字節(jié)來確定這個數(shù)字是 正數(shù)還是負(fù)數(shù)。你不必知道這個數(shù)值有多長,或者你也不必過一些字節(jié)來看這個數(shù)值是否含有符號位。這個數(shù)值是以它們被打印出來的順序存放的,所以從二進(jìn)制到十進(jìn)制的函數(shù)特別有效。因而,對于不同要求的機(jī)器,在設(shè)計存取方式時就會不同。

            Little-Endian優(yōu)點:提取一個,兩個,四個或者更長字節(jié)數(shù)據(jù)的匯編指令以與其他所有格式相同的方式進(jìn)行:首先在偏移地址為0的地方提取最低位的字節(jié),因為地址偏移和字節(jié)數(shù)是一對一的關(guān)系,多重精度的數(shù)學(xué)函數(shù)就相對地容易寫了。

            如果你增加數(shù)字的值,你可能在左邊增加數(shù)字(高位非指數(shù)函數(shù)需要更多的數(shù)字)。 因此, 經(jīng)常需要增加兩位數(shù)字并移動存儲器里所有Big-endian順序的數(shù)字,把所有數(shù)向右移,這會增加計算機(jī)的工作量。不過,使用Little- Endian的存儲器中不重要的字節(jié)可以存在它原來的位置,新的數(shù)可以存在它的右邊的高位地址里。這就意味著計算機(jī)中的某些計算可以變得更加簡單和快速。
            四、請寫一個C函數(shù),若處理器是Big_endian的,則返回0;若是Little_endian的,則返回1。
            1. int checkCPU(void)  
            2. {  
            3.     union  
            4.     {  
            5.         int a;  
            6.         char b;  
            7.     }c;  
            8.     c.a = 1;  
            9.     return (c.b == 1);  
            10. }  
            剖析:由于聯(lián)合體union的存放順序是所有成員都從低地址開始存放,利用該特性就可以輕松地獲得了CPU對內(nèi)存采用Little- endian還是Big-endian模式讀寫。
            說明:
            1  在c中,聯(lián)合體(共用體)的數(shù)據(jù)成員都是從低地址開始存放。
            2  若是小端模式,由低地址到高地址c.a存放為0x01 00 00 00,c.b被賦值為0x01;
              ————————————————————————————
               地址 0x00000000 0x00000001 0x00000002 0x00000003
               c.a  01         00         00         00
               c.b  01         00        
              ————————————————————————————  
            3  若是大端模式,由低地址到高地址c.a存放為0x00 00 00 01,c.b被賦值為0x0;
              ————————————————————————————
               地址 0x00000000 0x00000001 0x00000002 0x00000003
               c.a  00         00         00         01
               c.b  00         00                 
              ————————————————————————————  
            4  根據(jù)c.b的值的情況就可以判斷cpu的模式了。

            舉例,一個16進(jìn)制數(shù)是 0x11 22 33,其存放的位置是
            地址0x3000 中存放11
            地址0x3001 中存放22
            地址0x3002 中存放33
            連起來就寫成地址0x3000-0x3002中存放了數(shù)據(jù)0x112233
            而這種存放和表示方式,正好符合大端。

            另外一個比較好理解的寫法如下:
            1. bool checkCPU()     // 如果是大端模式,返回真  
            2. {  
            3.     short int test = 0x1234;  
            4.   
            5.     if( *((char *)&test) == 0x12)     // 低地址存放高字節(jié)數(shù)據(jù)  
            6.         return true;  
            7.     else  
            8.         return false;  
            9. }  
            10.   
            11. int main(void)  
            12. {  
            13.     if( !checkCPU())  
            14.         cout<<"Little endian"<<endl;  
            15.     else  
            16.         cout<<"Big endian"<<endl;  
            17.   
            18.     return 0;  
            19. }  
            或者下面兩種寫法也是可以的
            1. int main(void)  
            2. {  
            3.     short int a = 0x1234;  
            4.     char *p = (char *)&a;  
            5.   
            6.     if( *p == 0x34)  
            7.         cout<<"Little endian"<<endl;  
            8.     else  
            9.         cout<<"Big endian"<<endl;  
            10.   
            11.     return 0;  
            12. }  
            13.   
            14. int main(void)  
            15. {  
            16.     short int a = 0x1234;  
            17.     char x0 , x1;  
            18.   
            19.     x0 = ((char *)&a)[0];  
            20.     x1 = ((char *)&a)[1];  
            21.   
            22.     if( x0 == 0x34)  
            23.         cout<<"Little endian"<<endl;  
            24.     else  
            25.         cout<<"Big endian"<<endl;  
            26.   
            27.     return 0;  
            28. }  
            posted @ 2016-08-17 14:21 sheng 閱讀(190) | 評論 (0)編輯 收藏
                 摘要: C++ 虛函數(shù)表解析 陳皓http://blog.csdn.net/haoel  前言 C++中的虛函數(shù)的作用主要是實現(xiàn)了多態(tài)的機(jī)制。關(guān)于多態(tài),簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調(diào)用實際子類的成員函數(shù)。這種技術(shù)可以讓父類的指針有“多種形態(tài)”,這是一種泛型技術(shù)。所謂泛型技術(shù),說白了就是試圖使用不變...  閱讀全文
            posted @ 2016-06-30 15:36 sheng 閱讀(204) | 評論 (0)編輯 收藏

            首先看看如下一個聲明:

             

            int* ( *( *fun )( int* ) )[10];

             

            這是一個會讓初學(xué)者感到頭暈?zāi)垦?、感到恐懼的函?shù)指針聲明。在熟練掌握C/C++的聲明語法之前,不學(xué)習(xí)一定的規(guī)則,想理解好這類復(fù)雜聲明是比較困難的。

             

            C/C++所有復(fù)雜的聲明結(jié)構(gòu),都是由各種聲明嵌套構(gòu)成的。如何解讀復(fù)雜指針聲明?右左法則是一個很著名、很有效的方法。不過,右左法則其實并不是C/C++標(biāo)準(zhǔn)里面的內(nèi)容,它是從C/C++標(biāo)準(zhǔn)的聲明規(guī)定中歸納出來的方法。C/C++標(biāo)準(zhǔn)的聲明規(guī)則,是用來解決如何創(chuàng)建聲明的,而右左法則是用來解決如何辯識一個聲明的,從嵌套的角度看,兩者可以說是一個相反的過程。右左法則的英文原文是這樣說的:

             

            The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

             

             

            這段英文的翻譯如下:

             

            右左法則:首先從最里面的圓括號看起,然后往右看,再往左看。每當(dāng)遇到圓括號時,就應(yīng)該掉轉(zhuǎn)閱讀方向。一旦解析完圓括號里面所有的東西,就跳出圓括號。重復(fù)這個過程直到整個聲明解析完畢。

             

                筆者要對這個法則進(jìn)行一個小小的修正,應(yīng)該是從未定義的標(biāo)識符開始閱讀,而不是從括號讀起,之所以是未定義的標(biāo)識符,是因為一個聲明里面可能有多個標(biāo)識符,但未定義的標(biāo)識符只會有一個。

             

                現(xiàn)在通過一些例子來討論右左法則的應(yīng)用,先從最簡單的開始,逐步加深:

             

            int (*func)(int *p);

             

            首先找到那個未定義的標(biāo)識符,就是func,它的外面有一對圓括號,而且左邊是一個*號,這說明func是一個指針,然后跳出這個圓括號,先看右邊,也是一個圓括號,這說明(*func)是一個函數(shù),而func是一個指向這類函數(shù)的指針,就是一個函數(shù)指針,這類函數(shù)具有int*類型的形參,返回值類型是int

             

            int (*func)(int *p, int (*f)(int*));

             

            func被一對括號包含,且左邊有一個*號,說明func是一個指針,跳出括號,右邊也有個括號,那么func是一個指向函數(shù)的指針,這類函數(shù)具有int *int (*)(int*)這樣的形參,返回值為int類型。再來看一看func的形參int (*f)(int*),類似前面的解釋,f也是一個函數(shù)指針,指向的函數(shù)具有int*類型的形參,返回值為int。

             

            int (*func[5])(int *p);

             

            func右邊是一個[]運算符,說明func是一個具有5個元素的數(shù)組,func的左邊有一個*,說明func的元素是指針,要注意這里的*不是修飾func的,而是修飾func[5]的,原因是[]運算符優(yōu)先級比*高,func先跟[]結(jié)合,因此*修飾的是func[5]。跳出這個括號,看右邊,也是一對圓括號,說明func數(shù)組的元素是函數(shù)類型的指針,它所指向的函數(shù)具有int*類型的形參,返回值類型為int。

             

             

            int (*(*func)[5])(int *p);

             

            func被一個圓括號包含,左邊又有一個*,那么func是一個指針,跳出括號,右邊是一個[]運算符號,說明func是一個指向數(shù)組的指針,現(xiàn)在往左看,左邊有一個*號,說明這個數(shù)組的元素是指針,再跳出括號,右邊又有一個括號,說明這個數(shù)組的元素是指向函數(shù)的指針??偨Y(jié)一下,就是:func是一個指向數(shù)組的指針,這個數(shù)組的元素是函數(shù)指針,這些指針指向具有int*形參,返回值為int類型的函數(shù)。

             

            int (*(*func)(int *p))[5];

             

            func是一個函數(shù)指針,這類函數(shù)具有int*類型的形參,返回值是指向數(shù)組的指針,所指向的數(shù)組的元素是具有5int元素的數(shù)組。

             

            要注意有些復(fù)雜指針聲明是非法的,例如:

             

            int func(void) [5];

             

            func是一個返回值為具有5int元素的數(shù)組的函數(shù)。但C語言的函數(shù)返回值不能為數(shù)組,這是因為如果允許函數(shù)返回值為數(shù)組,那么接收這個數(shù)組的內(nèi)容的東西,也必須是一個數(shù)組,但C/C++語言的數(shù)組名是一個不可修改的左值,它不能直接被另一個數(shù)組的內(nèi)容修改,因此函數(shù)返回值不能為數(shù)組。

             

            int func[5](void);

             

            func是一個具有5個元素的數(shù)組,這個數(shù)組的元素都是函數(shù)。這也是非法的,因為數(shù)組的元素必須是對象,但函數(shù)不是對象,不能作為數(shù)組的元素。

             

            實際編程當(dāng)中,需要聲明一個復(fù)雜指針時,如果把整個聲明寫成上面所示這些形式,將對可讀性帶來一定的損害,應(yīng)該用typedef來對聲明逐層分解,增強(qiáng)可讀性。

             

            typedef是一種聲明,但它聲明的不是變量,也沒有創(chuàng)建新類型,而是某種類型的別名。typedef有很大的用途,對一個復(fù)雜聲明進(jìn)行分解以增強(qiáng)可讀性是其作用之一。例如對于聲明:

             

            int (*(*func)(int *p))[5];

             

            可以這樣分解:

             

            typedef  int (*PARA)[5];

            typedef PARA (*func)(int *);

             

            這樣就容易看得多了。

             

            typedef的另一個作用,是作為基于對象編程的高層抽象手段。在ADT中,它可以用來在C/C++和現(xiàn)實世界的物件間建立關(guān)聯(lián),將這些物件抽象成C/C++的類型系統(tǒng)。在設(shè)計ADT的時候,我們常常聲明某個指針的別名,例如:

             

            typedef struct node * list;

             

            ADT的角度看,這個聲明是再自然不過的事情,可以用list來定義一個列表。但從C/C++語法的角度來看,它其實是不符合C/C++聲明語法的邏輯的,它暴力地將指針聲明符從指針聲明器中分離出來,這會造成一些異于人們閱讀習(xí)慣的現(xiàn)象,考慮下面代碼:

             

            const struct node *p1;

            typedef struct node *list;

            const list p2;

             

            p1類型是const struct node*,那么p2呢?如果你以為就是把list簡單“代入”p2,然后得出p2類型也是const struct node*的結(jié)果,就大錯特錯了。p2的類型其實是struct node * const p2,那個const限定的是p2,不是node。造成這一奇異現(xiàn)象的原因是指針聲明器被分割,標(biāo)準(zhǔn)中規(guī)定:

             

            6.7.5.1 Pointer declarators

             

            Semantics

             

             If in the declaration ‘‘T D1’, D1 has the form

             

            * type-qualifier-listopt D

             

            and the type specified for ident in the declaration ‘‘T D’’ is

             

            ‘‘derived-declarator-type-list T’’

             

            then the type specified for ident is

             

            ‘‘derived-declarator-type-list type-qualifier-list pointer to T’’

             

            For each type qualifier in the list, ident is a so-qualified pointer.

             

            指針的聲明器由指針聲明符*、可選的類型限定詞type-qualifier-listopt和標(biāo)識符D組成,這三者在邏輯上是一個整體,構(gòu)成一個完整的指針聲明器。這也是多個變量同列定義時指針聲明符必須緊跟標(biāo)識符的原因,例如:

             

            int *p, q, *k;

             

            pk都是指針,但q不是,這是因為*p*k是一個整體指針聲明器,以表示聲明的是一個指針。編譯器會把指針聲明符左邊的類型包括其限定詞作為指針指向的實體的類型,右邊的限定詞限定被聲明的標(biāo)識符。但現(xiàn)在typedef struct node *list硬生生把*從整個指針聲明器中分離出來,編譯器找不到*,會認(rèn)為const list p2中的const是限定p2的,正因如此,p2的類型是node * const而不是const node*。

             

            雖然typedef struct node* list不符合聲明語法的邏輯,但基于typedefADT中的重要作用以及信息隱藏的要求,我們應(yīng)該讓用戶這樣使用list A,而不是list *A,因此在ADT的設(shè)計中仍應(yīng)使用上述typedef語法,但需要注意其帶來的不利影響。

            posted @ 2016-04-14 13:49 sheng 閱讀(246) | 評論 (0)編輯 收藏
            C99中,結(jié)構(gòu)中的最后一個元素允許是未知大小的數(shù)組,這就叫做柔性數(shù)組成員,但結(jié)構(gòu)中的柔性數(shù)組成員前面必須至少一個其 他成員。柔性數(shù)組成員允許結(jié)構(gòu)中包含一個大小可變的數(shù)組。sizeof返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。包含柔性數(shù)組成員的結(jié)構(gòu)用malloc ()函數(shù)進(jìn)行內(nèi)存的動態(tài)分配,并且分配的內(nèi)存應(yīng)該大于結(jié)構(gòu)的大小,以適應(yīng)柔性數(shù)組的預(yù)期大小。】 
            C語言大全,“柔性數(shù)組成員”

            【柔性數(shù)組結(jié)構(gòu)成員
              C99中,結(jié)構(gòu)中的最后一個元素允許是未知大小的數(shù)組,這就叫做柔性數(shù)組成員,但結(jié)構(gòu)中的柔性數(shù)組成員前面必須至少一個其 他成員。柔性數(shù)組成員允許結(jié)構(gòu)中包含一個大小可變的數(shù)組。sizeof返回的這種結(jié)構(gòu)大小不包括柔性數(shù)組的內(nèi)存。包含柔性數(shù)組成員的結(jié)構(gòu)用malloc ()函數(shù)進(jìn)行內(nèi)存的動態(tài)分配,并且分配的內(nèi)存應(yīng)該大于結(jié)構(gòu)的大小,以適應(yīng)柔性數(shù)組的預(yù)期大小?!?nbsp;
            C語言大全,“柔性數(shù)組成員”

            看看 C99 標(biāo)準(zhǔn)中 靈活數(shù)組成員:

            結(jié)構(gòu)體變長的妙用——0個元素的數(shù)組
            有時我們需要產(chǎn)生一個結(jié)構(gòu)體,實現(xiàn)了一種可變長度的結(jié)構(gòu)。如何來實現(xiàn)呢?
            看這個結(jié)構(gòu)體的定義:
            typedef struct st_type
            {
            int nCnt;
            int item[0];
            }type_a;
            (有些編譯器會報錯無法編譯可以改成:)
            typedef struct st_type
            {
            int nCnt;
            int item[];
            }type_a;
            這樣我們就可以定義一個可變長的結(jié)構(gòu),用sizeof(type_a)得到的只有4,就是sizeof(nCnt)=sizeof(int)那

            個0個元素的數(shù)組沒有占用空間,而后我們可以進(jìn)行變長操作了。
            C語言版:
            type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
            C++語言版:
            type_a *p = (type_a*)new char[sizeof(type_a)+100*sizeof(int)];
            這樣我們就產(chǎn)生了一個長為100的type_a類型的東西用p->item[n]就能簡單地訪問可變長元素,原理十分簡單

            ,分配了比sizeof(type_a)多的內(nèi)存后int item[];就有了其意義了,它指向的是int nCnt;后面的內(nèi)容,是沒

            有內(nèi)存需要的,而在分配時多分配的內(nèi)存就可以由其來操控,是個十分好用的技巧。
            而釋放同樣簡單:
            C語言版:
            free(p);
            C++語言版:
            delete []p;
            其實這個叫靈活數(shù)組成員(fleible array member)C89不支持這種東西,C99把它作為一種特例加入了標(biāo)準(zhǔn)。但

            是,C99所支持的是incomplete type,而不是zero array,形同int item[0];這種形式是非法的,C99支持的

            形式是形同int item[];只不過有些編譯器把int item[0];作為非標(biāo)準(zhǔn)擴(kuò)展來支持,而且在C99發(fā)布之前已經(jīng)有

            了這種非標(biāo)準(zhǔn)擴(kuò)展了,C99發(fā)布之后,有些編譯器把兩者合而為一。
            下面是C99中的相關(guān)內(nèi)容:
            6.7.2.1 Structure and union specifiers

            As a special case, the last element of a structure with more than one named member may have 

            an incomplete array type; this is called a flexible array member. With two exceptions, the 

            flexible array member is ignored. First, the size of the structure shall be equal to the offset 

            of the last element of an otherwise identical structure that replaces the flexible array member 

            with an array of unspecified length.106) Second, when a . (or ->) operator has a left operand 

            that is (a pointer to) a structure with a flexible array member and the right operand names that 

            member, it behaves as if that member were replaced with the longest array (with the same element 

            type) that would not make the structure larger than the object being accessed; the offset of the 

            array shall remain that of the flexible array member, even if this would differ from that of the 

            replacement array. If this array would have no elements, it behaves as if it had one element but 

            the behavior is undefined if any attempt is made to access that element or to generate a pointer 

            one past it.
            例如在VC++6里使用兩者之一都能通過編譯并且完成操作,而會產(chǎn)生warning C4200: nonstandard extension 

            used : zero-sized array in struct/union的警告消息。
            而在DEVCPP里兩者同樣可以使用,并且不會有警告消息。
            posted @ 2016-04-14 11:14 sheng 閱讀(475) | 評論 (0)編輯 收藏
                 摘要: 值得推薦的C/C++框架和庫【本文系外部轉(zhuǎn)貼,原文地址:http://coolshell.info/c/c++/2014/12/13/c-open-project.htm】留作存檔下次造輪子前先看看現(xiàn)有的輪子吧值得學(xué)習(xí)的C語言開源項目- 1. Webbench Webbench是一個在linux下使用的非常簡單的網(wǎng)站壓測工具。它使用fork()模擬多個客戶端同時訪問我們設(shè)定的URL,測試...  閱讀全文
            posted @ 2016-04-14 10:03 sheng 閱讀(680) | 評論 (0)編輯 收藏
                 摘要: Eric S. Raymond<esr@thyrsus.com>目錄1. 誰該閱讀這篇文章2. 我為什么寫這篇文章3.對齊要求4.填充5.結(jié)構(gòu)體對齊及填充6.結(jié)構(gòu)體重排序7.難以處理的標(biāo)量的情況8.可讀性和緩存局部性9.其他封裝的技術(shù)10.工具11.證明及例外12.版本履歷 1. 誰該閱讀這篇文章本文是關(guān)于削減C語言程序內(nèi)存占用空間的一項技術(shù)——為了減...  閱讀全文
            posted @ 2016-04-14 09:10 sheng 閱讀(337) | 評論 (0)編輯 收藏

            從實現(xiàn)裝飾者模式中思考C++指針和引用的選擇

             

            最近在看設(shè)計模式的內(nèi)容,偶然間手癢就寫了一個“裝飾者”模式的一個實例。該實例來源于風(fēng)雪漣漪的博客,我對它做了簡化。作為一個經(jīng)典的設(shè)計模式,本身并沒有太多要說的內(nèi)容。但是在我嘗試使用C++去實現(xiàn)這個模式的實例的時候,出現(xiàn)了一些看似無關(guān)緊要但是卻引人深思的問題。

            首先,我想簡單介紹一下這個實例的含義。實例的目的是希望通過裝飾器類對已有的蛋糕類進(jìn)行裝飾補(bǔ)充,于是按照裝飾者模式的設(shè)計結(jié)構(gòu),有類似圖1的設(shè)計結(jié)構(gòu)。

            1 裝飾者模式

            蛋糕類和裝飾器類都繼承于一個公共的基類,該基類聲明了一些公共接口。這里簡單的使用getName來返回當(dāng)前蛋糕的名稱,而裝飾器類可以對該蛋糕的名稱進(jìn)行修改補(bǔ)充。具體的蛋糕類都有自己的名稱,比如CheeseCake返回的是“奶油蛋糕”。如果使用了裝飾器類對該類進(jìn)行裝飾的話,返回的名字就發(fā)生了的變化,比如“裝飾了花的奶油蛋糕”,這正是裝飾器類的功能。實現(xiàn)這個功能的關(guān)鍵在于裝飾器公共基類Decorator,它包含了一個Cake類型的成員cake。在定義裝飾器的時候我們可以傳遞給裝飾器一個已經(jīng)建立好的蛋糕對象,比如CheeseCake對象。由于CheeseCakeCake的子類,因此該對象可以被cake成員記錄下來。由于具體的裝飾器繼承于裝飾器基類Decorator,因此保護(hù)乘員cake可以被看到,又因為裝飾器本身也是繼承與Cake的,因此也擁有getName的接口,這樣在裝飾器類內(nèi)用getName調(diào)用cakegetName接口并添加額外的操作就能完成裝飾的目的。另外,裝飾器本身也是Cake的子類,因此裝飾后的裝飾器類對象同時也是一個具體的蛋糕對象,它可以被再次裝飾!這樣裝飾器類反映在我們腦海里的情境就是一個原本的蛋糕對象外邊包裹了一層層裝飾器對象。

            以上的說明如果還不夠清楚的話,下邊展示具體的實現(xiàn)代碼。這里就需要考慮cake成員的類型問題,一般使用指針類型可能更符合C++的編程習(xí)慣。因為使用對象不僅消耗空間,還在每次構(gòu)造對象的時候進(jìn)行對象的復(fù)制,這都不是我們愿意看到的。當(dāng)然,使用引用或許更合理,因為按照平常的經(jīng)驗,很多使用C++指針的地方都可以用引用代替,有人甚至建議多使用引用少使用指針(當(dāng)然我也承認(rèn)C++引用也有很多好處~。不過,當(dāng)你讀完本文或許你就不大這么認(rèn)為了。首先,我們用Cake*pCake實現(xiàn)這個裝飾器類內(nèi)的成員,先具體了解一下這個代碼的具體內(nèi)容。

            按 Ctrl+C 復(fù)制代碼
            按 Ctrl+C 復(fù)制代碼

            從代碼中不難看出程序的輸出結(jié)構(gòu)應(yīng)該是“裝飾過花的裝飾過花的奶油蛋糕”,事實也的確如此!從裝飾器的使用格式來看FlowerDecorator(&FlowerDecorator(&CheeseCake()))倒也不至于十分麻煩。但是剛才討論過,如果能使用引用代替會許會更“舒服”,至少不用傳遞參數(shù)之前還要使用&獲取一下地址了~

            既然如此,我們把成員修改為引用格式的:

            復(fù)制代碼
            #pragma once
            #include <iostream>
            using namespace std;
            //Cake公共基類,提供裝飾者和被裝飾物的統(tǒng)一接口
            class Cake
            {
            public:
                virtual string getName()const=0;
            };

            //一個具體的蛋糕
            class CheeseCake:public Cake
            {
            public:
                virtual string getName()const
                {
                    return string("奶油蛋糕");
                }
            };

            //一個裝飾者基類
            class Decorator:public Cake
            {
            protected:
                Cake &pCake;
            public:
                Decorator(Cake&pc):pCake(pc){}
            };

            //一個具體的裝飾器
            class FlowerDecorator:public Decorator
            {
            public:
                FlowerDecorator(Cake&pc):Decorator(pc){}
                virtual string getName()const
                {
                    string decName="裝飾過花的";
                    decName+=pCake.getName();
                    return decName;
                }
            };
            int main()
            {
                cout<<
                    FlowerDecorator(
                        FlowerDecorator(
                            CheeseCake()
                    ))
                    .getName().c_str()
                    <<endl;
                return 0;
            }
            復(fù)制代碼

            修改后的代碼看起來的確更“順眼了”。因為調(diào)用的時候我們不用再寫那個看著別扭的取地址運算符了,然后我們滿懷欣喜的執(zhí)行了程序,輸出結(jié)果為:“裝飾過花的奶油蛋糕”!你我的第一反應(yīng)八成是覺得忘了多修飾一次了,但是我們認(rèn)真的檢查代碼,發(fā)現(xiàn)的確一切都是符合邏輯的……

            上邊做了這么多鋪墊就是為了引出這個奇怪的問題,我其實也被該問題困惑了很久。稍有編程經(jīng)驗的人都會跟蹤調(diào)試這些構(gòu)造函數(shù)的執(zhí)行過程,結(jié)果發(fā)現(xiàn)FlowerDecorator只被執(zhí)行了一次,因此少輸出一次“裝飾過花的”不足為奇。但是你我肯定好奇為什么會少輸出一次呢?

            再次再次的檢查代碼、調(diào)試、跟蹤,或許你會像發(fā)現(xiàn)新大陸一樣發(fā)現(xiàn)了一個隱藏的問題:第二次構(gòu)造FlowerDecorator時調(diào)用的是復(fù)制構(gòu)造函數(shù),而不是定義好的構(gòu)造函數(shù)(雖然子類FlowerDecoratorCake的子類,但是編譯器會自動最佳匹配函數(shù)參數(shù)類型)!由于復(fù)制構(gòu)造函數(shù)值原模原樣的拷貝出一個對象,所以只能完成一次裝飾器裝飾。非常完美的解釋!因此我們可以自己重寫復(fù)制構(gòu)造函數(shù)來完成我們的裝飾功能,這里先忽略原本的對象復(fù)制功能了。編譯器為我們生成的復(fù)制構(gòu)造函數(shù)應(yīng)該是:

            FlowerDecorator(const FlowerDecorator&pc):Decorator(pc.pCake){}

            而我們應(yīng)該將參數(shù)看作一個Cake對象進(jìn)行裝飾,因此修改為:

            FlowerDecorator(const FlowerDecorator&pc):Decorator(const_cast<FlowerDecorator&>(pc)){}

            同樣,由于構(gòu)造函數(shù)初始化了基類,所以基類的復(fù)制構(gòu)造也需要重寫:

            Decorator(const Decorator&pc):pCake(const_cast<Decorator&>(pc)){}

            即使傳遞的參數(shù)是FlowerDecorator對象和CakeDecorator不是一個類型,但是編譯器或許默認(rèn)的匹配繼承層次最近的類型!然后我們按照這樣要求重寫了代碼,執(zhí)行了程序,在期待結(jié)果的那一刻看到的是“裝飾過花的奶油蛋糕”……或許此時的你都會感到灰心,但是你還是依然的堅強(qiáng)的按下了F5單步跟蹤,結(jié)果你發(fā)現(xiàn)拷貝構(gòu)造函數(shù)并沒有被調(diào)用!難道以上的假設(shè)都錯了嗎?我可以確定的告訴讀者,我們以上的假設(shè)都是正確的。

            最終我也是沒有辦法,去StackOverFlow上求助,綜合回答者的討論,我終于把問題的原因鎖定了——編譯器優(yōu)化!我覺得用一個最簡單的例子來說明這個問題再合適不過了:

            復(fù)制代碼
            class A
            {
            public:
                A(int)
                {
                    cout<<"構(gòu)造\n";
                }
                A(const A&)
                {
                    cout<<"拷貝\n";
                }
            };

            int main()
            {
                A(0);
                cout<<"------------------------\n";
                A(A(0));
                cout<<"------------------------\n";
                A(A(A(0)));
                cout<<"------------------------\n";
                A(A(A(A(0))));
                cout<<"------------------------\n";
                A(A(A(A(A(0)))));
                cout<<"------------------------\n";
                return 0;
            }
            復(fù)制代碼

            這個簡單的例子結(jié)果或許大家都很明白,但是你親自測試一下就可能要懷疑自己的判斷能力了,程序輸出:

            是不是有點世界觀被顛覆的感覺?需要聲明一下,這個是Visual Studio 2010下的測試結(jié)果,因為這個程序的輸出的確和編譯器相關(guān)!為了確認(rèn)我用gcc-4.4.3測試了該段代碼,輸出結(jié)果為:

            看來,還是gcc優(yōu)化的比較徹底。因此我們可以得出結(jié)論,類似這種無名對象的構(gòu)造(有名的是按照規(guī)矩來的),調(diào)用多少次構(gòu)造函數(shù)要看編譯器的“脾氣”了。到這里,不知道你對引用參數(shù)的感覺如何?

            討論到這,或許有人說和本來要討論的話題離得太遠(yuǎn)了。其實并不是,佛家說:“今日之果皆來自昨日之因”,一切的一切都是由于我們使用了本以為毫無懸念的引用導(dǎo)致的!如果使用指針就不可能發(fā)生和拷貝構(gòu)造函數(shù)沖突的問題,也不會導(dǎo)致編譯器優(yōu)化的問題!回視本文剛開始舉的例子和該文的主題,或許我們應(yīng)該清楚有時候的確要好好區(qū)分一下指針和引用的差別了,當(dāng)然本文也是從一個實踐的例子中去發(fā)現(xiàn)和挖掘這一點。

            posted @ 2013-04-05 11:27 sheng 閱讀(208) | 評論 (0)編輯 收藏
             


            STL跨平臺調(diào)用會出現(xiàn)很多異常,你可以試試.

            STL使用模板生成,當(dāng)我們使用模板的時候,每一個EXE,和DLL都在編譯器產(chǎn)生了自己的代碼,導(dǎo)致模板所使用的靜態(tài)成員不同步,所以出現(xiàn)數(shù)據(jù)傳遞的各種問題,下面是詳細(xì)解釋。

            原因分析:
            一句話-----如果任何STL類使用了靜態(tài)變量(無論是直接還是間接使用),那么就不要再寫出跨執(zhí)行單元訪問它的代碼。 除非你能夠確定兩個動態(tài)庫使用的都是同樣的STL實現(xiàn),比如都使用VC同一版本的STL,編譯選項也一樣。強(qiáng)烈建議,不要在動態(tài)庫接口中傳遞STL容器??!

            STL不一定不能在DLL間傳遞,但你必須徹底搞懂它的內(nèi)部實現(xiàn),并懂得為何會出問題。
            微軟的解釋:
            http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b172396
            微軟給的解決辦法:
            http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b168958

            1、微軟的解釋:
            大部分C++標(biāo)準(zhǔn)庫里提供的類直接或間接地使用了靜態(tài)變量。由于這些類是通過模板擴(kuò)展而來的,因此每個可執(zhí)行映像(通常是.dll或.exe文件)就會存在一份只屬于自己的、給定類的靜態(tài)數(shù)據(jù)成員。當(dāng)一個需要訪問這些靜態(tài)成員的類方法執(zhí)行時,它使用的是“這個方法的代碼當(dāng)前所在的那份可執(zhí)行映像”里的靜態(tài)成員變量。由于兩份可執(zhí)行映像各自的靜態(tài)數(shù)據(jù)成員并未同步,這個行為就可能導(dǎo)致訪問違例,或者數(shù)據(jù)看起來似乎丟失或被破壞了。

            可能不太好懂,我舉個例子:假如類A<T>有個靜態(tài)變量m_s,那么當(dāng)1.exe使用了2.dll中提供的某個A<int>對象時,由于模板擴(kuò)展機(jī)制,1.exe和2.dll中會分別存在自己的一份類靜態(tài)變量A<int>.m_s。
            這樣,假如1.exe中從2.dll中取得了一個的類A<int>的實例對象a,那么當(dāng)在1.exe中直接訪問a.m_s時,其實訪問的是 1.exe中的對應(yīng)拷貝(正確情況應(yīng)該是訪問了2.dll中的a.m_s)。這樣就可能導(dǎo)致非法訪問、應(yīng)當(dāng)改變的數(shù)據(jù)沒有改變、不應(yīng)改變的數(shù)據(jù)被錯誤地更改等異常情形。

            原文:
            Most classes in the Standard C++ Libraries use static data members directly or indirectly. Since these classes are generated through template instantiation, each executable image (usually with DLL or EXE file name extensions) will contain its own copy of the static data member for a given class. When a method of the class that requires the static data member is executed, it uses the static data member in the executable image in which the method code resides. Since the static data members in the executable images are not in sync, this action could result in an access violation or data may appear to be lost or corrupted.

            1、保證資源的分配/刪除操作對等并處于同一個執(zhí)行單元;
               比如,可以把這些操作(包括構(gòu)造/析構(gòu)函數(shù)、某些容器自動擴(kuò)容{這個需要特別注意}時的內(nèi)存再分配等)隱藏到接口函數(shù)里面。換句話說:盡量不要直接從dll中輸出stl對象;如果一定要輸出,給它加上一層包裝,然后輸出這個包裝接口而不是原始接口。

            2、保證所有的執(zhí)行單元使用同樣版本的STL運行庫。
               比如,全部使用release庫或debug庫,否則兩個執(zhí)行單元擴(kuò)展出來的STL類的內(nèi)存布局就可能會不一樣。

            只要記住關(guān)鍵就是:如果任何STL類使用了靜態(tài)變量(無論是直接還是間接使用),那么就不要再寫出跨執(zhí)行單元訪問它的代碼。

            解決方法:
            1. 一個可以考慮的方案
            比如有兩個動態(tài)庫L1和L2,L2需要修改L1中的一個map,那么我在L1中設(shè)置如下接口
            int modify_map(int key, int new_value);
            如果需要指定“某一個map”,則可以考慮實現(xiàn)一種類似于句柄的方式,比如可以傳遞一個DWORD
            不過這個DWORD放的是一個地址

            那么modify_map就可以這樣實現(xiàn):
            int modify_map(DWORD map_handle, int key, int new_value)
            {
                std::map<int, int>& themap = *(std::map<int, int>*)map_handle;
                themap[key] = new_value;
            }

            map_handle的值也首先由L1“告訴”L2:
            DWORD get_map_handle();

            L2可以這樣調(diào)用:
            DWORD h = get_map_handle();
            modify_map(h, 1, 2);

            2. 加入一個額外的層,就可以解決問題。所以,你需要將你的Map包裝在dll內(nèi)部,而不是讓它出現(xiàn)在接口當(dāng)中。動態(tài)庫的接口越簡單越好,不好去傳太過復(fù)雜的東東是至理名言:)

             

            在動態(tài)連接庫開發(fā)中要特別注意內(nèi)存的分配與釋放問題,稍不注意,極可能造成內(nèi)存泄漏,從而訪問出錯。例如在某DLL中存在這樣一段代碼:

            extent "C" __declspec(dllexport) 
            void ExtractFileName( const std::string& path //!< Input path and filename.
            , std::string& fname //!< Extracted filename with extension.
            )
            {
            std::string::size_type startPos = path.find_last_of('\\');
            fname.assign(path.begin() startPos 1, path.end() );
            }

            在DLL中使用STL對象std::string,并且在其中改變std::string的內(nèi)容,即發(fā)生了內(nèi)存的重分配問題,若在EXE中調(diào)用該函數(shù)會出現(xiàn)內(nèi)存訪問問題。主要是:因為DLL和EXE的內(nèi)存分配方式不同,DLL中的分配的內(nèi)存不能在EXE中正確釋放掉。

            解決這一問題的途徑如下:
            一般情況下:構(gòu)建DLL必須遵循誰分配就由誰釋放的原則,例如COM的解決方案(利用引用計數(shù)),對象的創(chuàng)建(QueryInterface)與釋放均在COM組件內(nèi)部完成。在純C 環(huán)境下,可以很容易的實現(xiàn)類似方案。


            在應(yīng)用STL的情況下,很難使用上述方案來解決,因此必須另辟蹊徑,途徑有二:
            1、自己寫內(nèi)存分配器替代STL中的默認(rèn)分配器。
            2、使用STLport替代系統(tǒng)的標(biāo)準(zhǔn)庫。

            其實,上述問題在VC7及以后版本中,已得到解決,注意DLL工程和調(diào)用的工程一定要使用多線程DLL庫,就不會發(fā)生內(nèi)存訪問問題。

             

             

            一個很奇怪的問題:DLL中使用std::string作為參數(shù)結(jié)果出錯

            這段時間,在工程中將一些功能封裝成動態(tài)庫,需要使用動態(tài)庫接口的時候.使用了STL的一些類型作為參數(shù).

            比方string,vector,list.但是在使用接口的時候.
            1. class exportClass
            2. {
            3.      bool dll_funcation(string &str);
            4. };
            復(fù)制代碼
            //上面這個類只是一個形式,具體內(nèi)容不寫出來了.這個類被導(dǎo)出

            當(dāng)我在使用這個庫的時候.這樣寫代碼:
            1. string str="":
            2. exportClass tmp;
            3. tmp.dll_function(str);
            復(fù)制代碼
            這個函數(shù)能成功調(diào)用.但是在函數(shù)里面會給這個數(shù)組附值.如果字符串太長,就會出錯.函數(shù)調(diào)用能成功,但是一旦str資源需要釋放的時候,資源就不能釋放了,提示釋放了錯誤的內(nèi)存空間.

            一點一點取掉這個函數(shù)的代碼.最后就剩下

            str="qadasdasdasdsafsafas";

            還是出錯誤.

            如果改成很短的字符串,就不會出錯誤.
            在這個時候,只能嘗試認(rèn)為是字符串的空間太小

            最終我修改成這樣,錯誤消失了.希望錯誤真的是這個引起的
            1. string str="":

            2. str.resize(1000);

            3. exportClass tmp;

            4. tmp.dll_function(str);

             

            今天寫程序的時候要給一個模塊的dll傳遞一個參數(shù),由于參數(shù)數(shù)量是可變的,因此設(shè)計成了vector<string>類型,但調(diào)試過程中發(fā)現(xiàn)在exe中的參數(shù)傳遞到dll中的函數(shù)后,vector變成空的,改成傳引用類型后,vector竟然變得很大,并且是無意義的參數(shù)。

            對于這個問題,兩種辦法:

            1.傳遞vector指針

            2.傳遞const vector<TYPE>。

            究其原因:

            是因為vector在exe和dll之間傳遞的時候,由于在dll內(nèi)可能對vector插入數(shù)據(jù),而這段內(nèi)存是在dll里面分配的,exe無法知道如何釋放內(nèi)存,從而導(dǎo)致問題。而改成const類型后,編譯器便知道dll里不會改變vector,從而不會出錯。

            或者可以說這是"cross-DLL problem."(This problem crops up when an object is created using new in one dynamically linked library (DLL) but is deleted in a different DLL)的一種吧。

            對于STL,在DLL中使用的時候,往往存在這些問題,在網(wǎng)絡(luò)上搜集了下,這些都是要平時使用STL的時候注意的。

            ***************************************************************************************************************

            引用http://www.hellocpp.net/Articles/Article/714.aspx

            當(dāng)template 遭遇到dynamic link 時候, 很多時候卻是一場惡夢.
            現(xiàn)在來說說一部分我已經(jīng)碰到過的問題. 問題主要集中在內(nèi)存分配上.
            1> 
                  拿STL來說, 自己寫模板的時候,很難免就用到stl. stl的代碼都在頭文件里. 那么表示著內(nèi)存分配的代碼.只有包含了它的cpp 編譯的時候才會被決定是使用什么樣的內(nèi)存分配代碼. 考慮一下: 當(dāng)你聲明了一個vector<> . 并把這個vector<>交給一個 dll里的代碼來用. 用完后, 在你的程序里被釋放了.    那么如果你 在dll里往vector里insert了一些東西. 那么這個時候insert 發(fā)生的內(nèi)存分配的代碼是屬于dll的. 你不知道這個dll的內(nèi)存分配是什么. 是分配在哪里的. 而這個時候.釋放那促的動作卻不在dll里.....同時. 你甚至無法保證編譯dll的那個家伙使用的stl版本和你是完全一樣的..>
                  如此說來, 程序crash掉是天經(jīng)地義的.... 
                  對策: 千萬別別把你的stl 容器,模板容器在 dll 間傳來傳去 . 記住string也是....

            2> 
                 你在dll的某個類里聲明了一個vector之類的容器. 而沒有顯式的寫這個類的構(gòu)造和析構(gòu)函數(shù). 那么問題又來了.
                 你這個類肯定有操作這vector的函數(shù). 那么這些函數(shù)會讓vecoter<>生成代碼. 這些代碼在這個dll里都是一致的. 但是別忘了.你沒有寫析構(gòu)函數(shù)...... 如果這個時候, 別人在外面聲明了一個這樣的類.然后調(diào)用這個類的函數(shù)操作了這個vector( 當(dāng)然使用者并不知道什么時候操作了vector) . 它用完了這個類以后. 類被釋放掉了. 編譯器很負(fù)責(zé)的為它生成了一份析構(gòu)函數(shù)的代碼...... 聽好了.這份代碼并不是在 dll里 ... . 事情于是又和1>里的一樣了.... crash ......(可能還會伴隨著迷茫.....)
                 對策: 記得dll里每個類,哪怕式構(gòu)造析構(gòu)函數(shù)式空的. 也要寫到cpp里去. 什么都不寫也式很糟糕的.....同時,更要把任何和內(nèi)存操作有關(guān)的函數(shù)寫到 .cpp 里...

            3> 
                以上兩個問題似乎都是比較容易的-----只要把代碼都寫到cpp里去, 不要用stl容器傳來傳去就可以了.
               那么第三個問題就要麻煩的多.
               如果你自己寫了一個模板, 這個模板用了stl 容器..........
               這個時候你該怎么辦呢?
             顯然你無法把和內(nèi)存分配相關(guān)的函數(shù)都寫到.cpp里去 . template的代碼都必須放到header file里.....
               對策: 解決這個問題的基本做法是做一個stl 內(nèi)存分配器 , 強(qiáng)制把這個模板里和內(nèi)存分配相關(guān)的放到一個.cpp里去.這個時候編譯這個cpp就會把內(nèi)存分配代碼固定在一個地方: 要么是dll. 要么是exe里...

            模板+動態(tài)鏈接庫的使用問題還很多. 要千萬留心這個陷阱遍地的東西啊

            ***************************************************************************************************************************

            微軟關(guān)于這類問題的解釋:

            You may experience an access violation when you access an STL object through a pointer or reference in a different DLL or EXE

            http://support.microsoft.com/default.aspx?scid=KB;en-us;q172396

            How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object

            http://support.microsoft.com/default.aspx?scid=KB;en-us;q168958

             

             

             

            總結(jié):

            字符串參數(shù)用char*,Vector用char**,

            動態(tài)內(nèi)存要牢記誰申請誰釋放的原則。
             
            posted @ 2013-03-01 13:58 sheng 閱讀(6714) | 評論 (0)編輯 收藏
            僅列出標(biāo)題
            共3頁: 1 2 3 

            導(dǎo)航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統(tǒng)計

            常用鏈接

            留言簿(1)

            隨筆檔案

            收藏夾

            同行

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            香港aa三级久久三级| 色欲久久久天天天综合网 | 国产视频久久| 久久精品无码av| 亚洲AV日韩精品久久久久| 国产综合久久久久久鬼色| 久久美女人爽女人爽| 中文字幕亚洲综合久久菠萝蜜| 国产精品成人久久久久三级午夜电影| 久久99精品国产麻豆不卡| 热re99久久6国产精品免费| 久久久青草久久久青草| 成人久久综合网| 久久久中文字幕| 久久久久亚洲精品中文字幕| 久久性精品| 蜜桃麻豆WWW久久囤产精品| 中文字幕精品无码久久久久久3D日动漫 | 久久av无码专区亚洲av桃花岛| 亚洲国产精品无码久久久秋霞2 | 久久婷婷人人澡人人爽人人爱| 亚洲欧洲中文日韩久久AV乱码| 四虎久久影院| 亚洲精品乱码久久久久久久久久久久| 亚洲综合熟女久久久30p| 久久精品国产亚洲av麻豆小说| 久久久久久午夜成人影院| 精品精品国产自在久久高清 | 久久伊人影视| 欧美日韩精品久久久久| 久久精品国产亚洲av日韩| 狠狠综合久久综合中文88| 思思久久精品在热线热| 伊人久久大香线蕉亚洲| 91精品国产乱码久久久久久| 97超级碰碰碰碰久久久久| 亚洲午夜精品久久久久久浪潮| 青青草原精品99久久精品66| 久久99精品国产99久久6| 日产精品久久久久久久| 国产精品美女久久久网AV|