??xml version="1.0" encoding="utf-8" standalone="yes"?>
#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/hackbuteer1/article/details/7722667
?各种计算Zpȝ构中Q对于字节、字{的存储机制有所不同Q因而引发了计算?通信?域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等{)应该以什么样的顺序进行传送。如果不达成一致的规则Q通信双方无法进行正 的~?译码从而导致通信p|。目前在各种体系的计机中通常采用的字节存储机制主要有两种QBig-Endian和Little-EndianQ下面先从字节序说v?br />一、什么是字节?br />字节序,思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存N?一个字节的数据当然无需谈顺序的问题??/span>其实大部分h在实际的开 发中都很会直接和字节序打交道。唯有在跨^C及网l程序中字节序才是一个应该被考虑的问题?br />
在所有的介绍字节序的文章中都会提到字 节序分ؓ两类QBig-Endian和Little-EndianQ引用标准的Big-Endian和Little-Endian的定义如下:
a) Little-Endian是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端?br />b) Big-Endian是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端?br />c) |络字节序:TCP/IP各层协议字节序定义为Big-EndianQ因此TCP/IP协议中用的字节序通常UC为网l字节序?br />
1.1 什么是?低地址?br />首先我们要知道CE序映像中内存的I间布局情况Q在《C?家编E》中或者《Unix环境高~程》中有关于内存空间布局情况的说明,大致如下图:
----------------------- 最高内存地址 0xffffffff
栈底
?br />栈顶
-----------------------
NULL (I洞)
-----------------------
?br />-----------------------
未初?化的数据
----------------------- l称数据D?br />初始化的数据
-----------------------
?文段(代码D?
----------------------- 最低内存地址 0x00000000
由图可以看出Q再内存分布中,栈是向下增长的,而堆是向上增长的?/span>
以上图ؓ例如果我们在?上分配一个unsigned char buf[4]Q那么这个数l变量在栈上是如何布局的呢Q看下图Q?br />栈底 Q高地址Q?/span>
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
栈顶 Q低地址Q?/strong>
其实Q我们可以自己在~译器里面创Z个数l,然后分别输出数组U每个元素的地址Q来验证一下?br />1.2 什么是?低字?br />现在我们弄清了高/低地址Q接着考虑?低字?/span>。有些文章中UC位字节ؓ最低有效位Q高位字节ؓ最高有效位。如果我们有一?2位无W号整型0x12345678Q那么高位是什么,低位又是什么呢Q?其实很简单?span style="color: #ff0000;">在十q制中我们都说靠左边的是高位Q靠双的是低位Q在其他q制也是如此。就?0x12345678来说Q从高位C位的字节依次?x12?x34?x56?x78?/strong>
?低地址端和?低字节都弄清了。我们再来回?一下Big-Endian和Little-Endian的定义,q用囄说明两种字节序:
以unsigned int value = 0x12345678ZQ分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表CvalueQ?br />Big-Endian: 低地址存放高位Q如下图Q?br />栈底 Q高地址Q?br />---------------
buf[3] (0x78) -- 低位
buf[2] (0x56)
buf[1] (0x34)
buf[0] (0x12) -- 高位
---------------
栈顶 Q低地址Q?br />
Little-Endian: 低地址存放低位Q如下图Q?br />栈底 Q高地址Q?br />---------------
buf[3] (0x12) -- 高位
buf[2] (0x34)
buf[1] (0x56)
buf[0] (0x78) -- 低位
--------------
??Q低地址Q?br />
二、各UEndian
2.1 Big-Endian
计算Zpȝ构中一U描q多字节存储序的术语,在这U机制中最重要字节QMSBQ存攑֜最低端的地址 上。采用这U机制的处理器有IBM3700pd、PDP-10、Mortolora微处理器pd和绝大多数的RISC处理器?br />+----------+
| 0x34 |<-- 0x00000021
+----------+
| 0x12 |<-- 0x00000020
+----------+
?1Q双字节?x1234以Big-Endian的方式存在v始地址0x00000020?br />
在Big-Endian中,对于bit序列 中的序号~排方式如下Q以双字节数0x8B8AZQ:
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 |
+----------------------------------------+
?2QBig-Endian的bit序列~码方式
2.2 Little-Endian
计算Zpȝ构中 一U描q多字节存储序的术语,在这U机制中最不重要字节(LSBQ存攑֜最低端的地址上。采用这U机制的处理器有PDP-11、VAX?strong>Intelpd微处理器和一些网l通信讑֤。该术语除了描述多字节存储顺序外q常常用来描qC个字节中各个比特的排放次序?br />
+----------+
| 0x12 |<-- 0x00000021
+----------+
| 0x34 |<-- 0x00000020
+----------+
?Q双字节?x1234以Little-Endian的方式存在v始地址0x00000020?br /> ?Little-Endian中,对于bit序列中的序号~排和Big-Endian刚好相反Q其方式如下Q以双字节数0x8B8AZQ:
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 |
+-----------------------------------------+
?4QLittle-Endian的bit序列~码方式
注意Q通常我们说的L序(Host OrderQ就是遵循Little-Endian规则。所以当两台L之间要通过TCP/IP协议q行通信的时候就需要调用相应的函数q行L?QLittle-EndianQ和|络序(Big-EndianQ的转换?br />采用 Little-endian模式的CPUҎ(gu)作数的存放方式是从低字节到高字节Q而Big-endian模式Ҏ(gu)作数的存放方式是从高字节C字节?32bit宽的?x12345678在Little-endian模式CPU内存中的存放方式Q假设从地址0x4000开始存放)为:
内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x78 0x56 0x34 0x12
而在Big- endian模式CPU内存中的存放方式则ؓQ?br /> 内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x12 0x34 0x56 0x78
具体的区别如下:
三、Big-Endian和Little-Endian优缺?/strong>
Big-Endian优点Q靠首先提取高位字节Q你L可以q看在偏移位置?的字节来定q个数字?正数q是负数。你不必知道q个数值有多长Q或者你也不必过一些字节来看这个数值是否含有符号位。这个数值是以它们被打印出来的顺序存攄Q所以从二进制到十进制的函数特别有效。因而,对于不同要求的机器,在设计存取方式时׃不同?br />
Little-Endian优点Q提取一个,两个Q四个或者更长字节数据的汇编指o以与其他所有格式相同的方式q行Q首先在偏移地址?的地Ҏ(gu)取最低位的字节,因ؓ地址偏移和字节数是一对一的关p,多重_ֺ的数学函数就相对地容易写了?br />
如果你增加数字的|你可能在左边增加数字Q高位非指数函数需要更多的数字Q?因此Q?l常需要增加两位数字ƈUd存储器里所有Big-endian序的数字,把所有数向右U,q会增加计算机的工作量。不q,使用Little- Endian的存储器中不重要的字节可以存在它原来的位|,新的数可以存在它的右边的高位地址里。这意味着计算Z的某些计可以变得更加简单和快速?br />四、请写一个C函数Q若处理器是Big_endian的,则返?Q若是Little_endian的,则返??br />剖析Q由于联合体union的存N序是所有成员都从低地址开始存放,利用该特性就可以L地获得了CPU对内存采用Little- endianq是Big-endian模式d?br />说明Q?br />1 在c中,联合体(q体)的数据成员都是从低地址开始存放?br />2 若是端模式Q由低地址到高地址c.a存放?x01 00 00 00Qc.b被赋gؓ0x01Q?br /> ————————————————————————————
地址 0x00000000 0x00000001 0x00000002 0x00000003
c.a 01 00 00 00
c.b 01 00
————————————————————————————
3 若是大端模式Q由低地址到高地址c.a存放?x00 00 00 01Qc.b被赋gؓ0x0Q?br /> ————————————————————————————
地址 0x00000000 0x00000001 0x00000002 0x00000003
c.a 00 00 00 01
c.b 00 00
————————————————————————————
4 Ҏ(gu)c.b的值的情况可以判断cpu的模式了?br />
举例Q一?6q制数是 0x11 22 33Q其存放的位|是
地址0x3000 中存?1
地址0x3001 中存?2
地址0x3002 中存?3
qv来就写成地址0x3000-0x3002中存放了数据0x112233
而这U存攑֒表示方式Q正好符合大端?/span>
另外一个比较好理解的写法如下:
或者下面两U写法也是可以的
]]>
]]>
int* ( *( *fun )( int* ) )[10];
q是一个会让初学者感到头晕目眩、感到恐惧的函数指针声明。在熟练掌握C/C++的声明语法之前,不学?fn)一定的规则Q想理解好这cd杂声明是比较困难的?/span>
C/C++所有复杂的声明l构Q都是由各种声明嵌套构成的。如何解d杂指针声明?叛_法则是一个很著名、很有效的方法。不q,叛_法则其实q不?/span>C/C++标准里面的内容,它是?/span>C/C++标准的声明规定中归纳出来的方法?/span>C/C++标准的声明规则,是用来解军_何创建声明的Q而右左法则是用来解决如何辩识一个声明的Q从嵌套的角度看Q两者可以说是一个相反的q程。右左法则的英文原文是这栯的:
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.
q段英文的翻译如下:
叛_法则Q首先从最里面的圆括号看vQ然后往右看Q再往左看。每当遇到圆括号Ӟ应该掉转阅L向。一旦解析完圆括号里面所有的东西Q就跛_圆括受重复这个过E直到整个声明解析完毕?/span>
W者要对这个法则进行一个小的修正Q应该是从未定义的标识符开始阅读,而不是从括号读vQ之所以是未定义的标识W,是因Z个声明里面可能有多个标识W,但未定义的标识符只会有一个?/span>
现在通过一些例子来讨论叛_法则的应用,先从最单的开始,逐步加深Q?/span>
int (*func)(int *p);
首先扑ֈ那个未定义的标识W,是funcQ它的外面有一对圆括号Q而且左边是一?/span>*Pq说?/span>func是一个指针,然后跛_q个圆括P先看双Q也是一个圆括号Q这说明(*func)是一个函敎ͼ?/span>func是一个指向这cd数的指针Q就是一个函数指针,q类函数hint*cd的Ş参,q回值类型是int?/span>
int (*func)(int *p, int (*f)(int*));
func被一Ҏ(gu)号包含,且左Ҏ(gu)一?/span>*P说明func是一个指针,跛_括号Q右边也有个括号Q那?/span>func是一个指向函数的指针Q这cd数具?/span>int *?/span>int (*)(int*)q样的Ş参,q回gؓintcd。再来看一?/span>func的Ş?/span>int (*f)(int*)Q类似前面的解释Q?/span>f也是一个函数指针,指向的函数具?/span>int*cd的Ş参,q回gؓint?/span>
int (*func[5])(int *p);
func双是一?/span>[]q算W,说明func是一个具?/span>5个元素的数组Q?/span>func的左Ҏ(gu)一?/span>*Q说?/span>func的元素是指针Q要注意q里?/span>*不是修饰func的,而是修饰func[5]的,原因?/span>[]q算W优先?/span>*高,func先跟[]l合Q因?/span>*修饰的是func[5]。蟩个括P看右边,也是一对圆括号Q说?/span>func数组的元素是函数cd的指针,它所指向的函数具?/span>int*cd的Ş参,q回值类型ؓint?/span>
int (*(*func)[5])(int *p);
func被一个圆括号包含Q左边又有一?/span>*Q那?/span>func是一个指针,跛_括号Q右Ҏ(gu)一?/span>[]q算W号Q说?/span>func是一个指向数l的指针Q现在往左看Q左Ҏ(gu)一?/span>*P说明q个数组的元素是指针Q再跛_括号Q右边又有一个括P说明q个数组的元素是指向函数的指针。ȝ一下,是Q?/span>func是一个指向数l的指针Q这个数l的元素是函数指针,q些指针指向hint*形参Q返回gؓintcd的函数?/span>
int (*(*func)(int *p))[5];
func是一个函数指针,q类函数hint*cd的Ş参,q回值是指向数组的指针,所指向的数l的元素是具?/span>5?/span>int元素的数l?/span>
要注意有些复杂指针声明是非法的,例如Q?/span>
int func(void) [5];
func是一个返回gؓh5?/span>int元素的数l的函数。但C语言的函数返回g能ؓ数组Q这是因为如果允许函数返回gؓ数组Q那么接收这个数l的内容的东西,也必L一个数l,?/span>C/C++语言的数l名是一个不可修改的左|它不能直接被另一个数l的内容修改Q因此函数返回g能ؓ数组?/span>
int func[5](void);
func是一个具?/span>5个元素的数组Q这个数l的元素都是函数。这也是非法的,因ؓ数组的元素必L对象Q但函数不是对象Q不能作为数l的元素?/span>
实际~程当中Q需要声明一个复杂指针时Q如果把整个声明写成上面所C些Ş式,对可读性带来一定的损害Q应该用typedef来对声明逐层分解Q增强可?/span>性?/span>
typedef是一U声明,但它声明的不是变量,也没有创建新cdQ而是某种cd的别名?span style="margin: 0px; padding: 0px;">typedef有很大的用途,对一个复杂声明进行分解以增强可读性是其作用之一?/span>例如对于声明Q?/span>
int (*(*func)(int *p))[5];
可以q样分解Q?/span>
typedef int (*PARA)[5];
typedef PARA (*func)(int *);
q样容易看得多了?/span>
typedef的另一个作用,是作为基于对象编E的高层抽象手段。在ADT中,它可以用来在C/C++和现实世界的物g间徏立关联,这些物件抽象成C/C++的类型系l。在设计ADT的时候,我们常常声明某个指针的别名,例如Q?/span>
typedef struct node * list;
?/span>ADT的角度看Q这个声明是再自然不q的事情Q可以用list来定义一个列表。但?/span>C/C++语法的角度来看,它其实是不符?/span>C/C++声明语法的逻辑的,它暴力地指针声明符从指针声明器中分d来,q会造成一些异于h们阅M(fn)惯的现象Q考虑下面代码Q?/span>
const struct node *p1;
typedef struct node *list;
const list p2;
p1cd?/span>const struct node*Q那?/span>p2呢?如果你以为就是把list?#8220;代入”p2Q然后得?/span>p2cd也是const struct node*的结果,大错特错了?/span>p2的类型其实是struct node * const p2Q那?/span>const限定的是p2Q不?/span>node。造成q一奇异现象的原因是指针声明器被分割Q标准中规定Q?/span>
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.
指针的声明器由指针声明符*、可选的cd限定?/span>type-qualifier-listopt和标识符Dl成Q这三者在逻辑上是一个整体,构成一个完整的指针声明器。这也是多个变量同列定义时指针声明符必须紧跟标识W的原因Q例如:
int *p, q, *k;
p?/span>k都是指针Q但q不是Q这是因?/span>*p?/span>*k是一个整体指针声明器Q以表示声明的是一个指针。编译器会把指针声明W左边的cd包括光定词作ؓ指针指向的实体的cdQ右边的限定词限定被声明的标识符。但现在typedef struct node *list生生把*从整个指针声明器中分d来,~译器找不到*Q会认ؓconst list p2中的const是限?/span>p2的,正因如此Q?/span>p2的类型是node * const而不?/span>const node*?/span>
虽然typedef struct node* list不符合声明语法的逻辑Q但Ztypedef?/span>ADT中的重要作用以及信息隐藏的要求,我们应该让用戯样?/span>list AQ而不?/span>list *AQ因此在ADT的设计中仍应使用上述typedef语法Q但需要注意其带来的不利媄响?/span>
从实现装饰者模式中思考C++指针和引用的选择
最q在看设计模式的内容Q偶焉手痒写了一?#8220;装饰?#8221;模式的一个实例。该实例来源?/span>风雪涟漪的博客,我对它做了简化。作Z个经典的设计模式Q本wƈ没有太多要说的内宏V但是在我尝试?/span>C++d现这个模式的实例的时候,出现了一些看似无关紧要但是却引h深思的问题?/span>
首先Q我想简单介l一下这个实例的含义。实例的目的是希望通过装饰器类对已有的蛋糕c进行装饰补充,于是按照装饰者模式的设计l构Q有cM?/span>1的设计结构?/span>
?/span>1 装饰者模?/span>
蛋糕cd装饰器类都承于一个公q基类Q该基类声明了一些公共接口。这里简单的使用getName来返回当前蛋p的名称Q而装饰器cd以对该蛋p的名称q行修改补充。具体的蛋糕c都有自q名称Q比?/span>CheeseCakeq回的是“奶a蛋糕”。如果用了装饰器类对该c进行装饰的话,q回的名字就发生了的变化Q比?#8220;装饰了花?/strong>奶a蛋糕”Q这正是装饰器类的功能。实现这个功能的关键在于装饰器公共基c?/span>DecoratorQ它包含了一?/span>Cakecd的成?/span>cake。在定义装饰器的时候我们可以传递给装饰器一个已l徏立好的蛋p对象,比如CheeseCake对象。由?/span>CheeseCake?/span>Cake的子c,因此该对象可以被cake成员记录下来。由于具体的装饰器承于装饰器基c?/span>DecoratorQ因此保护乘?/span>cake可以被看刎ͼ又因饰器本n也是l承?/span>Cake的,因此也拥?/span>getName的接口,q样在装饰器cd?/span>getName调用cake?/span>getName接口q添加额外的操作p完成装饰的目的。另外,装饰器本w也?/span>Cake的子c,因此装饰后的装饰器类对象同时也是一个具体的蛋糕对象Q它可以被再ơ装饎ͼq样装饰器类反映在我们脑里的情境就是一个原本的蛋糕对象外边包裹了一层层装饰器对象?/span>
以上的说明如果还不够清楚的话Q下边展C具体的实现代码。这里就需要考虑cake成员的类型问题,一般用指针类型可能更W合C++的编E习(fn)惯。因Z用对象不仅消耗空_q在每次构造对象的时候进行对象的复制Q这都不是我们愿意看到的。当Ӟ使用引用或许更合理,因ؓ按照q_的经验,很多使用C++指针的地斚w可以用引用代替,有h甚至多用引用少使用指针Q当然我也承?/span>C++引用也有很多好处~Q?/span>。不q,当你d本文或许你就不大q么认ؓ了。首先,我们?/span>Cake*pCake实现q个装饰器类内的成员Q先具体了解一下这个代码的具体内容?/span>
从代码中不难看出E序的输出结构应该是“装饰q花的装饰过q奶a蛋糕”Q事实也的确如此Q从装饰器的使用格式来看FlowerDecorator(&FlowerDecorator(&CheeseCake()))倒也不至于十分麻烦。但是刚才讨Q如果能使用引用代替会许会更“舒服”Q至不用传递参C前还要?/span>&获取一下地址?/span>~
既然如此Q我们把成员修改为引用格式的Q?/span>
修改后的代码看v来的更“眼?#8221;。因用的时候我们不用再写那个看着别扭的取地址q算W了Q然后我们满怀ƣ喜的执行了E序Q输出结果ؓQ?#8220;装饰q花的奶油蛋p?#8221;Q你我的W一反应八成是觉得忘了多修饰一ơ了Q但是我们认真的查代码,发现的确一切都是符合逻辑?#8230;…
上边做了q么多铺垫就是ؓ了引个奇怪的问题Q我其实也被该问题困惑了很久。稍有编E经验的人都会跟t调试这些构造函数的执行q程Q结果发?/span>FlowerDecorator只被执行了一ơ,因此输Z?#8220;装饰q花?#8221;不为奇。但是你我肯定好奇ؓ什么会输Zơ呢Q?/span>
再次再次的检查代码、调试、跟t,或许你会像发现新大陆一样发C一个隐藏的问题Q第二次构?/span>FlowerDecorator时调用的是复制构造函敎ͼ而不是定义好的构造函敎ͼ虽然子类FlowerDecorator?/span>Cake的子c,但是~译器会自动最?jng)_配函数参数类型)Q由于复制构造函数值原模原L(fng)拯Z个对象,所以只能完成一ơ装饰器装饰。非常完的解释Q因此我们可以自己重写复制构造函数来完成我们的装饰功能,q里先忽略原本的对象复制功能了。编译器为我们生成的复制构造函数应该是Q?/span>
而我们应该将参数看作一?/span>Cake对象q行装饰Q因此修改ؓQ?/span>
同样Q由于构造函数初始化了基c,所以基cȝ复制构造也需要重写:
即传递的参数?/span>FlowerDecorator对象?/span>Cake?/span>Decorator不是一个类型,但是~译器或?dng)R认的匚wl承层次最q的cdQ然后我们按照这栯求重写了代码Q执行了E序Q在期待l果的那一ȝ到的?#8220;装饰q花的奶油蛋p?#8221;……或许此时的你都会感到灰心Q但是你q是依然的坚强的按下?/span>F5单步跟踪Q结果你发现拯构造函数ƈ没有被调用!N以上的假N错了吗?我可以确定的告诉读者,我们以上的假N是正的?/span>
最l我也是没有办法Q去StackOverFlow上求助,l合回答者的讨论Q我l于把问题的原因锁定?#8212;—~译器优化!我觉得用一个最单的例子来说明这个问题再合适不q了Q?/span>
q个单的例子l果或许大家都很明白Q但是你亲自试一下就可能要怀疑自q判断能力了,E序输出Q?/span>
是不是有点世界观被颠覆的感觉Q需要声明一下,q个?/span>Visual Studio 2010下的试l果Q因个程序的输出的确和编译器相关Qؓ了确认我?/span>gcc-4.4.3试了该D代码,输出l果为:
看来Q还?/span>gcc优化的比较彻底。因此我们可以得出结论,cMq种无名对象的构造(有名的是按照规矩来的Q,调用多少ơ构造函数要看编译器?#8220;脾气”了。到q里Q不知道你对引用参数的感觉如何?
讨论到这Q或许有和本来要讨论的话题离得太q了。其实ƈ不是Q佛(jng)家说Q?#8220;今日之果皆来自昨日之?#8221;Q一切的一切都是由于我们用了本以为毫无?zhn)늚引用D的!如果使用指针׃可能发生和拷贝构造函数冲H的问题Q也不会D~译器优化的问题Q回视本文刚开始D的例子和该文的主题,或许我们应该清楚有时候的要好好区分一下指针和引用的差别了Q当然本文也是从一个实늚例子中去发现和挖掘这一炏V?br />
原因分析Q?br style="margin: 0px; padding: 0px; " />一句话-----如果MSTLcM用了静态变量(无论是直接还是间接用)Q那么就不要再写执行单元讉K它的代码?nbsp;除非你能够确定两个动态库使用的都是同L(fng)STL实现Q比如都使用VC同一版本的STLQ编译选项也一栗强烈徏议,不要在动态库接口中传递STL容器Q!
STL不一定不能在DLL间传递,但你必须d搞懂它的内部实现Qƈ懂得Z会出问题?br style="margin: 0px; padding: 0px; " />微Y的解释:
http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b172396
微Yl的解决办法Q?br style="margin: 0px; padding: 0px; " />http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b168958
1、微软的解释Q?br style="margin: 0px; padding: 0px; " />大部分C++标准库里提供的类直接或间接地使用了静态变量。由于这些类是通过模板扩展而来的,因此每个可执行映像(通常?dll?exe文gQ就会存在一份只属于自己的、给定类的静态数据成员。当一个需要访问这些静态成员的cL法执行时Q它使用的是“q个Ҏ(gu)的代码当前所在的那䆾可执行映?#8221;里的静态成员变量。由于两份可执行映像各自的静态数据成员ƈ未同步,q个行ؓ可能导致访问违例,或者数据看hg丢失或被破坏了?br style="margin: 0px; padding: 0px; " />
可能不太好懂Q我举个例子Q假如类A<T>有个静态变量m_sQ那么当1.exe使用?.dll中提供的某个A<int>对象Ӟ׃模板扩展机制Q?.exe?.dll中会分别存在自己的一份类静态变量A<int>.m_s?br style="margin: 0px; padding: 0px; " />q样Q假?.exe中从2.dll中取得了一个的cA<int>的实例对象aQ那么当?.exe中直接访问a(chn).m_sӞ其实讉K的是 1.exe中的对应拯Q正情况应该是讉K?.dll中的a.m_sQ。这样就可能D非法讉K、应当改变的数据没有改变、不应改变的数据被错误地更改{异常情形?br style="margin: 0px; padding: 0px; " />
原文Q?br style="margin: 0px; padding: 0px; " />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、保证资源的分配/删除操作对等q处于同一个执行单元;
比如Q可以把q些操作Q包括构?析构函数、某些容器自动扩容{q个需要特别注意}时的内存再分配等Q隐藏到接口函数里面。换句话_量不要直接从dll中输出stl对象Q如果一定要输出Q给它加上一层包装,然后输出q个包装接口而不是原始接口?br style="margin: 0px; padding: 0px; " />
2、保证所有的执行单元使用同样版本的STLq行库?br style="margin: 0px; padding: 0px; " /> 比如Q全部用release库或debug库,否则两个执行单元扩展出来的STLcȝ内存布局可能会不一栗?br style="margin: 0px; padding: 0px; " />
只要C关键是Q?span style="margin: 0px; padding: 0px; ">如果MSTLcM用了静态变量(无论是直接还是间接用)Q那么就不要再写执行单元讉K它的代码?br style="margin: 0px; padding: 0px; " />
解决Ҏ(gu)Q?br style="margin: 0px; padding: 0px; " />1. 一个可以考虑的方?br style="margin: 0px; padding: 0px; " />比如有两个动态库L1和L2QL2需要修改L1中的一个mapQ那么我在L1中设|如下接?br style="margin: 0px; padding: 0px; " />int modify_map(int key, int new_value);
如果需要指?#8220;某一个map”Q则可以考虑实现一U类g句柄的方式,比如可以传递一个DWORD
不过q个DWORD攄是一个地址
那么modify_map可以这样实玎ͼ
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的g首先由L1“告诉”L2:
DWORD get_map_handle();
L2可以q样调用:
DWORD h = get_map_handle();
modify_map(h, 1, 2);
2. 加入一个额外的层,可以解决问题。所以,你需要将你的Map包装在dll内部Q而不是让它出现在接口当中。动态库的接口越单越好,不好M太过复杂的东东是至理名言Q)
在动态连接库开发中要特别注意内存的分配与释N题,E不注意Q极可能造成内存泄漏Q从而访问出错。例如在某DLL中存在这样一D代码: 今天写程序的时候要l一个模块的dll传递一个参敎ͼ׃参数数量是可变的Q因此设计成了vector<string>cdQ但调试q程中发现在exe中的参数传递到dll中的函数后,vector变成I的Q改成传引用cd后,vector竟然变得很大Qƈ且是无意义的参数?/p> 对于q个问题Q两U办法: 1.传递vector指针 2.传递const vector<TYPE>?/p> I其原因Q?/p> 是因为vector在exe和dll之间传递的时候,׃在dll内可能对vector插入数据Q而这D内存是在dll里面分配的,exe无法知道如何释放内存Q从而导致问题。而改成constcd后,~译器便知道dll里不会改变vectorQ从而不会出错?/p> 或者可以说q是"cross-DLL problem."QThis problem crops up when an object is created using new in one dynamically linked library (DLL) but is deleted in a different DLLQ的一U吧?/p> 对于STLQ在DLL中用的时候,往往存在q些问题Q在|络上搜集了下,q些都是要^时用STL的时候注意的?/p> *************************************************************************************************************** 引用http://www.hellocpp.net/Articles/Article/714.aspx 当template 遭遇到dynamic link 时? 很多时候却是一场恶? 2> 3> 模板+动态链接库的用问题还很多. 要千万留心这个陷阱遍地的东西?/p> *************************************************************************************************************************** 微Y关于q类问题的解释: 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 ȝQ?/p> 字符串参数用char*QVector用char**Q?/p>
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::stringQƈ且在其中改变std::string的内容,卛_生了内存的重分配问题,若在EXE中调用该函数会出现内存访问问题?span style="margin: 0px; padding: 0px; color: #ff3333; ">主要是:因ؓDLL和EXE的内存分配方式不同,DLL中的分配的内存不能在EXE中正释放掉?br style="margin: 0px; padding: 0px; " />
解决q一问题的途径如下Q?br style="margin: 0px; padding: 0px; " />一般情况下Q构建DLL必须遵@谁分配就p释放?/span>原则Q例如COM的解x案(利用引用计数Q,对象的创建(QueryInterfaceQ与释放均在COMlg内部完成。在UC 环境下,可以很容易的实现cMҎ(gu)?/p>
在应用STL的情况下Q很难用上q方案来解决Q因此必d辟蹊径,途径有二Q?br style="margin: 0px; padding: 0px; " />1、自己写内存分配器替代STL中的默认分配器?br style="margin: 0px; padding: 0px; " />2、用STLport替代pȝ的标准库?br style="margin: 0px; padding: 0px; " />
其实Q上q问题在VC7及以后版本中Q已得到解决Q注意DLL工程和调用的工程一定要使用多线EDLL库,׃会发生内存访问问题?/p>一个很奇怪的问题QDLL中用std::string作ؓ参数l果出错
q段旉,在工E中一些功能封装成动态库,需要用动态库接口的时?使用?span style="margin: 0px; padding: 0px; ">STL的一些类型作为参?
比方string,vector,list.但是在用接口的时?
当我在用这个库的时?q样写代?
一点一点取掉这个函数的代码.最后就剩下
str="qadasdasdasdsafsafas";
q是出错?
如果Ҏ(gu)很短的字W串,׃会出错误.
在这个时?只能试认ؓ是字W串的空间太?br style="margin: 0px; padding: 0px; " />
最l我修改成这?错误消失?希望错误真的是这个引L(fng)
现在来说说一部分我已l碰到过的问? 问题主要集中在内存分配上.
1>
拿STL来说, 自己写模板的时?很难免就用到stl. stl的代码都在头文g? 那么表示着内存分配的代?只有包含了它的cpp ~译的时候才会被军_是用什么样的内存分配代? 考虑一? 当你声明了一个vector<> . q把q个vector<>交给一?dll里的代码来用. 用完? 在你的程序里被释放了. 那么如果?在dll里往vector里insert了一些东? 那么q个时候insert 发生的内存分配的代码是属于dll? 你不知道q个dll的内存分配是什? 是分配在哪里? 而这个时?释放那促的动作却不在dll?....同时. 你甚x法保证编译dll的那个家伙用的stl版本和你是完全一L(fng)..>
如此说来, E序crash掉是天经C?...
对策: 千万别别把你的stl 容器,模板容器?dll 间传来传?. Cstring也是....
你在dll的某个类里声明了一个vector之类的容? 而没有显式的写这个类的构造和析构函数. 那么问题又来?
你这个类肯定有操作这vector的函? 那么q些函数会让vecoter<>生成代码. q些代码在这个dll里都是一致的. 但是别忘?你没有写析构函数...... 如果q个时? 别h在外面声明了一个这L(fng)c?然后调用q个cȝ函数操作了这个vector( 当然使用者ƈ不知道什么时候操作了vector) . 它用完了q个cM? c被释放掉了. ~译器很负责的ؓ它生成了一份析构函数的代码...... 听好?q䆾代码q不是在 dll?... . 事情于是又和1>里的一样了.... crash ......(可能q会伴随着q.....)
对策: 记得dll里每个类,哪怕式构造析构函数式I的. 也要写到cpp里去. 什么都不写也式很糟p的.....同时,更要把Q何和内存操作有关的函数写?.cpp ?..
以上两个问题g都是比较Ҏ(gu)?----只要把代码都写到cpp里去, 不要用stl容器传来传去可以了.
那么W三个问题就要麻烦的?
如果你自己写了一个模? q个模板用了stl 容器..........
q个时候你该怎么办呢?
昄你无法把和内存分配相关的函数都写?cpp里去 . template的代码都必须攑ֈheader file?....
对策: 解决q个问题的基本做法是做一个stl 内存分配?, 强制把这个模杉K和内存分配相关的攑ֈ一?cpp里去.q个时候编译这个cpp׃把内存分配代码固定在一个地? 要么是dll. 要么是exe?..
]]>
ɫۺϾþþþ|
Ļþ2020
|
þþþùɫAVѿͼƬ|
һƷþð
|
þrоƷƵ|
99þùۺϾƷӰԺ|
Ʒþþþþùţţapp|
ҹƷþþþþ|
ŷ츾XXXXԾþþ|
99þóĻ|
Ʒþþþþù|
18պҹþó|
þþþƷһ|
97þþƷˬ|
þþƵ|
Ʒþ߹ۿ|
þ뾫Ʒһ|
ԭƷ99þþƷ66|
þþƷ|
ŷ˾þۺһ|
91þùƵ|
þþƷһ|
þþþ99ƷƬëƬ|
Ļһþ|
ݺþþþþۺ|
պ뾫Ʒþþò|
þ996ȾƷxxxx|
¶ۺϼ¾þ|
ŷþþþþþ|
þþƷһ|
ľþþƷ|
þûɫƵ|
ƷŮþþ|
Ʒþþþþο|
ݺݾþۺ˲|
Ʒþþþվ
|
Ʒþþþþ|
Ʒþ|
һþƵ|
þþþþһƷ|
ھƷ99þ|