2012年1月28日

【转】V8 Javascript 引擎设计理念

V8 Javascript 引擎设计理念

本文翻译自 Google 的开源 Javascript 引擎 V8 的在线文档。其实我都没有真正翻译过什么东西,本来我的英文就比较一般,中文语言组织也很弱。而且许多文档(比如这篇)基本上如果是对此感兴趣的人,直接阅读英文原文文档肯定都是没有问题的。不过既然突然心血来潮,就试一试吧,能力总是要锻炼才会有的。我自己对 Language VM 比较感兴趣,V8 其实并不是一个 VM ,因为它是直接编译为本地机器码执行的,但是也有不少相通的地方。废话少说,下面是译文。

Netscape Navigator 在 90 在年代中期对 JavaScript 进行了集成,这让网页开发人员对 HTML 页面中诸如 form 、frame 和 image 之类的元素的访问变得非常容易。由此 JavaScript 很快成为了用于定制控件和添加动画的工具,到 90 年代后期的时候,大部分的 JavaScript 脚本仅仅完成像“根据用户的鼠标动作把一幅图换成另一幅图”这样简单的功能。

随着最近 AJAX 技术的兴起,JavaScript 现在已经变成了实现基于 web 的应用程序(例如我们自己的 Gmail)的核心技术。JavaScript 程序从聊聊几行变成数百 KB 的代码。JavaScript 被设计于完成一些特定的任务,虽然 JavaScript 在做这些事情的时候通常都很高效,但是性能已经逐渐成为进一步用 JavaScript 开发复杂的基于 web 的应用程序的瓶颈。

V8 是一个全新的 JavaScript 引擎,它在设计之初就以高效地执行大型的 JavaScript 应用程序为目的。在一些性能测试中,V8 比 Internet Explorer 的 JScript 、Firefox 中的 SpiderMonkey 以及 Safari 中的 JavaScriptCore 要快上数倍。如果你的 web 程序的瓶颈在于 JavaScript 的运行效率,用 V8 代替你现在的 JavaScript 引擎很可能可以提升你的程序的运行效率。具体会有多大的性能提升依赖于程序执行了多少 JavaScript 代码以及这些代码本身的性质。比如,如果你的程序中的函数会被反复执行很多遍的话,性能提升通常会比较大,反过来,如果代码中有很多不同的函数并且都只会被调用一次左右,那么性能提升就不会那么明显了。其中的原因在你读过这份文档余下的部分之后就会明白了。

V8 的性能提升主要来自三个关键部分:

快速属性访问

JavaScript 是一门动态语言,属性可以在运行时添加到或从对象中删除。这意味着对象的属性经常会发生变化。大部分 JavaScript 引擎都使用一个类似于字典的数据结构来存储对象的属性,这样每次访问对象的属性都需要进行一次动态的字典查找来获取属性在内存中的位置。这种实现方式让 JavaScript 中属性的访问比诸如 Java 和 Smalltalk 这样的语言中的成员变量的访问慢了许多。成员变量在内存中的位置离对象的地址的距离是固定的,这个偏移量由编译器在编译的时候根据对象的类的定义决定下来。因此对成员变量的访问只是一个简单的内存读取或写入的操作,通常只需要一条指令即可。

为了减少 JavaScript 中访问属性所花的时间,V8 采用了和动态查找完全不同的技术来实现属性的访问:动态地为对象创建隐藏类。这并不是什么新的想法,基于原型的编程语言 Self 就用 map 来实现了类似的功能(参见 An Efficient Implementation of Self, a Dynamically-Typed Object-Oriented Language Based on Prototypes )。在 V8 里,当一个新的属性被添加到对象中时,对象所对应的隐藏类会随之改变。

下面我们用一个简单的 JavaScript 函数来加以说明:

function Point(x, y) {
this.x = x;
this.y = y;
}

当 new Point(x, y) 执行的时候,一个新的 Point 对象会被创建出来。如果这是 Point 对象第一次被创建,V8 会为它初始化一个隐藏类,不妨称作 C0。因为这个对象还没有定义任何属性,所以这个初始类是一个空类。到这个时候为止,对象 Point 的隐藏类是 C0

map_trans_a

执行函数 Point 中的第一条语句(this.x = x;)会为对象 Point 创建一个新的属性 x。此时,V8 会:

  • 在 C0 的基础上创建另一个隐藏类 C1,并将属性 x 的信息添加到 C1 中:这个属性的值会被存储在距 Point 对象的偏移量为 0 的地方。
  • 在 C0 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 x 之后能够找到 C1 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C1

map_trans_b

执行函数 Point 中的第二条语句(this.y = y;)会添加一个新的属性 y 到对象 Point 中。同理,此时 V8 会:

  • 在 C1 的基础上创建另一个隐藏类 C2,并在 C2 中添加关于属性 y 的信息:这个属性将被存储在内存中离 Point 对象的偏移量为 1 的地方。
  • 在 C1 中添加适当的类转移信息,使得当有另外的以其为隐藏类的对象在添加了属性 y 之后能够找到 C2 作为新的隐藏类。此时对象 Point 的隐藏类被更新为 C2

map_trans_c

咋一看似乎每次添加一个属性都创建一个新的隐藏类非常低效。实际上,利用类转移信息,隐藏类可以被重用。下次创建一个 Point 对象的时候,就可以直接共享由最初那个 Point 对象所创建出来的隐藏类。例如,如果又一个 Point 对象被创建出来了:

  • 一开始 Point 对象没有任何属性,它的隐藏类将会被设置为 C0
  • 当属性 x 被添加到对象中的时候,V8 通过 C0 到 C1 的类转移信息将对象的隐藏类更新为C1 ,并直接将 x 的属性值写入到由 C1 所指定的位置(偏移量 0)。
  • 当属性 y 被添加到对象中的时候,V8 又通过 C1 到 C2 的类转移信息将对象的隐藏类更新为 C2 ,并直接将 y 的属性值写入到由 C2 所指定的位置(偏移量 1)。

尽管 JavaScript 比通常的面向对象的编程语言都要更加动态一些,然而大部分的 JavaScript 程序都会表现出像上述描述的那样的运行时高度结构重用的行为特征来。使用隐藏类主要有两个好处:属性访问不再需要动态字典查找了;为 V8 使用经典的基于类的优化和内联缓存技术创造了条件。关于内联缓存的更多信息可以参考 Efficient Implementation of the Smalltalk-80 System这篇论文。

动态机器码生成

V8 在第一次执行 JavaScript 代码的时候会将其直接编译为本地机器码,而不是使用中间字节码的形式,因此也没有解释器的存在。属性访问由内联缓存代码来完成,这些代码通常会在运行时由 V8 修改为合适的机器指令。

