#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;
摘要: 轉載自http://blog.csdn.net/u010984552/article/details/51887108為什么需要線程池目前的大多數(shù)網(wǎng)絡服務器,包括Web服務器、Email服務器以及數(shù)據(jù)庫服務器等都具有一個共同點,就是單位時間內(nèi)必須處理數(shù)目巨大的連接請求,但處理時間卻相對較短。 傳 統(tǒng)多線程方案中我們采用的服務器模型則是一旦接受到請求之后,即創(chuàng)建一個新的線程,由該線程執(zhí)行任...
閱讀全文
摘自http://blog.csdn.net/hackbuteer1/article/details/7722667
在 各種計算機體系結構中,對于字節(jié)、字等的存儲機制有所不同,因而引發(fā)了計算機 通信領 域中一個很重要的問題,即通信雙方交流的信息單元(比特、字節(jié)、字、雙字等等)應該以什么樣的順序進行傳送。如果不達成一致的規(guī)則,通信雙方將無法進行正 確的編/譯碼從而導致通信失敗。目前在各種體系的計算機中通常采用的字節(jié)存儲機制主要有兩種:Big-Endian和Little-Endian,下面先從字節(jié)序說起。
一、什么是字節(jié)序
字節(jié)序,顧名思義字節(jié)的順序,再多說兩句就是大于一個字節(jié)類型的數(shù)據(jù)在內(nèi)存中的存放順序(一個字節(jié)的數(shù)據(jù)當然就無需談順序的問題了)。其實大部分人在實際的開 發(fā)中都很少會直接和字節(jié)序打交道。唯有在跨平臺以及網(wǎng)絡程序中字節(jié)序才是一個應該被考慮的問題。
在所有的介紹字節(jié)序的文章中都會提到字 節(jié)序分為兩類:Big-Endian和Little-Endian,引用標準的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)絡字節(jié)序:TCP/IP各層協(xié)議將字節(jié)序定義為Big-Endian,因此TCP/IP協(xié)議中使用的字節(jié)序通常稱之為網(wǎng)絡字節(jié)序。
1.1 什么是高/低地址端
首先我們要知道C程序映像中內(nèi)存的空間布局情況:在《C專 家編程》中或者《Unix環(huá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,那么高位是什么,低位又是什么呢? 其實很簡單。在十進制中我們都說靠左邊的是高位,靠右邊的是低位,在其他進制也是如此。就拿 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
計算機體系結構中一種描述多字節(jié)存儲順序的術語,在這種機制中最重要字節(jié)(MSB)存放在最低端的地址 上。采用這種機制的處理器有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
計算機體系結構中 一種描述多字節(jié)存儲順序的術語,在這種機制中最不重要字節(jié)(LSB)存放在最低端的地址上。采用這種機制的處理器有PDP-11、VAX、Intel系列微處理器和一些網(wǎng)絡通信設備。該術語除了描述多字節(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序列編碼方式
注意:通常我們說的主機序(Host Order)就是遵循Little-Endian規(guī)則。所以當兩臺主機之間要通過TCP/IP協(xié)議進行通信的時候就需要調用相應的函數(shù)進行主機序 (Little-Endian)和網(wǎng)絡序(Big-Endian)的轉換。
采用 Little-endian模式的CPU對操作數(shù)的存放方式是從低字節(jié)到高字節(jié),而Big-endian模式對操作數(shù)的存放方式是從高字節(jié)到低字節(jié)。 32bit寬的數(shù)0x12345678在Little-endian模式CPU內(nèi)存中的存放方式(假設從地址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ù)還是負數(shù)。你不必知道這個數(shù)值有多長,或者你也不必過一些字節(jié)來看這個數(shù)值是否含有符號位。這個數(shù)值是以它們被打印出來的順序存放的,所以從二進制到十進制的函數(shù)特別有效。因而,對于不同要求的機器,在設計存取方式時就會不同。
Little-Endian優(yōu)點:提取一個,兩個,四個或者更長字節(jié)數(shù)據(jù)的匯編指令以與其他所有格式相同的方式進行:首先在偏移地址為0的地方提取最低位的字節(jié),因為地址偏移和字節(jié)數(shù)是一對一的關系,多重精度的數(shù)學函數(shù)就相對地容易寫了。
如果你增加數(shù)字的值,你可能在左邊增加數(shù)字(高位非指數(shù)函數(shù)需要更多的數(shù)字)。 因此, 經(jīng)常需要增加兩位數(shù)字并移動存儲器里所有Big-endian順序的數(shù)字,把所有數(shù)向右移,這會增加計算機的工作量。不過,使用Little- Endian的存儲器中不重要的字節(jié)可以存在它原來的位置,新的數(shù)可以存在它的右邊的高位地址里。這就意味著計算機中的某些計算可以變得更加簡單和快速。
四、請寫一個C函數(shù),若處理器是Big_endian的,則返回0;若是Little_endian的,則返回1。
- int checkCPU(void)
- {
- union
- {
- int a;
- char b;
- }c;
- c.a = 1;
- return (c.b == 1);
- }
剖析:由于聯(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進制數(shù)是 0x11 22 33,其存放的位置是
地址0x3000 中存放11
地址0x3001 中存放22
地址0x3002 中存放33
連起來就寫成地址0x3000-0x3002中存放了數(shù)據(jù)0x112233
而這種存放和表示方式,正好符合大端。
另外一個比較好理解的寫法如下:
- bool checkCPU() // 如果是大端模式,返回真
- {
- short int test = 0x1234;
-
- if( *((char *)&test) == 0x12) // 低地址存放高字節(jié)數(shù)據(jù)
- return true;
- else
- return false;
- }
-
- int main(void)
- {
- if( !checkCPU())
- cout<<"Little endian"<<endl;
- else
- cout<<"Big endian"<<endl;
-
- return 0;
- }
或者下面兩種寫法也是可以的- int main(void)
- {
- short int a = 0x1234;
- char *p = (char *)&a;
-
- if( *p == 0x34)
- cout<<"Little endian"<<endl;
- else
- cout<<"Big endian"<<endl;
-
- return 0;
- }
-
- int main(void)
- {
- short int a = 0x1234;
- char x0 , x1;
-
- x0 = ((char *)&a)[0];
- x1 = ((char *)&a)[1];
-
- if( x0 == 0x34)
- cout<<"Little endian"<<endl;
- else
- cout<<"Big endian"<<endl;
-
- return 0;
- }
摘要: C++ 虛函數(shù)表解析 陳皓http://blog.csdn.net/haoel 前言 C++中的虛函數(shù)的作用主要是實現(xiàn)了多態(tài)的機制。關于多態(tài),簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調用實際子類的成員函數(shù)。這種技術可以讓父類的指針有“多種形態(tài)”,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變...
閱讀全文
首先看看如下一個聲明:
int* ( *( *fun )( int* ) )[10];
這是一個會讓初學者感到頭暈目眩、感到恐懼的函數(shù)指針聲明。在熟練掌握C/C++的聲明語法之前,不學習一定的規(guī)則,想理解好這類復雜聲明是比較困難的。
C/C++所有復雜的聲明結構,都是由各種聲明嵌套構成的。如何解讀復雜指針聲明?右左法則是一個很著名、很有效的方法。不過,右左法則其實并不是C/C++標準里面的內(nèi)容,它是從C/C++標準的聲明規(guī)定中歸納出來的方法。C/C++標準的聲明規(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.
這段英文的翻譯如下:
右左法則:首先從最里面的圓括號看起,然后往右看,再往左看。每當遇到圓括號時,就應該掉轉閱讀方向。一旦解析完圓括號里面所有的東西,就跳出圓括號。重復這個過程直到整個聲明解析完畢。
筆者要對這個法則進行一個小小的修正,應該是從未定義的標識符開始閱讀,而不是從括號讀起,之所以是未定義的標識符,是因為一個聲明里面可能有多個標識符,但未定義的標識符只會有一個。
現(xiàn)在通過一些例子來討論右左法則的應用,先從最簡單的開始,逐步加深:
int (*func)(int *p);
首先找到那個未定義的標識符,就是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先跟[]結合,因此*修飾的是func[5]。跳出這個括號,看右邊,也是一對圓括號,說明func數(shù)組的元素是函數(shù)類型的指針,它所指向的函數(shù)具有int*類型的形參,返回值類型為int。
int (*(*func)[5])(int *p);
func被一個圓括號包含,左邊又有一個*,那么func是一個指針,跳出括號,右邊是一個[]運算符號,說明func是一個指向數(shù)組的指針,現(xiàn)在往左看,左邊有一個*號,說明這個數(shù)組的元素是指針,再跳出括號,右邊又有一個括號,說明這個數(shù)組的元素是指向函數(shù)的指針。總結一下,就是:func是一個指向數(shù)組的指針,這個數(shù)組的元素是函數(shù)指針,這些指針指向具有int*形參,返回值為int類型的函數(shù)。
int (*(*func)(int *p))[5];
func是一個函數(shù)指針,這類函數(shù)具有int*類型的形參,返回值是指向數(shù)組的指針,所指向的數(shù)組的元素是具有5個int元素的數(shù)組。
要注意有些復雜指針聲明是非法的,例如:
int func(void) [5];
func是一個返回值為具有5個int元素的數(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ù)組的元素。
實際編程當中,需要聲明一個復雜指針時,如果把整個聲明寫成上面所示這些形式,將對可讀性帶來一定的損害,應該用typedef來對聲明逐層分解,增強可讀性。
typedef是一種聲明,但它聲明的不是變量,也沒有創(chuàng)建新類型,而是某種類型的別名。typedef有很大的用途,對一個復雜聲明進行分解以增強可讀性是其作用之一。例如對于聲明:
int (*(*func)(int *p))[5];
可以這樣分解:
typedef int (*PARA)[5];
typedef PARA (*func)(int *);
這樣就容易看得多了。
typedef的另一個作用,是作為基于對象編程的高層抽象手段。在ADT中,它可以用來在C/C++和現(xiàn)實世界的物件間建立關聯(lián),將這些物件抽象成C/C++的類型系統(tǒng)。在設計ADT的時候,我們常常聲明某個指針的別名,例如:
typedef struct node * list;
從ADT的角度看,這個聲明是再自然不過的事情,可以用list來定義一個列表。但從C/C++語法的角度來看,它其實是不符合C/C++聲明語法的邏輯的,它暴力地將指針聲明符從指針聲明器中分離出來,這會造成一些異于人們閱讀習慣的現(xiàn)象,考慮下面代碼:
const struct node *p1;
typedef struct node *list;
const list p2;
p1類型是const struct node*,那么p2呢?如果你以為就是把list簡單“代入”p2,然后得出p2類型也是const struct node*的結果,就大錯特錯了。p2的類型其實是struct node * const p2,那個const限定的是p2,不是node。造成這一奇異現(xià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和標識符D組成,這三者在邏輯上是一個整體,構成一個完整的指針聲明器。這也是多個變量同列定義時指針聲明符必須緊跟標識符的原因,例如:
int *p, q, *k;
p和k都是指針,但q不是,這是因為*p、*k是一個整體指針聲明器,以表示聲明的是一個指針。編譯器會把指針聲明符左邊的類型包括其限定詞作為指針指向的實體的類型,右邊的限定詞限定被聲明的標識符。但現(xiàn)在typedef struct node *list硬生生把*從整個指針聲明器中分離出來,編譯器找不到*,會認為const list p2中的const是限定p2的,正因如此,p2的類型是node * const而不是const node*。
雖然typedef struct node* list不符合聲明語法的邏輯,但基于typedef在ADT中的重要作用以及信息隱藏的要求,我們應該讓用戶這樣使用list A,而不是list *A,因此在ADT的設計中仍應使用上述typedef語法,但需要注意其帶來的不利影響。
摘要: 值得推薦的C/C++框架和庫【本文系外部轉貼,原文地址:http://coolshell.info/c/c++/2014/12/13/c-open-project.htm】留作存檔下次造輪子前先看看現(xiàn)有的輪子吧值得學習的C語言開源項目- 1. Webbench Webbench是一個在linux下使用的非常簡單的網(wǎng)站壓測工具。它使用fork()模擬多個客戶端同時訪問我們設定的URL,測試...
閱讀全文
摘要: Eric S. Raymond<esr@thyrsus.com>目錄1. 誰該閱讀這篇文章2. 我為什么寫這篇文章3.對齊要求4.填充5.結構體對齊及填充6.結構體重排序7.難以處理的標量的情況8.可讀性和緩存局部性9.其他封裝的技術10.工具11.證明及例外12.版本履歷 1. 誰該閱讀這篇文章本文是關于削減C語言程序內(nèi)存占用空間的一項技術——為了減...
閱讀全文
STL跨平臺調用會出現(xiàn)很多異常,你可以試試.
STL使用模板生成,當我們使用模板的時候,每一個EXE,和DLL都在編譯器產(chǎn)生了自己的代碼,導致模板所使用的靜態(tài)成員不同步,所以出現(xiàn)數(shù)據(jù)傳遞的各種問題,下面是詳細解釋。
原因分析:
一句話-----如果任何STL類使用了靜態(tài)變量(無論是直接還是間接使用),那么就不要再寫出跨執(zhí)行單元訪問它的代碼。 除非你能夠確定兩個動態(tài)庫使用的都是同樣的STL實現(xiàn),比如都使用VC同一版本的STL,編譯選項也一樣。強烈建議,不要在動態(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++標準庫里提供的類直接或間接地使用了靜態(tài)變量。由于這些類是通過模板擴展而來的,因此每個可執(zhí)行映像(通常是.dll或.exe文件)就會存在一份只屬于自己的、給定類的靜態(tài)數(shù)據(jù)成員。當一個需要訪問這些靜態(tài)成員的類方法執(zhí)行時,它使用的是“這個方法的代碼當前所在的那份可執(zhí)行映像”里的靜態(tài)成員變量。由于兩份可執(zhí)行映像各自的靜態(tài)數(shù)據(jù)成員并未同步,這個行為就可能導致訪問違例,或者數(shù)據(jù)看起來似乎丟失或被破壞了。
可能不太好懂,我舉個例子:假如類A<T>有個靜態(tài)變量m_s,那么當1.exe使用了2.dll中提供的某個A<int>對象時,由于模板擴展機制,1.exe和2.dll中會分別存在自己的一份類靜態(tài)變量A<int>.m_s。
這樣,假如1.exe中從2.dll中取得了一個的類A<int>的實例對象a,那么當在1.exe中直接訪問a.m_s時,其實訪問的是 1.exe中的對應拷貝(正確情況應該是訪問了2.dll中的a.m_s)。這樣就可能導致非法訪問、應當改變的數(shù)據(jù)沒有改變、不應改變的數(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í)行單元;
比如,可以把這些操作(包括構造/析構函數(shù)、某些容器自動擴容{這個需要特別注意}時的內(nèi)存再分配等)隱藏到接口函數(shù)里面。換句話說:盡量不要直接從dll中輸出stl對象;如果一定要輸出,給它加上一層包裝,然后輸出這個包裝接口而不是原始接口。
2、保證所有的執(zhí)行單元使用同樣版本的STL運行庫。
比如,全部使用release庫或debug庫,否則兩個執(zhí)行單元擴展出來的STL類的內(nèi)存布局就可能會不一樣。
只要記住關鍵就是:如果任何STL類使用了靜態(tài)變量(無論是直接還是間接使用),那么就不要再寫出跨執(zhí)行單元訪問它的代碼。
解決方法:
1. 一個可以考慮的方案
比如有兩個動態(tài)庫L1和L2,L2需要修改L1中的一個map,那么我在L1中設置如下接口
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可以這樣調用:
DWORD h = get_map_handle();
modify_map(h, 1, 2);
2. 加入一個額外的層,就可以解決問題。所以,你需要將你的Map包裝在dll內(nèi)部,而不是讓它出現(xiàn)在接口當中。動態(tài)庫的接口越簡單越好,不好去傳太過復雜的東東是至理名言:)
在動態(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中調用該函數(shù)會出現(xiàn)內(nèi)存訪問問題。主要是:因為DLL和EXE的內(nèi)存分配方式不同,DLL中的分配的內(nèi)存不能在EXE中正確釋放掉。
解決這一問題的途徑如下:
一般情況下:構建DLL必須遵循誰分配就由誰釋放的原則,例如COM的解決方案(利用引用計數(shù)),對象的創(chuàng)建(QueryInterface)與釋放均在COM組件內(nèi)部完成。在純C 環(huán)境下,可以很容易的實現(xiàn)類似方案。
在應用STL的情況下,很難使用上述方案來解決,因此必須另辟蹊徑,途徑有二:
1、自己寫內(nèi)存分配器替代STL中的默認分配器。
2、使用STLport替代系統(tǒng)的標準庫。
其實,上述問題在VC7及以后版本中,已得到解決,注意DLL工程和調用的工程一定要使用多線程DLL庫,就不會發(fā)生內(nèi)存訪問問題。
一個很奇怪的問題:DLL中使用std::string作為參數(shù)結果出錯
這段時間,在工程中將一些功能封裝成動態(tài)庫,需要使用動態(tài)庫接口的時候.使用了STL的一些類型作為參數(shù).
比方string,vector,list.但是在使用接口的時候.- class exportClass
- {
- bool dll_funcation(string &str);
- };
復制代碼 //上面這個類只是一個形式,具體內(nèi)容不寫出來了.這個類被導出
當我在使用這個庫的時候.這樣寫代碼:- string str="":
- exportClass tmp;
- tmp.dll_function(str);
復制代碼 這個函數(shù)能成功調用.但是在函數(shù)里面會給這個數(shù)組附值.如果字符串太長,就會出錯.函數(shù)調用能成功,但是一旦str資源需要釋放的時候,資源就不能釋放了,提示釋放了錯誤的內(nèi)存空間.
一點一點取掉這個函數(shù)的代碼.最后就剩下
str="qadasdasdasdsafsafas";
還是出錯誤.
如果改成很短的字符串,就不會出錯誤. 在這個時候,只能嘗試認為是字符串的空間太小
最終我修改成這樣,錯誤消失了.希望錯誤真的是這個引起的- string str="":
- str.resize(1000);
- exportClass tmp;
- tmp.dll_function(str);
|
今天寫程序的時候要給一個模塊的dll傳遞一個參數(shù),由于參數(shù)數(shù)量是可變的,因此設計成了vector<string>類型,但調試過程中發(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)存,從而導致問題。而改成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)絡上搜集了下,這些都是要平時使用STL的時候注意的。
***************************************************************************************************************
引用http://www.hellocpp.net/Articles/Article/714.aspx
當template 遭遇到dynamic link 時候, 很多時候卻是一場惡夢.
現(xiàn)在來說說一部分我已經(jīng)碰到過的問題. 問題主要集中在內(nèi)存分配上.
1>
拿STL來說, 自己寫模板的時候,很難免就用到stl. stl的代碼都在頭文件里. 那么表示著內(nèi)存分配的代碼.只有包含了它的cpp 編譯的時候才會被決定是使用什么樣的內(nèi)存分配代碼. 考慮一下: 當你聲明了一個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之類的容器. 而沒有顯式的寫這個類的構造和析構函數(shù). 那么問題又來了.
你這個類肯定有操作這vector的函數(shù). 那么這些函數(shù)會讓vecoter<>生成代碼. 這些代碼在這個dll里都是一致的. 但是別忘了.你沒有寫析構函數(shù)...... 如果這個時候, 別人在外面聲明了一個這樣的類.然后調用這個類的函數(shù)操作了這個vector( 當然使用者并不知道什么時候操作了vector) . 它用完了這個類以后. 類被釋放掉了. 編譯器很負責的為它生成了一份析構函數(shù)的代碼...... 聽好了.這份代碼并不是在 dll里 ... . 事情于是又和1>里的一樣了.... crash ......(可能還會伴隨著迷茫.....)
對策: 記得dll里每個類,哪怕式構造析構函數(shù)式空的. 也要寫到cpp里去. 什么都不寫也式很糟糕的.....同時,更要把任何和內(nèi)存操作有關的函數(shù)寫到 .cpp 里...
3>
以上兩個問題似乎都是比較容易的-----只要把代碼都寫到cpp里去, 不要用stl容器傳來傳去就可以了.
那么第三個問題就要麻煩的多.
如果你自己寫了一個模板, 這個模板用了stl 容器..........
這個時候你該怎么辦呢?
顯然你無法把和內(nèi)存分配相關的函數(shù)都寫到.cpp里去 . template的代碼都必須放到header file里.....
對策: 解決這個問題的基本做法是做一個stl 內(nèi)存分配器 , 強制把這個模板里和內(nèi)存分配相關的放到一個.cpp里去.這個時候編譯這個cpp就會把內(nèi)存分配代碼固定在一個地方: 要么是dll. 要么是exe里...
模板+動態(tài)鏈接庫的使用問題還很多. 要千萬留心這個陷阱遍地的東西啊
***************************************************************************************************************************
微軟關于這類問題的解釋:
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
總結:
字符串參數(shù)用char*,Vector用char**,
動態(tài)內(nèi)存要牢記誰申請誰釋放的原則。