在第一次执行到访问某个对象的属性的代码时,V8 会找出对象当前的隐藏类。同时,V8 会假设在相同代码段里的其他所有对象的属性访问都由这个隐藏类进行描述,并修改相应的内联代码让他们直接使用这个隐藏类。当 V8 预测正确的时候,属性值的存取仅需一条指令即可完成。如果预测失败了,V8 会再次修改内联代码并移除刚才加入的内联优化。

例如,访问一个 Point 对象的 x 属性的代码如下:

point.x

在 V8 中,对应生成的机器码如下:

; ebx = the point object
cmp [ebx, <hidden class offset>], <cached hidden class>
jne <inline cache miss>
mov eax, [ebx, <cached x offset>]

如果对象的隐藏类和缓存的隐藏类不一样,执行会跳转到 V8 运行系统中处理内联缓存预测失败的地方,在那里原来的内联代码会被修改以移除相应的内联缓存优化。如果预测成功了,属性 x的值会被直接读出来。

当有许多对象共享同一个隐藏类的时候,这样的实现方式下属性的访问速度可以接近大多数动态语言。使用内联缓存代码和隐藏类实现属性访问的方式和动态代码生成和优化的方式结合起来,让大部分 JavaScript 代码的运行效率得以大幅提升。

高效的垃圾收集

V8 会自动回收不再被对象使用的内存,这个过程通常被称为“垃圾收集(Garbage Collection)”。为了保证快速的对象分配和缩短由垃圾收集造成的停顿,并杜绝内存碎片,V8 使用了一个 stop-the-world, generational, accurate 的垃圾收集器,换句话说,V8 的垃圾收集器:

  • 在执行垃圾回收的时候会中断程序的执行。
  • 大部分情况下,每个垃圾收集周期只处理整个对象堆的一部分,这让程序中断造成的影响得以减轻。
  • 总是知道内存中所有的对象和指针所在的位置,这避免了非 accurate 的垃圾收集器中普遍存在的由于错误地把对象当作指针而造成的内存溢出的情况。

在 V8 中,对象堆被分成两部分:用于为新创建的对象分配空间的部分和用于存放在垃圾收集周期中生存下来的那些老的对象的部分。如果一个对象在垃圾收集的过程中被移动了,V8 会更新所有指向这个对象的指针到新的地址。

posted @ 2012-01-28 15:47 SeanLin 阅读(9) 评论(0) 编辑

2011年9月19日

android 字体位置信息

字体位置信息:

posted @ 2011-09-19 11:03 SeanLin 阅读(7) 评论(0) 编辑

2011年7月18日

【转】蓝牙协议中HCI层的研究与开发

蓝牙协议中HCI层的研究与开发


刘向阳,沈连丰

(东南大学移动通信国家重点实验室, 南京 210096)

一、HCI在蓝牙软件协议模型位置的分析

蓝牙系统的协议模型如图1所示。从图中可以看出,HCI是位于蓝牙系统的L2CAP(逻辑链路控制与适配协议)层和LMP(链路管理协议)层之间的一层协议。HCI为上层协议提供了进入LM的统一接口和进入基带的统一方式。在HCI的主机(Host)和HCI主机控制器(Host Controller)之间会存在若干传输层,这些传输层是透明的,只需完成传输数据的任务,不必清楚数据的具体格式。目前,蓝牙的SIG规定了四种与硬件连接的物理总线方式:USB、RS232、UART和PC卡。其中通过RS232串口线方式进行连接具有差错校验。

 

HCI层的底层层协议如图2所示。由图可看出,HCI层屏蔽了基带,为协议层的上层提供了进入基带的统一方式。

二、HCI与基带通信方式的研究与分析

蓝牙系统的底层协议通信如图3所示。下面结合图3对蓝牙系统中HCI层与基带间的通信作一些分析研究。

1.通信方式的分析

HCI是通过包的方式来传送数据、命令和事件的,所有在主机和主机控制器之间的通信都以包的形式进行。包括每个命令的返回参数都通过特定的事件包来传输。HCI有数据、命令和事件三种包,其中数据包是双向的,命令包只能从主机发往主机控制器,而事件包始终是主机控制器发向主机的。主机发出的大多数命令包都会触发主机控制器产生相应的事件包作为响应。
命令包分为六种类型:


* 链路控制命令;
* 链路政策和模式命令;
* 主机控制和基带命令;
* 信息命令;
* 状态命令;
* 测试命令。

事件包也可分为三种类型:
* 通用事件,包括命令完成包(Command Complete)和命令状态包(Command Status);
* 测试事件;
* 出错时发生的事件,如产生丢失(Flush Occured)和数据缓冲区溢出(Data Buffer Overflow)。
数据包则可分为ACL和SCO的数据包。包的格式如图4所示。

2.包的参数分析研究
命令包:命令包中的OCF(Opcode Command Field)和OGF(Opcode Group Field)是用于区分命令种的。Parameter Length表示所带参数的长度,以字节数为单位,随后就是所带的参数列表。下面以Inquiry命令为例对HCI的命令包做具体说明。

在Inquiry命令中,OGF=0x01表示此命令属于链路控制命令,同时OCF=0x0001则表示此命令为链路控制命令中的Inquiry命令。OCF与OGF共占2字节,又由于底位字节在前,则它们在命令包为0x0104。在Inquiry 命令中,参数Parameter Length为5。Inquiry命令带3个参数,第一个参数为LAP(low address part), 它将用来产生Baseband中查询命令包的包头中的Access Code。第二个参数为Inquiry_Length,它时表示在Inquiry命令停止前所定义的最大时间,超过此时间,Inquiry命令将终止。第三个参数为NUM_Response,它的值为0X00表示设备响应数不受限制,只为0x00-0xff则表示在Inquiry命令终止前最大的设备响应数。因此,若LAP=0x9e8b00,Inquiry_Length=0x05,NUM_Response=0x05,则协议上层调用Inquiry命令是HCI向基带发的明令包将为:0x01 04 05 00 8b 9e 05 05。

事件包:事件包的Event Code用来区分不同的事件包,Parameter Length表示所带参数的长度,以字节数为单位,随后就是所带的参数列表。以Command Status Event事件包为例对HCI的事件包进行具体说明。

当主机控制器收到主机发来的如上面所提到的Inquiry命令包并开始处理时,它就会向主机发送Command Status Event事件包,此事件包为:0x0f 04 00 0a 01 04。0xOf表示此事件包为Command Status Event事件包,0x04表示此事件包带4字节长度的参数,0x00为此事件包的第一个参数即Status,表示命令包正在处理。0x0a为事件包的第二个参数NUM_HCI_Command_Packets,表示主机最多可在向主机控制器发10个命令包。0x01 04 为第三个参数Command_Opcode, 表示此事件包是对Inquiry命令包的响应。

数据包:ACL和SCO数据包中的Connection Handle即连接句柄是一个12比特的标志符,用于唯一确认两台蓝牙设备间的数据或语音连接,可以看作是两台蓝牙设备间唯一的数据通道的标识。两台设备间只能有一条ACL连接,也就是只有一个ACL的连接句柄,相应L2CAP的信道都是建立在这个连接句柄表示的数据通道上;两台设备间可以有多个SCO的连接,则一对设备间会有多个SCO的连接句柄。连接句柄在两设备连接期间一直存在,不管设备处于什么状态。在ACL数据包中,Flags分为PB Flag和BC Flag,PB Flag为包的界限标志,PB Flag=0x00表示此数据包为上层协议包(如L2CAP包)的起始部分;PB Flag=0x01表示此数据包为上层协议包(如L2CAP包)的后续部分。BC Flag为广播发送的标志,BC Flag=0x00表示无广播发送,只是点对点的发送;BC Flag=0x01表示对所有处于激活状态的从设备进行广播发送,BC Flag=0x02表示对所有的从设备包括处于休眠状态的从设备进行广播发送。ACL和SCO数据包中的Data Total Length 都表示所载荷的数据的长度,以字节位单位。

3.通信过程的研究与分析

当主机与基带之间用命令的方式进行通信时,主机向主机控制器发送命令包。主机控制器完成一个命令,大多数情况下,它会向主机发出一个命令完成事件包(Command Complete Packet),包中携带命令完成的信息。有些命令不会收到命令完成事件,而会收到命令状态事件包(Command Status Packet),当收到该事件则表示主机发出的命令已经被主机控制器接收并开始处理,过一段时间该命令被执行完毕时,主机控制器会向主机发出相应的事件包来通知主机。如果命令参数有误,则会在命令状态事件中给出相应错误码。假如错误出现在一个返回Command Complete事件包的命令中,则此Command Complete事件包不一定含有此命令所定义的所有参数。状态参数作为解释错误原因同时也是第一个返回的参数,总是要返回的。假如紧随状态参数之后是连接句柄或蓝牙的设备地址,则此参数也总是要返回,这样可判别出此Command Complete事件包属于那个实例的一个命令。在这种情况下,事件包中连接句柄或蓝牙的设备地址应与命令包种的相应参数一致。假如错误出现在一个不返回Command Complete事件包的命令中,则事件包包含的所有参数都不一定是有效的。主机必须根据于此命令相联系的事件包中的状态参数来决定它们的有效性。

4.HCI流量控制(Flow Control)的分析研究

HCI的流量控制是为了管理主机和主机控制器中有限的资源并控制数据流量而设计的,由主机管理主机控制器的数据缓存区,主机可动态地调整每个连接句柄的流量。
对于命令包的流量控制,主机在每发一个命令之前都要确定当前能发命令包的数目,当然,在开机和重启动时发命令包可以不用考虑接收情况,直到收到命令完成事件包或命令状态事件包为止。因为在每个命令完成事件包和命令状态事件包中都有Num_HCI_Command_ Packets选项表明当时主机能向主机控制器发送的命令包的数目,而对于每个命令必然会有相应的命令完成事件包和命令状态事件包,主机就能控制命令包不会溢出。

对于数据包的流量控制,一开始,主机调用Read_Buffer_Size命令,该命令返回的两个参数决定了主机能发往主机控制器的ACL和SCO两种数据包的大小的最大值,同时两个附加参数则说明了主机控制器能接收的ACL和SCO数据包总的数目。而每隔一段时间,主机控制器会向主机发Number_Of_Complete_Packets事件,该事件的参数值表明了对每个连接句柄已经处理的数据包的数目(包括正确传输和被丢弃的)。主机根据一开始就知道的总数,减去已经处理的包的数目,则可计算出还能发多少数据包,从而控制数据包的流量。

如有必要,HCI的流量控制也可由主机控制器来实现对主机的控制,可以通过Set_Host_ Controller_To_Host_Flow_Control命令来设置,其控制过程基本与主机控制过程类似,只是命令稍有不同。当主机收到断链确认的事件后,就认为所有传往主机控制器的数据包已经全部被丢弃了,同时主机控制器中的数据缓冲区也被释放了。

三、HCI协议层软件开发

我们在对HCI层进行全面的分析研究之后,提出了HCI协议层软件开发的方案,定出了HCI层提供给协议上层的接口。这些接口给蓝牙协议栈的上层提供了进入BaseBand的统一入口。整个接口按协议站的要求分为八大部分。下面介绍每部分的接口。整个软件层采用传递消息加函数调用相结合的机制来实现,即上层对HCI层接口的调用采用函数调用的机制,HCI对上层的通信采用传递消息的方式。

1.接口分类及举例说明

(1)开始命令

此命令接口是主机向HCI注册及并启动HCI。
如启动HCI的函数接口为HCI_ReqStart(),HCI在启动后发向上层的消息接口为HCI_START_CNF()。命令执行过程如图5所示。

(2)链路控制命令

链路控制命令是允许主机控制器控制与其他蓝牙设备的连接。在链路控制命令运行时, LM 控制蓝牙微微网与分布网的建立与维持。这些命令指示LM创建及修改与远端蓝牙设备的连接链路,查询范围内的其他蓝牙设备,及其他链路管理协议命令。

以查找并发现周围设备为例,HCI层为上层提供了函数接口HCI_ReqInquiry,消息接口为HCI_INQUIRY_RESULT_EVT和HCI_INQUIRY_CNF。命令执行过程如图6所示。

主机首先调用HCI的HCI_ReqInquiry函数开始查询过程,在此过程中,如有蓝牙响应此查询,则会产生一HCI_INQUIRY_RESULT_EVT事件通知主机。在此次查询过程结束时,会产生HCI_INQUIRY_CNF这条消息通知主机,参数NrofResponse表示在此次查询过程所响应的蓝牙设备数。 

(3)链路政策命令

链路政策命令提供了一种影响LM怎样管理微微网的方法。当链路政策命令运行时,LM仍然以可调整的参数控制微微网及分布网的建立和维持。这些链路政策命令调整LM的行为,从尔导致与远端蓝牙设备的链路层连接的改变。

已建立服务质量为例,HCI层为上层提供了函数接口HCI_ReqQoSSetup,消息接口为HCI_QOS_SETUP_EVT和HCI_QOS_SETUP_CNF,HCI_QOS_SETUP_CNF_NEG。命令执行过程如图7所示。

主机首先调用HCI_ReqQosSetup请求建立Qos。当Qos建立成HCI_QOS_SETUP_CNF 消息被送往发起端,同时一个事件消息送往远端设备。当Qos建立失败时,HCI_QOS_SETUP_CNF_NEG被送往发起端。

(4)主机控制器及基带命令

主机控制器及基带命令被用来改变与建立诸如声音设置,认证模式,加密模式的连接相联系的LM的操作方式。

已读取主机控制器所存储的Link Key为例,HCI层为上层提供了函数接口HCI_ReqReadLinkKey,消息接口为HCI_READ_LINK_KEY_RRESULT_EVT和HCI_READ_LINK_KEY_ CNF。命令执行过程如图8所示。

(5)信息命令

这些信息命令的参数是由蓝牙硬件制造商确定的。它们提供了关于蓝牙设备及设备的主机控制器,链路管理器及基带的信息。主机设备不能更改这些参数。

HCI层为上层提供了函数接口为:
HCI_ReqCountryCode HCI_ReqLocalAddress
HCI_ReqReadLocalFeatures HCI_ReqReadLocalVersion
HCI_ReqReadBD_ADDR
HCI层提供的消息接口为: 
HCI_COUNTRY_CODE_CNF HCI_COUNTRY_CODE_CNF_NEG
HCI_LOCAL_ADDRESS_CNF HCI_LOCAL_ADDRESS_CNF_NEG
HCI_READ_LOCAL_FEATURES_CNF HCI_READ_LOCAL_FEATURES_CNF_NEG
HCI_READ_LOCAL_VERSION_CNF HCI_READ_LOCAL_VERSION_CNF_NEG
HCI_READ_BD_ADDR_CNF HCI_READ_BD_ADDR_CNF_NEG

(6)状态命令

状态命令提供了目前HCI,LM,及BB的状态消息。这些状态参数不能被主机改变,除了一些参数可以被重置。

HCI层为上层提供了函数接口为:
HCI_ReqGetLinkQuality HCI_ReqReadFailedCounter
HCI_ReqResetFailedCounter HCI_ReqRssi
HCI层提供的消息接口为:
HCI_GET_LINK_QUALITY_CNF HCI_GET_LINK_QUALITY_CNF_NEG HCI_READ_FAILED_COUNTER_CNF HCI_READ_FAILED_COUNTER_CNF_NEG
HCI_RESET_FAILED_COUNTER_CNF HCI_RESET_FAILED_COUNTER_CNF_NEG
HCI_RSSI_CNF HCI_RSSI_CNF_NEG

(7)测试命令

测试命令能够测试蓝牙硬件各种功能,并蔚蓝牙设备的测试提供不同的测试条件。

HCI层为上层提供了函数接口为:
HCI_ReqEnableDutMode HCI_ReqReadLoopbackMode
HCI_ReqWriteLoopbackMode
HCI层提供的消息接口为:
HCI_ENABLE_DUT_MODE_CNF HCI_ENABLE_DUT_MODE_CNF_NEG
HCI_READ_LOOPBACK_MODE_CNF HCI_READ_LOOPBACK_MODE_CNF_NEG
HCI_WRITE_LOOPBACK_MODE_CNF HCI_WRITE_LOOPBACK_MODE_CNF_NEG

(8)数据传输命令:

这些命令为蓝牙设备之间传输数据提供了所需要的接口。如分配所需内存的接口HCI_DataAlloc,传输数据的接口HCI_DataSend,提取数据的接口HCI_DataExtract。图9说明了在蓝牙系统中传输数据时对这些接口的使用。

四、结论

HCI为为蓝牙协议层的上层提供了进入基带的统一接口。经过测试,所开发的接口能将上层的数据流匹配到基带,使基带能对之进行处理,并产生相应的事件。

posted @ 2011-07-18 17:16 SeanLin 阅读(216) 评论(0) 编辑

【转】蓝牙协议的命令和事件

蓝牙协议的命令和事件
(廖铮 2001年06月18日 16:21)

命令

下表列出蓝牙协议中从L2CAP到物理层的命令和事件映射。希望这个列表可以帮助你分析蓝牙协议中从协议高层发给较低层次的控制命令和消息命令。我们还会通过讲解一个示例来具体地说明协议高层收到连接请求时的情况。

L2CAP

HCI

LMP/BASEBAND

连接和不连接

L2CAP_ConnectReq
L2CAP_ConnectRsp
L2CAP_ConnectRspPnd
L2CAP_ConnectRspNeg
LP_ConnectReq
LP_ConnectRsp
LP_ConnectRspNeg
LP_ConnectCfm
LP_ConnectCfmNeg
LP_ConnectInd
L2CA_ConnectInd
L2CA_ConnectCfm
L2CA_ConnectCfmNeg
L2CA_ConnectPnd
L2CA_ConnectReq
L2CA_ConnectRsp
L2CA_ConnectRspNeg

HCI_Create_Connection
HCI_Accept_Connection_Request
HCI_Reject_Connection_Request
HCI_Add_SCO_Connection
HCI_Read_Connection_Accept_Timeout
HCI_Write_Connection_Accept_Timeout

LMP_host_connection_req
LMP_setup_complete
LMP_SCO_link_req

L2CAP_DisconnectReq
L2CAP_DisconnectRsp
LP_DisconnectInd
L2CA_DisconnectReq
L2CA_DisconnectRsp
L2CA_DisconnectInd
L2CA_DisconnectCfm

HCI_Disconnect

LMP_detach
LMP_remove_SCO_link_req

安全

L2CAP层不安全

HCI_Authentication_Requested
HCI_Set_Connection_Encryption
HCI_Change_Connection_Link_Key
HCI_Master_Link_Key
HCI_PIN_Code_Request_Reply
HCI_PIN_Code_Request_Negative_Reply
HCI_Link_Key_Request_Reply
HCI_Link_Key_Request_Negative_Reply
HCI_Read_Pin_Type
HCI_Write_Pin_Type
HCI_Create_New_Unit_Key
HCI_Read_Stored_Link_Key
HCI_Write_Stored_Link_Key
HCI_Delete_Stored_Link_Key
HCI_Read_Authentication_Enable
HCI_Write_Authentication_Enable
HCI_Read_Encryption_Mode
HCI_Write_Encryption_Mode

LMP_au_rand
LMP_in_rand
LMP_temp_rand
LMP_temp_key
LMP_comb_key
LMP_unit_key
LMP_sres
LMP_start_encryption_req
LMP_stop_encryption_req
LMP_encryption_mode_req
LMP_encryption_key_size_req
LMP_use_semi_permanent_key

模式处理

L2CAP层不进行模式处理

HCI_Hold_Mode
HCI_Sniff_Mode
HCI_Exit_Sniff_Mode
HCI_Park_Mode
HCI_Exit_Park_Mode
HCI_Read_Hold_Mode_Activity
HCI_Write_Hold_Mode_Activity

LMP_hold
LMP_hold_req
LMP_sniff
LMP_sniff_req
LMP_unsniff_req
LMP_park_req
LMP_park
LMP_set_broadcast_scan_window
LMP_modify_beacon
LMP_unpark_PM_ADDR_req
LMP_unpark_BD_ADDR_req

连接子状态

L2CAP不进行连接子状态处理

HCI_Inquiry
HCI_Inquiry_Cancel
HCI_Periodic_Inquiry_Mode
HCI_Exit_Periodic_Inquiry_Mode
HCI_Read_Page_Timeout
HCI_Write_Page_Timeout
HCI_Read_Scan_Enable
HCI_Write_Scan_Enable
HCI_Read_Page_Scan_Activity
HCI_Write_Page_Scan_Activity
HCI_Read_Inquiry_Scan_Activity
HCI_Write_Inquiry_Scan_Activity
HCI_Read_Page_Scan_Mode
HCI_Write_Page_Scan_Mode
HCI_Read_Page_Scan_Period_Mode
HCI_Write_Page_Scan_Period_Mode
HCI_Read_Num_Broadcast_Retransmissions
HCI_Write_Num_Broadcast_Retransmissions

LMP_page_mode_req
LMP_page_scan_mode_req

QoS和流控

LP_QoSReq
LP_QoSCfm
LP_QoSCfmNeg
LP_QoSViolationInd
L2CA_QoSViolationInd

HCI_QoS_Setup
HCI_Read_SCO_Flow_Control_Enable
HCI_Write_SCO_Flow_Control_Enable
HCI_Set_Host_Controller_To_Host_Flow_Control
HCI_Get_Link_Quality

LMP_quality_of_service
LMP_quality_of_service_req
LMP_auto_rate
LMP_preferred_rate

其他

L2CAP_Data
L2CAP_ConfigReq
L2CAP_ConfigRsp
L2CAP_ConfigRspNeg
L2CA_ConfigReq
L2CA_ConfigRsp
L2CA_ConfigRspNeg
L2CA_ConfigInd
L2CA_ConfigCfm
L2CA_ConfigCfmNeg
L2CA_TimeOutInd
L2CA_DataRead
L2CA_DataWrite

HCI_Change_Connection_Packet_Type
HCI_Remote_Name_Requested
HCI_Read_Remote_Supported_Features
HCI_Read_Remote_Version_Information
HCI_Read_Local_Version_Information
HCI_Local_Supported_Features
HCI_Read_Clock_Offset
HCI_Role_Discovery
HCI_Switch_Role
HCI_Read_Link_Policy_Settings
HCI_Write_Link_Policy_Settings
HCI_Read_Buffer_Size
HCI_Host_Buffer_Size
HCI_Read_Country_Code
HCI_BD_ADDR
HCI_Read_Current_IAC_LAP
HCI_Write_Current_IAC_LAP
HCI_Read_Number_Of_Supported_IAC
HCI_Read_Class_Of_Device
HCI_Write_Class_Of_Device
HCI_Read_Voice_Setting
HCI_Write_Voice_Setting
HCI_Read_Automatic_Flush_Timeout
HCI_Write_Automatic_Flush_Timeout
HCI_Read_Supervision_Timeout
HCI_Read_Link_Supervision_Timeout
HCI_Write_Link_Supervision_Timeout
HCI_Read_Transmit_Power_Level
HCI_Change_Local_Name
HCI_Read_Local_Name
HCI_Set_Event_Mask
HCI_Set_Event_Filter
HCI_Flush
HCI_Reset
HCI_Host_Number_Of_Completed_Packets

LMP_name_req
LMP_name_res
LMP_accpted
LMP_not_accepted
LMP_switch_req
LMP_version_req
LMP_version_res
LMP_clkoffset_req
LMP_clkoffset_res
LMP_timing_accuracy_req
LMP_timing_accuracy_res
LMP_max_power
LMP_min_power
LMP_incr_power_req
LMP_decr_power_req
LMP_slot_offset
LMP_max_slot
LMP_max_slot_req
LMP_features_req
LMP_features_res

 事件

HCI事件

说明

Inquiry Complete Event

表示查询已经完成。

Inquiry Result Event

表示某台蓝牙设备或者多台蓝牙设备在当前查询过程中已经做出响应。

Connection Complete Event

向形成连接的主机双方指示新连接已经建立。

Connection Request Event

表示新入站连接正在建立过程中。

Disconnection Complete Event

在连接被终止后触发。

Authentication Complete Event

当指定连接的认证过程完成后触发。

Remote Name Request Complete Event

表示远端名称请求已经完成。

Encryption Change Event

表示连接句柄(Connection_Handle)的加密变更已经完成。

Change Connection Link Key Complete Event

表示连接句柄的链路密钥(Link Key)变更已经完成。

Master Link Key Complete Event

表示蓝牙主机方的临时或者半永久链路密钥的变更已经完成。

Read Remote Supported Features Complete Event

表示获得远端蓝牙设备所支持特性的链路管理器(Link Manager)过程已经完成。

Read Remote Version Information Complete Event

表示获得远端蓝牙设备版本信息的链路管理器(Link Manager)过程已经完成。

QoS Setup Complete Event

表示设置远端蓝牙设备QoS的链路管理器过程已经完成。

Command Complete Event

主机控制器(Host Controller)使用该事件传递命令的返回状态。

Command Status Event

表示命令已经收到,主机控制器目前正在执行该命令下达的任务。

Hardware Error Event

表示蓝牙设备的某种类型硬件出现故障。

Flush Occurred Event

表示对指定的连接句柄当前被传输的用户数据已经取消。

Role Change Event

表示和特定连接相关联的当前蓝牙设备的角色已经改变。

Number Of Completed Packets Event

主机控制器使用该事件向主机表示在前一个Number Of Completed Packets 之后到目前每个Connection Handle 所完成的HCI 数据分组数量。

Mode Change Event

表示关联连接句柄的设备在Active、Hold、Sniff和Park模式之间发生了变更。

Return Link Keys Event

用于返回存储的链路秘钥。

PIN Code Request Event

表示需要PIN码来为某个连接创建新链路秘钥。

Link Key Request Event

表示以和BD_ADDR指定的设备连接需要一个链路密钥。

Link Key Notification Event

向主机表示和BD_ADDR指定的设备连接所需要的新链路密钥已经创建。

Loopback Command Event

回送主机发送给主机控制器的大多数命令。

Data Buffer Overflow Event

表示主机控制器的数据缓冲已经溢出。

Max Slots Change Event

在LMP_Max_Slots参数改变的时候通知主机。

Read Clock Offset Complete Event

表示获得Clock Offset 信息的LM过程已经完成。

Connection Packet Type Changed Event

表示改变指定连接句柄数据包类型的LM过程已经完成。

QoS Violation Event

表示LM不能为当前的连接句柄提供所需的QoS。

Page Scan Mode Change Event

表示已经连接的、具有指定连接句柄的远端蓝牙设备已经成功改变其寻呼扫描模式(Page_Scan_Mode)。

Page Scan Repetition Mode Change Event

表示已经连接的、具有指定连接句柄的远端蓝牙设备已经成功地改变Page_Scan_Repetition_Mode(SR)。

posted @ 2011-07-18 17:04 SeanLin 阅读(220) 评论(0) 编辑

2011年5月24日

【转】类的static成员函数和const成员函数

类的static成员函数和const成员函数

static成员的特性 

  static成员函数没有this指针。

  static成员函数不能被声明为const。

  static成员函数也不能被声明为虚函数。

  static成员函数可以直接访问所属类的static成员,但不能直接使用非static成员。

  static数据成员的类型,可以是该成员所属的类类型。非static成员被限定声明为其自身类对象的指针或引用。 

  static数据成员可以作为成员函数的默认实参。非static数据成员,不能用作成员函数的默认实参,因为它的值不能独立于所属对象而使用。分析:静态成员函数如果未初始化,系统自动会给它初始化为某个缺省值(如int初始化为0, 指针初始化为NULL等)。 所以一旦定义,其值已确定,可以作为类成员函数的缺省参数。

   当在类的外部定义static成员时,无须重复指定static关键字。static关键字只能用于类定义体内部的声明中,定义不能标示为static。

 

  static数据成员必须在类定义体的外部定义正好一次。不像普通的数据成员,static成员不是通过类构造函数进行初始化,而是应该在定义时初始化。一般将static数据成员的定义放在包含类的非内联成员函数定义的文件中。 

  一般的讲,static数据成员和普通数据成员一样,不能在类的定义体中初始化。static数据成员一般在定义时候初始化。这个规则的一个例外是,只要初始化式是一个常量表达式,const static 数据成员就可以在类的定义体中初始化(Perimer上只写了const static 整形,经过测试发现string也可以):

const static成员
1 class Account {
2 public:
3     static double rate() { return interestRate; }
4     static void rate(double);        // sets a new rate
5 private:
6     static const int period = 30;    // interest posted every 30 days
7     double daily_tbl[period];        // ok: period is constant expression
8 };

 

 Primer P400


static数据成员、成员函数的访问

   访问类的static成员,既可以通过对象来访问,也可通过类直接访问。

   类成员函数(无论是static成员函数或非static成员函数)都可以直接访问static数据成员。访问方式有两种:object.static_var 或   classname::static_var;object.static_func() 或   classname::static_func()。

 

const成员函数

  不能改变对象的普通数据成员,可以改变是static和mutable的数据成员。

 

posted @ 2011-05-24 10:06 SeanLin 阅读(62) 评论(0) 编辑

2011年5月18日

【转】Testing和Instrumentation

Android提供了一系列强大的测试工具,它针对Android的环境,扩展了业内标准的JUnit测试框架。尽管你可以使用JUnit测试Android工程,但Android工具允许你为应用程序的各个方面进行更为复杂的测试,包括单元层面及框架层面。

Android测试环境的主要特征有:

l   可以访问Android系统对象。

l   框架可以控制和测试应用程序。

l   Android系统常用对象的模拟版本。

l   运行单个 suite的工具,带或不带

l   支持以Eclipse的ADT插件和命令行方式管理工程。

这篇文章是对Android测试环境和测试方法的简要介绍,并假设你已经拥有一定的Android应用程序编程及JUnit测试的经验。

概要

Android测试环境的核心是一个框架,在这个框架下,你的测试应用程序可以精确控制应用程序。使用,你可以在主程序启动之前,创建模拟的系统对象,如Context;控制应用程序的多个生命周期;发送UI事件给应用程序;在执行期间检查程序状态。框架通过将主程序和测试程序运行在同一个进程来实现这些功能。

通过在测试工程的manifest文件中添加<>元素来指定要测试的应用程序。这个元素的特性指明了要测试的应用程序包名,以及告诉Android如何运行测试程序。在Inustrumentation TestRunner章节有更多的细节描述。

下面的图片概要的描述了Android的测试环境:

在Android中,测试程序也是Android程序,因此,它和被测试程序的书写方式有很多相同的地方。SDK工具能帮助你同时创建主程序工程及它的测试工程。你可以通过Eclipse的ADT插件或者命令行来运行Android测试。Eclipse的ADT提供了大量的工具来创建测试用例,运行以及查看结果。

Testing API

Android提供了基于JUnit测试框架的测试API来书写测试用例和测试程序。另外,Android还提供了强大的框架,允许测试用例访问程序的状态及运行时对象。

下面的章节描述了Android中可利用的主要测试API。

JUnit TestCase

继承自JUnit的TestCase,不能使用框架。但这些类包含访问系统对象(如Context)的方法。使用Context,你可以浏览资源,文件,数据库等等。基类是AndroidTestCase,一般常见的是它的子类,和特定组件关联。

子类有:

l   ApplicationTestCase——测试整个应用程序的类。它允许你注入一个模拟的Context到应用程序中,在应用程序启动之前初始化测试参数,并在应用程序结束之后销毁之前检查应用程序。

l   ProviderTestCase2——测试单个ContentProvider的类。因为它要求使用MockContentResolver,并注入一个IsolatedContext,因此Provider的测试是与OS孤立的。

l   ServiceTestCase——测试单个Service的类。你可以注入一个模拟的Context或模拟的Application(或者两者),或者让Android为你提供Context和MockApplication。

TestCase

继承自JUnit TestCase类,并可以使用框架,用于测试Activity。使用,Android可以向程序发送事件来自动进行UI测试,并可以精确控制Activity的启动,监测Activity生命周期的状态。

基类是InstrumentationTestCase。它的所有子类都能发送按键或触摸事件给UI。子类还可以注入一个模拟的Intent。

子类有:

l   ActivityTestCase——Activity测试类的基类。

l   SingleLaunchActivityTestCase——测试单个Activity的类。它能触发一次setup()和tearDown(),而不是每个方法调用时都触发。如果你的测试方法都是针对同一个Activity的话,那就使用它吧。

l   SyncBaseInstrumentation——测试Content Provider同步性的类。它使用在启动测试同步性之前取消已经存在的同步对象。

l   ActivityUnitTestCase——对单个Activity进行单一测试的类。使用它,你可以注入模拟的Context或Application,或者两者。它用于对Activity进行单元测试。

不同于其它的类,这个测试类不能注入模拟的Intent。

l   ActivityInstrumentationTestCase2——在正常的系统环境中测试单个Activity的类。你不能注入一个模拟的Context,但你可以注入一个模拟的Intent。另外,你还可以在UI线程(应用程序的主线程)运行测试方法,并且可以给应用程序UI发送按键及触摸事件。

Assert

Android还继承了JUnit的Assert类,其中,有两个子类,MoreAsserts和ViewAsserts:

l   MoreAsserts类包含更多强大的断言方法,如assertContainsRegex(String, String),可以作正则表达式的匹配。

l   ViewAsserts类包含关于Android View的有用断言方法,如assertHasScreenCoordinates(View, View, int, int),可以测试View在可视区域的特定X、Y位置。这些Assert简化了UI中几何图形和对齐方式的测试。

Mock对象类

Android有一些类可以方便的创建模拟的系统对象,如Application,Context,Content Resolver和Resource。Android还在一些测试类中提供了一些方法来创建模拟的Intent。因为这些模拟的对象比实际对象更容易使用,因此,使用它们能简化依赖注入。你可以在android.和android..mock中找到这些类。

它们是:

l   IsolatedContext——模拟一个Context,这样应用程序可以孤立运行。与此同时,还有大量的代码帮助我们完成与Context的通信。这个类在单元测试时很有用。

l   RenamingDelegatingContext——当修改默认的文件和数据库名时,可以委托大多数的函数到一个存在的、常规的Context上。使用这个类来测试文件和数据库与正常的系统Context之间的操作。

l   MockApplication,MockContentResolver,MockContext,MockDialogInterface,MockPackageManager,MockResources——创建模拟的系统对象的类。它们只暴露那些对对象的管理有用的方法。这些方法的默认实现只是抛出异常。你需要继承这些类并重写这些方法。

TestRunner

Android提供了自定义的运行测试用例的类,叫做InstrumentationTestRunner。这个类控制应用程序处于测试环境中,在同一个进程中运行测试程序和主程序,并且将测试结果输出到合适的地方。IntrumentationTestRunner在运行时对整个测试环境的控制能力的关键是使用。注意,如果你的测试类不使用的话,你也可以使用这个TestRunner。

当你运行一个测试程序时,首先会运行一个系统工具叫做Activity Manager。Activity Manager使用框架来启动和控制TestRunner,这个TestRunner反过来又使用Intrumentation来关闭任何主程序的实例,然后启动测试程序及主程序(同一个进程中)。这就能确保测试程序与主程序间的直接交互。

在测试环境中工作

对Android程序的测试都包含在一个测试程序里,它本身也是一个Android应用程序。测试程序以单独的Android工程存在,与正常的Android程序有着相同的文件和文件夹。测试工程通过在manifest文件中指定要测试的应用程序。

每个测试程序包含一个或多个针对特定类型组件的测试用例。测试用例里定义了测试应用程序某些部分的测试方法。当你运行测试程序,Android会在相同进程里加载主程序,然后触发每个测试用例里的测试方法。

测试工程

为了开始对一个Android程序测试,你需要使用Android工具创建一个测试工程。工具会创建工程文件夹、文件和所需的子文件夹。工具还会创建一个manifest文件,指定被测试的应用程序。

测试用例

一个测试程序包含一个或多个测试用例,它们都继承自Android TestCase类。选择一个测试用例类取决于你要测试的Android组件的类型以及你要做什么样的测试。一个测试程序可以测试不同的组件,但每个测试用例类设计时只能测试单一类型的组件。

一些Android组件有多个关联的测试用例类。在这种情况下,在可选择的类间,你需要判断你要进行的测试类型。例如,对于Activity来说,你有两个选择,ActivityInstrumentationTestCase2和ActivityUnitTestCase。

ActivityInstrumentationTestCase2设计用于进行一些功能性的测试,因此,它在一个正常的系统环境中测试Activity。你可以注入模拟的Intent,但不能是模拟的Context。一般来说,你不能模拟Activity间的依赖关系。

相比而言,ActivityUnitTestCase设计用于单元测试,因此,它在一个孤立的系统环境中测试Activity。换句话说,当你使用这个测试类时,Activity不能与其它Activity交互。

作为一个经验法则,如果你想测试Activity与Android的交互的话,使用ActivityInstrumentationTestCase2。如果你想对一个Activity做回归测试的话,使用ActivityUnitTestCase。

测试方法

每个测试用例类提供了可以建立测试环境和控制应用程序的方法。例如,所有的测试用例类都提供了JUnit的setUp()方法来搭建测试环境。另外,你可以添加方法来定义单独的测试。当你运行测试程序时,每个添加的方法都会运行一次。如果你重写了setUp()方法,它会在每个方法运行前运行。相似的,tearDown()方法会在每个方法之后运行。

测试用例类提供了大量的对组件启动和停止控制的方法。由于这个原因,在运行测试之前,你需要明确告诉Android启动一个组件。例如,你可以使用getActivity()来启动一个Activity。在整个测试用例期间,你只能调用这个方法一次,或者每个测试方法一次。甚至你可以在单个测试方法中,调用它的finishing()来销毁Activity,然后再调用getActivity()重新启动一个。

运行测试并查看结果

编译完测试工程后,你就可以使用系统工具Activity Manager来运行测试程序。你给Activity Manager提供了TestRunner的名(一般是InstrumentationTestRunner,在程序中指定);名包括被测试程序的包名和TestRunner的名。Activity Manager加载并启动你的测试程序,杀死主程序的任何实例,然后在测试程序的同一个进程里加载主程序,然后传递测试程序的第一个测试用例。这个时候,TestRunner会接管这些测试用例,运行里面的每个测试方法,直到所有的方法运行结束。

如果你使用Eclipse,结果会在JUnit的面板中显示。如果你使用命令行,将输出到STDOUT上。

测试什么?

除了一些功能测试外,这里还有一些你应该考虑测试的内容:

l   Activity生命周期事件:你应该测试Activity处理生命周期事件的正确性。例如,一个Activity应该在pause或destroy事件时保存它的状态。记住一点的是屏幕方向的改变也会引发当前Activity销毁,因此,你需要测试这种偶然情况确保不会丢失应用程序状态。

l   数据库操作:你应该确保数据库操作能正确处理应用程序状态的变化。使用android..mock中的模拟对象。

l   屏幕大小和分辨率:在发布程序之前,确保在所有要运行的屏幕大小和分辨率上测试通过。你可以使用AVD来测试,或者使用真实的目标设备进行测试。

附加:UI测试

接下来的章节为应用程序UI的测试提供了一些提示,特别是帮助你在UI线程里处理动作,触屏和按键事件,和锁屏。

UI线程中测试

Activity运行在程序的UI线程里。一旦UI初始化后,例如在Activity的onCreate()方法后,所有与UI的交互都必须运行在UI线程里。当你正常运行程序时,它有权限可以访问这个线程,并且不会出现什么特别的事情。

当你运行测试程序时,这一点发生了变化。在带有的类里,你可以触发方法在UI线程里运行。其它的测试用例类不允许这么做。为了一个完整的测试方法都在UI线程里运行,你可以使用@UIThreadTest来声明线程。注意,这将会在UI线程里运行方法里所有的语句。不与UI交互的方法不允许这么做;例如,你不能触发.waitForIdleSync()。

如果让方法中的一部分代码运行在UI线程的话,创建一个匿名的Runnable对象,把代码放到run()方法中,然后把这个对象传递给appActivity.runOnUiThread(),在这里,appActivity就是你要测试的app对象。

例如,下面的代码实例化了一个要测试的Activity,为Spinner请求焦点,然后发送一个按键给它。注意:waitForIdleSync和sendKeys不允许在UI线程里运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private MyActivity mActivity; // MyActivity is the class name of the app under test 
  private Spinner mSpinner; 
 
  ... 
 
  protected void setUp() throws Exception { 
      super.setUp(); 
      mInstrumentation = getInstrumentation(); 
 
      mActivity = getActivity(); // get a references to the app under test 
 
      /* 
       * Get a reference to the main widget of the app under test, a Spinner 
       */ 
      mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01); 
 
  ... 
 
  public void aTest() { 
      /* 
       * request focus for the Spinner, so that the test can send key events to it 
       * This request must be run on the UI thread. To do this, use the runOnUiThread method 
       * and pass it a Runnable that contains a call to requestFocus on the Spinner. 
       */ 
      mActivity.runOnUiThread(new Runnable() { 
          public void run() { 
              mSpinner.requestFocus(); 
          } 
      }); 
 
      mInstrumentation.waitForIdleSync(); 
 
      this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);

关闭触屏模式

为了控制从测试程序中发送给模拟器或设备的按键事件,你必须关闭触屏模式。如果你不这么做,按键事件将被忽略。

关闭触摸模式,你需要在调用getActivity()启动Activity之前调用ActivityInstrumentationTestCase2.setActivityTouchMode(false)。你必须在非UI线程中运行这个调用。基于这个原因,你不能在声明有@UIThread的测试方法调用。可以在setUp()中调用。

模拟器或设备的解锁

你可能已经发现,如果模拟器或设备的键盘保护模式使得HOME画面不可用时,UI测试不能正常工作。这是因为应用程序不能接收sendKeys()的事件。避免这种情况最好的方式是在启动模拟器或设备时关闭键盘保护模式。

你还可以显式地关闭键盘保护。这需要在manifest文件中添加一个权限,然后就能在程序中关闭键盘保护。注意,你必须在发布程序之前移除这个,或者在发布的程序中禁用这个功能。

元素下添加。为了关闭键盘保护,在你测试的Activity的onCreate()方法中添加以下代码:

1
2
3
4
5
mKeyGuardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE); 
 
  mLock = mKeyGuardManager.newKeyguardLock("activity_classname"); 
 
  mLock.disableKeyguard();

这里,activity_classname是Activity的类名。

原文作者:xirihanlin

原文链接:http://www.cnblogs.com/xirihanlin/archive/2010/06/15/1758677.html

posted @ 2011-05-18 20:03 SeanLin 阅读(30) 评论(0) 编辑

2011年5月12日

[转]ofstream/ifstream 文本/二进制 方式 读入/写出 数据方法

摘要: 原文地址:http://www.cnblogs.com/cy163/archive/2007/05/26/760426.html文件 I/O 在C++中比烤蛋糕简单多了。 在这篇文章里,我会详细解释ASCII和二进制文件的输入输出的每个细节,值得注意的是,所有这些都是用C++完成的。 一、ASCII 输出 为了使用下面的方法, 你必须包含头文件<fstream.h>(译者注:在标准C++中,已经使用<fstream>取代< fstream.h>,所有的C++标准头文件都是无后缀的。)。这是 <iostream.h>的一个扩展集, 提供有缓冲的文件阅读全文

posted @ 2011-05-12 16:37 SeanLin 阅读(124) 评论(0) 编辑

2011年5月4日

【转】为什么会出现LNK2005"符号已定义"的链接错误?

摘要: 许多Visual C++的使用者都碰到过LNK2005:symbol already defined和LNK1169:one or more multiply defined symbols found这样的链接错误,而且通常是在使用第三方库时遇到的。对于这个问题,有的朋友可能不知其然,而有的朋友可能知其然却不知其所以然,那么本文就试图为大家彻底解开关于它的种种疑惑。 大家都知道,从C/C++源程序到可执行文件要经历两个阶段:(1)编译器将源文件编译成汇编代码,然后由汇编器(assembler)翻译成机器指令(再加上其它相关信息)后输出到一个个目标文件(object file,VC的编译器编译阅读全文

posted @ 2011-05-04 09:58 SeanLin 阅读(9) 评论(0) 编辑

MDd,MLd,MTd链接选项的差异

摘要: 各种 C 运行时库的区别 ( 1 )静态链接的单线程库 静态链接的单线程库只能用于单线程的应用程序, C 运行时库的目标代码最终被编译在应用程序的二进制文件中。通过 /ML 编译选项可以设置 Visual C++ 使用静态链接的单线程库。 ( 2 )静态链接的多线程库 静态链接的多线程库的目标代码也最终被编译在应用程序的二进制文件中,但是它可以在多线程程序中使用。通过 /MT 编译选项可以设置 Visual C++ 使用静态链接的单线程库。 ( 3 )动态链接的运行时库 动态链接的运行时库将所有的 C 库函数保存在一个单独的动态链接库 MSVCRTxx.DLL 中, MSVCRTxx.DLL 阅读全文

posted @ 2011-05-04 09:39 SeanLin 阅读(41) 评论(0) 编辑

2011年4月29日

Android CTS测试的几个必要条件

摘要: 1. 真机host,虚拟机下的linux可能会出现超时问题2. devices是usr版本,设置屏幕为不关闭,并打开辅助功能的几个选项。3.devices开机之后,停留在home界面下,且不要操作4.运行adb start-server,但是不要运行其他相关的adb client程序5.手机上需有T卡6.默认语言修改为英文阅读全文

posted @ 2011-04-29 14:13 SeanLin 阅读(50) 评论(0) 编辑

导航

<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

公告

昵称:SeanLin
园龄:4年
粉丝:2
关注:1

搜索

 
 

常用链接

随笔分类

随笔档案

最新评论

阅读排行榜

评论排行榜

推荐排行榜