??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久久99精品成人片,久久亚洲av无码精品浪潮,精品国产91久久久久久久http://www.shnenglu.com/ivenher/category/243.htmlzh-cnThu, 22 May 2008 21:08:16 GMTThu, 22 May 2008 21:08:16 GMT60深入getmessage和peekmessagehttp://www.shnenglu.com/ivenher/articles/12473.html爱饭?/dc:creator>爱饭?/author>Thu, 14 Sep 2006 10:18:00 GMThttp://www.shnenglu.com/ivenher/articles/12473.htmlhttp://www.shnenglu.com/ivenher/comments/12473.htmlhttp://www.shnenglu.com/ivenher/articles/12473.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/12473.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12473.html阅读全文

]]>
windows|络~程?/title><link>http://www.shnenglu.com/ivenher/articles/12130.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Thu, 07 Sep 2006 14:32:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/12130.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12130.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12130.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12130.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12130.html</trackback:ping><description><![CDATA[ <p> 3. recv和W S A R e c v<br />对在已连接套接字上接受接入数据来_ r e c v函数是最基本的方式。它的定义如下:</p> <p>int recv(<br />     SOCKET s,<br />     char FAR * buf,<br />     int len,<br />     int flags<br />    );<br />W一个参数sQ是准备接收数据的那个套接字。第二个参数b u fQ是卛_收到数据的字W缓Ԍ而l e n则是准备接收的字节数或b u f~冲的长度。最后, f l a g s参数可以是下面的| 0、M S G _ P E E K或M S G _ O O B。另外,q可对这些标志中的每一个进行按位和q算。当Ӟ 0表示无特D行为。M S G _ P E E K会有用的数据复制到所提供的接收端~冲内,但是没有从系l缓<br />冲中它删除。另外,q返回了待发字节数?br />消息取数不太好。它不仅D性能下降Q因为需要进行两ơ系l调用,一ơ是取数Q另一ơ是无M S G _ P E E K标志的真正删除数据的调用Q,在某些情况下q可能不可靠。返回的数据可能没有反射出真正有用的数量。与此同Ӟ把数据留在系l缓Ԍ可容Ux入数据的pȝI间׃来少。其l果便是Q系l减各发送端的T C PH口定w。由此,你的应用׃能获得最大的通。最好是把所有数据都复制到自q~冲中,q在那里计算数据。前面曾介绍qM S G _ O O B标志。有兌情,参见前面“带外数据”的内容?/p> <p>在面向消息或面向数据报的套接字上使用r e c vӞq几点应该注意。在待发数据大于所提供的缓冲这一事g中,~冲内会量地填充数据。这Ӟ r e c v调用׃产生W S A E M S G S I Z E错误。注意,消息镉K误是在用面向消息的协议时发生的。流协议把接入的数据~存下来Q?br />q尽量地q回应用所要求的数据,即待发数据的数量比~冲大。因此,Ҏ式传输协议来_׃会碰到W S A E M S G S I Z Eq个错误?br />W S A R e c v函数在r e c v的基上增加了一些新Ҏ。比如说重叠I / O和部分数据报通知?br />W S A R e c v的定义如下:</p> <p>int WSARecv(<br />       SOCKET s,<br />       LPWSABUF lpBuffers,<br />       DWORD   dwBufferCount,<br />       LPWORD  lpNumberOfBytesRecved,<br />       LPWSAOVERLAPPED lpOverlapped,<br />       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE<br />      );<br />      <br />参数sQ是已徏立连接的套接字。第二和W三个参数是接收数据的缓册Ӏl p B u ff e r s参数是一个W S A B U Fl构l成的数l,而d w B u ff e r C o u n t则表明前一个数l中W S A B U Fl构的数目?br />如果接收操作立即完成Q?l p N u m b e r O f B y t e s R e c e i v e d参数׃指向执行q个函数调用所收到的字节数。l p F l a g s参数可以是下面Q何一个| M S G _ P E E K、M S G _ O O B、M S G _ PA RT I A L或者对q些D行按位和q算之后的结果。M S G _ PA RT I A L标志使用和出现的地方不同Q其?br />义也不同。对面向消息的协议来_q个标志是W S A R e c v调用q回后设|的Q如果因为缓冲空间不够导致整条消息未能在q次调用中返回的话)。这Ӟ后面的W S A R e c v调用׃讄q个标志M A S G _ PA RT I A LQ直到整条消息返回,才把q个标志清除。如果这个标志当作一个输入参数投递,接收操作应该在一收到数据q束,即它收到的只是整条消息中的一部分?br />M S G _ PA RT I A L标志只随面向消息的协议一起用。每个协议的协议条目都包含一个标志,表明是否支持q一Ҏ。有兌情,参见W?章。l p O v e r l a p p e d和l p C o m p l e t i o n R O U T I N E参数用于重叠I / O操作</p> <p>4. WSARecvDisconnect<br />q函CW S A S e n d D i s c o n n e c t函数对应Q其定义如下Q?br />int WSARecvDisconnect( <br />            SOCKET s,<br />            LPWSABUF lpOUTboundDisconnectData<br />           );<br />和W S A S e n d D i s c o n n e c t函数的参CP该函数的参数也是已徏立连接的套接字句柄和<br />一个有效的W S A B U Fl构Q带有收到的数据Q。收到的数据可以只是断开数据。这个断开数据是另一端执行W S A S e n d D i s c o n n e c t调用发出的,它不能用于接收普通数据。另外,一旦收到这个数据, W S A R e c v D i s c o n n e c t函数׃取消接收q程通信方的数据Q其作用和调用带有S D _ R E C V的s h u t d o w n函数相同?br />5. WSARecvEx<br />W S A R e c v E x函数是微软专有的Winsock 1扩展Q除了f l a g s参数是按值引用外Q其余和r e c v函数是一L。它允许基层的提供者设|M S G _ PA RT I A L标志。该函数的原型如下:</p> <p>int PASCAL FAR WSARecvEx(<br />             SOCKET s,<br />             char FAR * buf,<br />             int len,<br />             int * flags<br />            );<br />            </p> <p> <br />如果收到的数据不是一条完整的消息Q?f l a g s参数中就会返回M S G _ PA RT I A L标志。对面向消息的协议(即非协议)来说Q这个标志比较有用(即非协议)。在M S G _ PA RT I A L标志被当作f l a g s参数的一部分投递,而且收到的消息又不完整时Q调用W S A R e c v E xQ就会立?br />q回收到的那个数据。如果提供的接收~冲容纳不下整条消息Q?W S A R e c v E x׃p|Qƈ出现W S A E M S G S I Z E 错误Q剩下的数据也会被截掉。注意, M S G _ PA RT I A L 标志和W S A E M S G S I Z E错误之间的确区别是:有了q个错误Q即使整条消息到达接收端Q但׃?br />供的数据~冲太少Q也不能对它q行接收。M S G _ P E E K 和M S G _ O O B标志q可以和W S A R e c v E x一起用?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12130.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-07 22:32 <a href="http://www.shnenglu.com/ivenher/articles/12130.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows|络~程?/title><link>http://www.shnenglu.com/ivenher/articles/12055.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Tue, 05 Sep 2006 11:31:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/12055.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/12055.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/12055.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/12055.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/12055.html</trackback:ping><description><![CDATA[ <p> W?章重定向?br /> 首先Q我们打解释如<br />何通过|络Q用“多U N C提供者”( Multiple UNC Provider, MUPQ资源定位符Q通过“通用命名规范”(Universal Naming Convention, UNCQ来引用q程文g?/p> <p>随后Q我们讲解了M U P如何调用一个网l提供者,从而揭C出怎样通过一个重定向器,在“服务器消息块”(Server Message Block, SMBQ协议的帮助下,在不同的计算Z间徏立数据通信?/p> <p>最后,我们探讨了网l安全方面的一些问题。用基本的文gI / O操作Q通过|络来访问文件时Q这些安全问题是必须考虑到的?/p> <p>2.1 通用命名规范</p> <p>“U N C路径? 为网l文件及讑֤的访问徏立了一套统一的规范。它最大的特点便是不必指定或引用一个已映射到远E文件系l的本地驱动器字母?/p> <p>U N C名字完全解决了这些问题,它的格式如下Q?br />\ \ [服务器] \ [׃n名] \ [路径]<br />W一部分是\ \ [服务器]Q必M两个反斜杠开_紧跟着一个服务器名字?br />W二部分是\ [׃n名]Q它对应着q程服务器上的一个“共享入口”或者“共享位|”?br />。而第三部分\ [路径]  对应的是׃n位置下的某个具体目录Q或子目录)</p> <p> <br />W?章邮?/p> <p>一U简单的单向“进E间通信”(interprocess communication,I P CQ机制。这个机制的名字非常古怪,叫作“邮槽”(M a i l s l o tQ。用最单的话来_通过<br />邮槽Q客hq程可将消息传送或q播l一个或多个服务器进E。在同一台计机的不同进E之_或在跨越整个|络的不同计机的进E之_协助q行消息的传输。用邮槽来开发应用程序是一仉常简单的事情Q不要求对T C P / I P或I P Xq样的基层网l传送协议有着非常深入的了解。由于邮槽是围绕一个广播通信体系设计出来的,所以当然不能指望能通过它实现数据的“可靠”传输?/p> <p>邮槽最大的一个缺点便是只允许从客h到服务器Q徏立一U不可靠的单向数据通信?br />而另一斚wQ邮槽最大的一个优点在于,它们使客h应用能够非常Ҏ地将q播消息发送给一个或多个服务器应用?/p> <p>3.1 邮槽实施l节<br />邮槽是围lWi n d o w s文gpȝ接口设计出来的。客h和服务器应用需要用标准的Wi n 3 2文gpȝI / OQ输入/输出Q函敎ͼ比如R e a d F i l e和Wr i t e F i l e{等Q以便在邮槽上收发数据,同时利用Wi n 3 2文gpȝ的命名规则。邮槽必M赖Wi n d o w s重定向器Q通过一个“邮槽文件系l”(Mailslot File System, MSFSQ,来创建及标识邮槽?/p> <p>3.1.1 邮槽的名?br />寚w槽进行标识时Q需遵守下述命名规则Q?br />\ \ s e r v e r \ M a i l s l o t \ [ p a t h ] n a m e<br />请将上述字串分ؓ三段来看Q?\ \ s e r v e r、\ M a i l s l o t和\ [ p a t h ] n a m e。第一部分\ \ s e r v e r对应于服务器的名字,我们要在上面创徏邮槽Qƈ在在上面q行服务器程序。第二部分\ M a i l s l o t是一个“硬~码”的固定字串Q用于告诉系l这个文件名从属于M S F S。而第三部分\ [ p a t h ] n a m e?br />允许应用E序独一无二地定义及标识一个邮槽名。其中,“p a t h”代表\径,可指定多U目录?br />举个例子来说Q对一个邮槽进行标识时Q下面这些Ş式的名字都是合法的(注意M a i l s l o t不得变化Q必d文照输,亦即所谓的“硬~码”)Q?br />׃邮槽要依赖Wi n d o w s文gpȝ服务在网上来创徏和传输数据,所以接口是“与协议无关”的?br />要想保证各种Wi n d o w sq_之间能够完全正常地通信Q强烈徏议将消息长度限制? 2 4字节Q或者更短。如果进行面向连接的传输Q可考虑使用命名道Q而不是简单的邮槽?/p> <p>3.5 结<br />本章讲解了邮槽( M a i l s l o tQ网l编E技术。利用这一技术,应用E序可以在Wi n d o w s重定向器的帮助下Q实现简单的单向q程间数据通信。对邮槽来说Q它最有h值的一功能便是通过|络Q将一条消息广播给一台或多台计算机。然而,邮槽q未提供Ҏ据可靠传输的保障。假如希望用Wi n d o w s重定向器实现“可靠”的数据通信Q请考虑使用命名道Q这是下一章的主题Q?/p> <img src ="http://www.shnenglu.com/ivenher/aggbug/12055.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-09-05 19:31 <a href="http://www.shnenglu.com/ivenher/articles/12055.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windows |络~程http://www.shnenglu.com/ivenher/articles/12040.html爱饭?/dc:creator>爱饭?/author>Tue, 05 Sep 2006 08:46:00 GMThttp://www.shnenglu.com/ivenher/articles/12040.htmlhttp://www.shnenglu.com/ivenher/comments/12040.htmlhttp://www.shnenglu.com/ivenher/articles/12040.html#Feedback1http://www.shnenglu.com/ivenher/comments/commentRss/12040.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/12040.html1、netBIOS
非可路由协议
LAN适配器(LAN  adapterQ编号很重要.
每个LANA~号对应于网卡和传输协议的唯一l合?/p>

netBIOS名字
在Wi n 3 2环境中,针对每个可用的L A N A~号Q每
个进E都会ؓ其维持一张N e t B I O S名字表。若为LANA 0增添一个名字,意味着你的应用E序
只能在LANA 0上同客户机徏立连接。对每个L A N A来说Q能够添加的名字的最大数量是2 5 4Q?br />~号?? 5 4Q?? 5 5ql保留)
QN e t B I O S名字共有两个性质Q唯一名字和组?br />微Y|络中的机器命名是NetBIOS命名?br />“组名”的作用是将数据同时发给多个接收者;或者相反,接收发给多个
接收者的数据。组名ƈ非一定要“独一无二”,它主要用于多播(多点发送)数据通信
若有“windows互联|命名服务器”即winsQ则有它理Q若无则发广播探是否重名?/p>

1.1.3 NetBIOSҎ?/p>

N e t B I O S同时提供了“面向连接”服务以及“无q接”服务?/p>

1.2 NetBIOS~程基础
NetBIOS API的设|,只有一个函敎ͼ
UCHAR Netbios(PNCB pNCB);

用于N e t B I O S的所有函数声明、常数等{均是在头文件N b 3 0 . h内定义的。若惌?br />N e t B I O S应用Q唯一需要的库是N e t a p i 3 2 . l i b?br />调用N e t b i o s函数Ӟ可选择q行同步调用Q还是进行异步调用。所有N e t B I O S命o本n均是同步的。要惛_步调用一个命令,需要让N e t B I O S命o同A S Y N C H标志q行一ơ逻辑O RQ或Q运。如指定了A S Y N C H标志Q那么必dn c b _ p o s t字段中指定一个后例程QPost RoutineQ,或必dn c b _e v e n t字段中指定一个事件句柄。执行一个异步命令时Q从N e t b i o sq回的值是N R C _ G O O D R E T( 0 x 0 0 )Q但n c b _ c m d _ c p l t字段会设为N R C _ P E N D I N G ( 0 x F F )。除此以外, N e t b i o s函数q会N C Bl构的n c b _ c m d _ c p l t字段设ؓN R C _ P E N D I N GQ待冻IQ直到命令完成ؓ止。命令完成后Qn c b _ c m d _ c p l t字段会设命o的返回倹{N e t b i o s也会在完成后n c b _ r e t c o d e字段设ؓ命o的返回倹{?br />

1.4 数据报的工作原理

“数据报”(D a t a g r a mQ属于一U“无q接”的通信Ҏ。作为发送方Q只需用目?br />N e t B I O S名字为发出的每个包定址Q然后简单地送出了事。此Ӟ不会执行M查,以确
保数据的完整性、抵N序或者传输的可靠性等{?/p>

发出一个数据报共有三种方式?br />W一U是指挥数据报抵达一个特定的Q或唯一的)l名。这意味着只能有一个进E负责数据报的接收—亦x册了目标名字的那个进E?br />W二U是数据报发给一个组名。只有注册了指定l名的那些进E才有权接收消息?br />最后,W三U方式是数据报q播到整个网l?/p>

]]>
Rich Client技术简?4http://www.shnenglu.com/ivenher/articles/5814.html爱饭?/dc:creator>爱饭?/author>Tue, 18 Apr 2006 04:52:00 GMThttp://www.shnenglu.com/ivenher/articles/5814.htmlhttp://www.shnenglu.com/ivenher/comments/5814.htmlhttp://www.shnenglu.com/ivenher/articles/5814.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/5814.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/5814.html

    原代号ؓ“Royale”的MacromediaFlex软g把服务器Y件、开发指南和其他工具l合在一P使传l的|络应用开发h员能够用Macromedia公司的Flash格式创作软g单元。如从前报道的那P该品的重点是让那些使用Sun微系l公司的Java2企业?J2EE)的开发h员能够创作出更有吸引力、更ҎD的J2EE应用软g接口?o:p>

    FlexJ2EE开发h员用标准的文本式开发工h制作Flash应用E序Q而不必用Macromedia公司以前出售的复杂的设计工具。Macromedia公司从今q年初开始,努力扩大Flash格式对于L开发商的吸引力Q其目标是扩大Flash的用途,使其成ؓ提供互联|应用和建立交互式网站的基础?o:p>

    Macromedia公司计划?004q上半年推出Flex服务器YӞ该Y件的h目前q没有确定。它的初U版本将q行于J2EE中,q计划随后推出支持微软的.Net格式的版本。最初的支持者包括IBM公司Q它随自己的WebSphere软g一hqFlex的应用?

    需要了解更多Flex技术的朋友可以讉KFlex的主:

    http://www.macromedia.com/software/flex/ 

 

Thinlet

    Thinlet是一个采用Applet解析XULq提供相应界面的解析器,在事件发生时Q调用用戯q事g处理E序(java E序)Q需要客L览器支持Applet。更多信息可以参考 http://www.thinlet.com/

 



]]>
Rich Client技术简?3http://www.shnenglu.com/ivenher/articles/5813.html爱饭?/dc:creator>爱饭?/author>Tue, 18 Apr 2006 04:50:00 GMThttp://www.shnenglu.com/ivenher/articles/5813.htmlhttp://www.shnenglu.com/ivenher/comments/5813.htmlhttp://www.shnenglu.com/ivenher/articles/5813.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/5813.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/5813.htmlHessianQhttp://www.caucho.comQ是由Resin应用服务器的开发商Caucho公司制定的一个RPC协议Q虽然它也是通过HTTP协议传输RPC包Q但是它的RPC包却是以二q制形式~码的,而且能够表现对象模型和异怽p,q就使得Hessian?a class="Channel_KeyLink" >XML-RPCh更高的效率??XML:NAMESPACE PREFIX = O /?>

具体通信机制资料误者参考网上内容和透明?004q?期《程序员》杂志中《王朝复辟还是ʎ火重生》一文?o:p>

Rich Client 开源开发^?/b> Laszlo

Laszlo是一个开源的Rich client开发环境。用Laszloq_Ӟ开发者只需~写名ؓLZX的描q语aQ其中整合了XML和JavaScriptQ,q行在J2EE应用服务器上的Laszloq_会将其编译成FLASH文gq传输给客户端展C。单从运行原理来_Laszlo与XULQ?a class="Channel_KeyLink" >XML用户接口语言Q  ?XML User interface LanguageQ、XAMLQ“Longhorn”)标记语言很类伹{但它的最大优势在于:它把描述语言~译成FLASHQ而FLASH是Q何浏览器都支持的展示形式Q从而一举解决了览器之间的UL问题。而且Q在未来的计划中QLaszloq可以将LZX~译成Java?NET本地代码Q从而大大提高运行效率?o:p>

具体请参考http://www.openlaszlo.org?o:p>

 

IBM AlphaWorks|站q日发布了用于开发Laszlo应用E序的集成开发环境(实际上是一个Eclipse插gQ,使J2EE开发者能够在他们熟悉的Eclipse环境中快速开发基于Laszlo的rich client应用E序。可以在下列地址下蝲该插Ӟ

http://alphaworks.ibm.com/tech/ide4laszlo

此外QAlphaWorks|站q提供了一个用Laszlo开发的CZ应用Q展CZ在Eclispe环境下开发Laszlo应用的过E。demo的地址如下Q?o:p>

http://dl.alphaworks.ibm.com/technologies/rcb/demo.html

 

FLEX

    Flex是Macromedia公司开发的Q用于Rich client开发的环境Q其原理是将MXML(the Macromedia Flex Markup Language)文gQ编译成SWF文gQ然后显C在览器中,q利用Web Service技术和服务器通信。从而利用Flash的强大功能,带来更丰富的用户体验?/p>

]]>
Rich Client技术简?2http://www.shnenglu.com/ivenher/articles/5812.html爱饭?/dc:creator>爱饭?/author>Tue, 18 Apr 2006 04:47:00 GMThttp://www.shnenglu.com/ivenher/articles/5812.htmlhttp://www.shnenglu.com/ivenher/comments/5812.htmlhttp://www.shnenglu.com/ivenher/articles/5812.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/5812.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/5812.htmlRMI可以是做CORBA的Java版本Q但相比较而言q是一个轻量的版本了Q对于服务器和客L两边都用Java来实现的前提下,q是一个非常好的选择??XML:NAMESPACE PREFIX = O /?>

CORBA和RMI有一个共同的~陷Q通常不会在系l?0端口提供服务Q所以这在具备网|防火墙的情况下昑־非常被动?o:p>

XML-RPC

Z解决在系l的80端口提供RPC的服务,而又不媄响正在执行的WEB服务Qh们想Z用HTTP协议传输RPC包的办法。对于几乎是专门用于传输文本的HTTP协议Q要在其上传输RPC包Q最方便的方法莫q于把RPC包~码成文本Ş式——例?a class="Channel_KeyLink" >XML文g?o:p>

XML-RPCQhttp://www.xml-rpc.comQ是q国UserLand公司指定的一个RPC协议。它RPC信息包~码?a class="Channel_KeyLink" >XMLQ然后通过HTTP传输包Q它是一个简单的RPC协议Q只支持一些基本数据类型,不支持对象模型,q势必掣肘在客户端和服务器端之间传输复杂的对象?o:p>

 

SOAP

      SOAP即Simple Object Access Protocol(单对象访问协?是在分散或分布式的环境中交换信息的简单的协议Q是一个基?a class="Channel_KeyLink" >XML的协议。它允许所有的操作?0端口上进行,从而也l过了防火墙{问题?o:p>

  SOAP规范中有三个基本l成部分QSOAP装Q编码规则,以及在请求和响应之间的交互方式?o:p>

  目前已有的基于JAVA提供SOAP功能的品有QApache SOAP, IBM SOAP4J{?o:p>

    要了解更多关于SOAP的信息,可以讉K http://www.w3.org/TR/SOAP



]]>
Rich Client技术简?1Q{Q?/title><link>http://www.shnenglu.com/ivenher/articles/5811.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Tue, 18 Apr 2006 04:36:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/5811.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/5811.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/5811.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/5811.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/5811.html</trackback:ping><description><![CDATA[传统的基于C/S的Windows应用E序L让客户面临着一些感觉很是不爽的问题Q如Q部|问题、升U困难、维护困难、安全问题,但是完全的WEB开发由于HTTP协议的无状态特性——浏览器和服务器L在不停地执行Request和Response来营造一U有状态、持l会话的假象Q致使h们又开始怀恋具有更能力、本地存储能力和更稳定的通讯能力的客LE序了??xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?><o:p></o:p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24.1pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">不得不说的是Q宽带网l的出现在某U意义促成了Rich Client的诞生。通过快捷的发布,特定的通讯协议标准QRich Client正以不可L的气势向Z重现着C/S模式下客LE序的优ѝ?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 13pt 0cm; LINE-HEIGHT: 170%; mso-layout-grid-align: none; mso-pagination: lines-together; mso-outline-level: 1"><b>Rich Client</b><b>的发?/b><b><o:p></o:p></b></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">C/S架构下,客户端程序发布与l护一直比较困隑֒J琐。在版本更新以后Q需要对客户的客LE序q行逐个下蝲安装及配|更斎ͼq是一个体力活Q而这也一直是使用户大量选择WEBE序的因素之一?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">在Rich Client时代Q由于宽带网l的便利Q在客户端尽需要从服务器端下蝲已经更新好的E序q行Q而不必理会繁琐的下蝲、安装和配置的过E?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">q里不得不提Java的是WebStart技术?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">WebStart是让用户只需在网上点击一个超U链接就能运行一个Java桌面应用的技术。对于一个拥有WebStart能力的Java应用来说Q用户用它和使用WEB应用一L单,但它所h的界面能力和本地处理能力却是WEB应用无法望其背的?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">具体的应用的技术知识可以从<u>http://java.sun.com</u>中寻扄x档,q里不一一赘述?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 13pt 0cm; LINE-HEIGHT: 170%; mso-layout-grid-align: none; mso-pagination: lines-together; mso-outline-level: 1"><b>Rich Client</b><b>的通信机制</b><b><o:p></o:p></b></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">除了快捷方便的发布外QRich Clientq需要与服务器端建立一U快速、可靠、强大、易用的通信交互机制。但我们开发WEB应用Ӟ表现层和业务服务层常常只是同一个进E中的不同对象,它们之间的交互不q是Java的方法调用而已Q当表现层逻辑被分发到世界各地的计机上,客户端和服务器之间的交互成了一个大问题——从前的C/S被淘汎ͼ很大E度上归咎于socket通信的复杂性?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">现在QŞ形色色的RPCQ远E过E调用,Remote Procedure CallQ技术以独特的优势扮演v了信使的角色。以下列丑ևURich Client可以采用的通信机制?o:p></o:p></p><p class="MsoNormal" style="MARGIN: 13pt 0cm; LINE-HEIGHT: 170%; mso-layout-grid-align: none; mso-pagination: lines-together; mso-outline-level: 1"><b>CORBA</b><b>?/b><b>RMI<o:p></o:p></b></p><p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; LINE-HEIGHT: 150%; mso-layout-grid-align: none">CORBAQ通用对象h代理体系l构QCommon Object Request Broker ArchitectureQ曾l红极一Ӟ它能够兼容各U操作系l^台的语言Q强大的的可扩展性所带来的负面媄响就是实现的复杂和繁琐。如果服务器端和客户端都采用Java开发,那么CORBA所需要的语言无关的IDL完全变成了画蛇添。当Ӟ对于需要集成大量企业内遗留的系l的EAIQ企业应用集成)目中,它一直是首选的技术?/p><img src ="http://www.shnenglu.com/ivenher/aggbug/5811.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2006-04-18 12:36 <a href="http://www.shnenglu.com/ivenher/articles/5811.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在DOS实模式下直接存取4GB内存http://www.shnenglu.com/ivenher/articles/1737.html爱饭?/dc:creator>爱饭?/author>Wed, 14 Dec 2005 03:57:00 GMThttp://www.shnenglu.com/ivenher/articles/1737.htmlhttp://www.shnenglu.com/ivenher/comments/1737.htmlhttp://www.shnenglu.com/ivenher/articles/1737.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/1737.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/1737.html        虽然有许多书c对保护模式作解释,但没有一本能单明了地解释清楚Q冗长烦杂的术语让h看着x瞌睡Q甚臌有许多用汇编写的(可能Ҏ不能q行?保护模式试验E序Q事实上用C语言本n可以做保护模式的进出工作?BR>       我们可能知道CPU上电后从ROM中的BIOS开始运行,而Intel文档却说80x86CUP上电L从最高内存下16字节开始执行,那么BIOS是处在内存的最端64K(FFFF0000H)q是1M之下?4K(F0000H)处呢?事实上在q两个地斚w同时出现(可用后面存取4GB内存的程序验??BR>Z?Z弄清楚以上问题,首先要了解CPU是如何处理物理地址?真的是在实模式下用段寄存器左U?位与偏移量相加,在保护模式下用段描述W中的基地址加偏U量而两者是毫无兌的吗?{案是两者其实是一L。当Intel?0286推出时其地址I间变成?4位,?086?0位到24位,十分自然地要加大D寄存器才行Q实际上它们都被加大了,只是׃保护的原因加大的部分没有被程序看见,C80386之后地址又从24位加大到32?80386SX?4?。整个段寄存器如下图所C:
@@12A08400.GIF;?@@
?086中CPU只有“看得见部分”,从而也直接参与了地址形成q算Q但?0286之后Q在“看不见部分”中已经包含了地址|“看得见部分”就退化ؓ只是一个标号再也不用参与地址形成q算了。地址的Ş成L从“不可看见部分”取出基址g偏移相加形成地址。也是说在实模式下当一个段寄存器被装入一个值时Q“看不见部分”的界限被设成FFFFHQ基址部分才是要装入值左U?位,属性部分设?6?ҎU。这个过E与保护模式时装入一个段存器是同理的Q只是保护模式的“不可见部分”是从描q表中取|而实模式是一套固定的q程?BR>对于CPU在Ş成地址Ӟ是没有实模式与保护模式之分的Q它只管用基址(“不可见部分?d上偏U量。实模式与保护模式的差别实际上只是保护处理部件是否工作得更精而已Q比如不允许代码D늚写入。实模式下的D寄存装入有固定的Ş成办法从而也׃需要保护模式的“描q符”了Q因此保持了?086/8088的兼Ҏ。而“描q符”也只是Z装入D寄存器的“不可见部分”而设的?BR>从上面的“整个段寄存器”可见CPU的地址形成与“看得见部分”的当前值毫无关p,q也解释了ؓ什么在刚进入保护模式时后面的代码依然被正确地运行而这时代码段寄存器CS的值却q是q入保护模式前的实模式|或者从保护模式回到实模式时代码DCS被改变之前程序是正常地工作,而不会“突变”到CS左移4位的地址上去Q比如在保护模式时CS?8H的选择器,C实模式时CSq是08H但地址不会H然变成80H加上偏段量中厅R因为地址的Ş成不理会D寄存器“看得见部分”的当前|q一个值只是在被装入时对CPU有用?BR>地址的Ş成与CPU的工作模式无养I也就是说实模式与0ҎU保护模式不分页时是一模一L。明白了q一机理Q在实模式下一样可以处理通常被认为只有在保护模式才能做的事,比如讉K整个机器的内存。可以不必理会保护模式下的众多术语,或者更易于理解Q如选择器就是“看得见部分”,描述W是Z装入“不可见部分”而设的?BR>作ؓ验证CPU的这U机理,q里写了一个实模式下访?GB内存的CE序。有一些书c也介绍有同样功能的汇编E序Q但它们都错误地认ؓ是利?0386芯片的设计疏漏。实际上Intel本n在使用q种办法Q得CPU上电时能从FFFFFFF0H处开始第一条指令,q种技术在286之后的每一台机器每一ơ冷启动旉使用Q只是我们不知道|了。CPU上电也整个代码段寄存器是q样的:
@@12A08401.GIF;?@@
EIP=0000FFF0H
q样CS∶EIP形成了FFFFFFF0H的物理地址Q当CPUq行一ơ远跌{重新装入CSӞ基址变了?BR>Z讉K4G内存I间Q必L一个段寄存器的“不可见部分”的界限?G-1Q基址?Q这样就包含?GB内存Q不必理会可见部分的倹{显然要让段寄存器在实模式下直接装入q些值是不可能的。唯一的办法是让CPUq入一会儿保护模式在装入了D寄存器之后马上回到实模式?BR>q入保护模式十分单,只要建好GDT把CRO寄存器的?|上1QCPU在保护模式了,从前面所分析CPU地址形成机理可知Q这时不必理会寄存器的“看得见部分”值是否合法,各种D寄存器是一样可用的Q就像没q保护模式一栗在把一个包含有4GB地址I间的D入某个段寄存器之后就可返回实模式?BR>预先可徏好GDT如下Q?BR>unsigned long GDT-Table[]={0,0, //I描q符Q必Mؓ?x0000FFFF,0xCF9A00, //32位^面式代码D?x0000FFFF,0xCF9200 } , //32位^面式数据D只是ؓ了访问数据的话只?个GDTp够了Q因为ƈ没有重装代码D,q里l出3个GDT只是Z完整性?BR>通常在进入保护模式时要关闭所有的中断Q把IDTR的界限设|ؓ0QCPU自动关闭所有中断,包括NMIQ返回实模式后恢复IDTRq开中断?BR>另外A20地址U的控制对于正确讉K整个内存也很重要Q在q入保护模式前要?042打开A20地址Uѝ?BR>在这个例子里FSD寄存器设成可访?GB内存的基址和界限,׃在DOS中很有E序会用到GS、FSq两?86增加的段寄存器,当要d4GB范围中的M个地斚w可通过FSD|辑ֈQ直到FS在实模式下被重装入冲掉ؓ止?BR>q个例子?86SX?86DX?86上都q行通过。例子里加有十分详细的注释,׃q一E序是用BC 3.1~译q接的,而其q接器不能ؓDOSE序处理32位寄存器Q所以直接在代码中加入操作码前缀0x66和地址前缀0x67Q以便让DOS实模式下?6位程序可?2位寄存器和地址。程序的双以注释Ş式给出等效的32位指令。要注意16位的指o中mov al, byte ptr [BX]的指令码正好?2位的指omov al, byte ptr[EDI]?BR>读者可用这个程序验证BIOS是否同时在两个区域出现。如果有U性定址能力的VESA昄?如TVGA9440)q可q一步验证线性显C缓冲区?MB之上的工作情c?BR>#include <dos.h>
unsigned long GDT-Table[]=
{0,0, //NULL - 00H
0x0000FFFF,0x00CF9A00, //Code32 - 08h Base=0 Limit=4G-1 Size=4G
0x0000FFFF,0x00CF9200 //Data32 - 10h Base=0 Limit=4G-1 Size=4G
};
unsigned char OldIDT [6]={0}; //Save The IDTR before Enter Protect Mode.
unsigned char pdescr-tmp [6]={0}; //NULL The IDTR s Limit=0 CPU will
// disable all Interrupts, include NMI.
#define KeyWait() {while(inportb(0x64) &2);}
void A20Enable(void)
{
keyWait ();
outportb(0x64,0xD1);
KeyWait();
outportb(0x60,0xDF); //Enable A20 with 8042.
KeyWait();
outportb(0x64,0xFF);
KeyWait ();
}
void LoadFSLimit4G(void)
{
A20Enable (); //Enable A20
//***
Disable ints & Null IDT
//***
asm {
CLI //Disable inerrupts
SIDT OldIDT //Save OLD IDTR
LIDT pdescr-tmp //Set up empty IDT.Disable any interrupts,
} // Include NMI.
//***
Lodd GDTR
//***
asm{ // The right Code is Real, But BC++ s Linker NOT
// Work with 32bits Code.
db 0x66 //32 bit Operation Prefix in 16 Bit DOS.
MOV CX,DS //MOV ECX,DS
db 0x66 //Get Data segment physical Address
SHL CX,4 //SHL ECX,4
MOV word ptr pdescr-tmp [0],(3*8-1)
//MOV word ptr pdescr-tmp [0], (3*8-1)
db 0x66
XOR AX,AX //XOR EAX,EAX
MOV AX,offset GDT-Table
// MOV AX,offset GDT-Table
db 0x66
ADD AX,CX //ADD EAX,ECX
MOV word ptr pdescr-tmp [2], AX
//GDTR Base low16 bits
db 0x66
SHR AX,16 //SHR EAX,16
MOV word ptr pdescr-tmp [4],AX
//GDTR Base high16 bits
LGDT pdescr-tmp //Load GDTR
}
//****
//* Enter 32 bit Flat Protected Mode
//****
asm{
mov DX,0x10 // The Data32 Selector
db 0x66,0x0F,0x20,0xC0 // MOV EAX,CR0
db 0x66
MOV BX,AX // MOV EBX,EAX
OR AX,1
db 0x66,0x0F,0x22,0xC0
//MOV CRO,EAX // Set Protection enable bit
JMP Flsuh
} //Clear machine perform cache.
flush: // Now In Flat Mode, But The CS is Real Mode Value.
asm { //And it s attrib is 16Bit Code Segment.
db 0x66
MOV AX,BX //MOV EAX,EBX
db 0x8E,0xE2 //MOV FS,DX
//Load FS Base=0 Size=4G now
db 0x66,0x0F,0x22,0xC0 //MOV CRO,EAX
//Return Real Mode.
LIDT OldIDT //LIDT OldIDT //Restore IDTR
STI // STI //Enable INTR
}
}
unsigned char ReadByte (unsigned long Address)
{
asm db 0x66
asm mov di,word ptr Address // MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS:
asm mov al,byte ptr [BX] // =MOV AL, FS: [EDI]
return -AL;
}
unsigned char WriteByte(unsigned Long Address)
{
asm db 0x66
asm mov di,word ptr Address //MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS:
asm mov byte ptr [BX],al //=MOV FS: [EDI],AL
return -AL;
}
//////// Don t Touch Above Code ///
# include <stdio, h>
void Dump4G (unsigned long Address)
{
int i;
int j;
for (i=0; i<20; i++)
{
printf (?081X: ? (Address+i*16));
for (j=0; j<16;j++)
printf ("% 02X" ,ReadByte (Address+i*16+j));
printf (" ");
for (j=0;j<16;j++)
{
if (ReadByte (Address+i*16+j) <0x20) printf (" . ");
else printf (" %C ", ReadByte (Address+i*16+j));
}
printf (" ");
}
}
main ()
{
unsigned long Address=0;
unsigned long tmp;
LoadFSLimit4G ();
printf ("====Designed By Southern. 1995.7.17==== ");
printf (" Now you can Access The Machine All 4G Memory. ");
printf (" Input the Start Memory Physical to DUMP. ");
printf (" Press D to Cuntinue DUMP, 0 to End & Quit, ");
do {
printf ("-");
scanf ("%IX", &tmp);
if (tmp==0x0d) Address+=(20*16);
else Address=tmp;
Dump4G (Address);
}while (Address !=0);
return 0;
}

(作者地址Q珠巨人集团电脑排版公?


]]>
Win2k内存分页机制 (一)- -http://www.shnenglu.com/ivenher/articles/1735.html爱饭?/dc:creator>爱饭?/author>Wed, 14 Dec 2005 03:30:00 GMThttp://www.shnenglu.com/ivenher/articles/1735.htmlhttp://www.shnenglu.com/ivenher/comments/1735.htmlhttp://www.shnenglu.com/ivenher/articles/1735.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/1735.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/1735.html

基本概念

    Windows 2000 使用Z分页机制的虚拟内存。每个进E有4GB的虚拟地址I间。基于分|Ӟq?GB地址I间的一些部分被映射了物理内存,一些部分映硬盘上的交换文Ӟ一些部分什么也没有映射。程序中使用的都?GB地址I间中的虚拟地址。而访问物理内存,需要用物理地址?BR>
下面我们看看什么是物理地址Q什么是虚拟地址?BR>
物理地址 (physical address): 攑֜dȝ上的地址。放在寻址ȝ上,如果是读Q电路根据这个地址每位的值就相应地址的物理内存中的数据放到数据ȝ中传输。如果是写,电\Ҏq个地址每位的值就相应地址的物理内存中攑օ数据ȝ上的内容。物理内存是以字?8?为单位编址的?BR>
虚拟地址 (virtual address): 4G虚拟地址I间中的地址Q程序中使用的都是虚拟地址?BR>
如果CPU寄存器中的分|志位被设|,那么执行内存操作的机器指令时QCPU会自动根据页目录和页表中的信息,把虚拟地址转换成物理地址Q完成该指o。比?mov eax,004227b8h Q这是把地址004227b8h处的Dl寄存器的汇~代码,004227b8q个地址是虚拟址。CPU在执行这行代码时Q发现寄存器中的分页标志位已l被讑֮Q就自动完成虚拟地址到物理地址的{换,使用物理地址取出|完成指o。对于Intel CPU 来说Q分|志位是寄存器CR0的第31位,?表示使用分页Qؓ0表示不用分c对于初始化之后?Win2k 我们观察 CR0 Q发现第31位ؓ1。表明Win2k是用分늚?BR>
    使用了分|制之后,4G的地址I间被分成了固定大小的页Q每一|者被映射到物理内存,或者被映射到硬盘上的交换文件中Q或者没有映Q何东ѝ对于一般程序来_4G的地址I间Q只有一部分映了物理内存Q大片大片的部分是没有映Q何东ѝ物理内存也被分,来映地址I间。对?2bit的Win2kQ页的大是4K字节。CPU用来把虚拟地址转换成物理地址的信息存攑֜叫做늛录和表的结构里?BR>
    物理内存分页Q一个物理页的大ؓ4K字节Q第0个物理页从物理地址 0x00000000 处开始。由于页的大ؓ4KBQ就?x1000字节Q所以第1从物理地址 0x00001000 处开始。第2从物理地址 0x00002000 处开始。可以看到由于页的大是4KBQ所以只需?2bit的地址中高20bit来寻址物理c?/FONT>

    表Q一个页表的大小?K字节Q放在一个物理页中。由1024?字节的页表项l成。页表项的大ؓ4个字?32bit)Q所以一个页表中?024个页表项。页表中的每一的内容Q每?个字?32bitQ高20bit用来放一个物理页的物理地址Q低12bit攄一些标志?BR>
    늛录,一个页目录大小?K字节Q放在一个物理页中。由1024?字节的页目录组成。页目录的大小?个字?32bit)Q所以一个页目录中有1024个页目录V页目录中的每一的内容Q每?个字节)?0bit用来放一个页表(表攑֜一个物理页中)的物理地址Q低12bit攄一些标志?/FONT>

    对于x86pȝQ页目录的物理地址攑֜CPU的CR3寄存器中?BR>
    CPU把虚拟地址转换成物理地址Q?BR>    一个虚拟地址Q大?个字?32bit)Q包含着扑ֈ物理地址的信息,分ؓ3个部分:W?2位到W?1位这10位(最?0位)是页目录中的索引Q第12位到W?1位这10位是表中的索引Q第0位到W?1位这12位(?2位)是页内偏UR对于一个要转换成物理地址的虚拟地址QCPU首先ҎCR3中的|扑ֈ늛录所在的物理c然后根据虚拟地址的第22位到W?1位这10位(最高的10bit)的g为烦引,扑ֈ相应的页目录?PDE,page directory entry),늛录项中有q个虚拟地址所对应表的物理地址。有了页表的物理地址Q根据虚拟地址的第12位到W?1位这10位的g为烦引,扑ֈ该页表中相应的页表项(PTE,page table entry),表中有q个虚拟地址所对应物理늚物理地址。最后用虚拟地址的最?2位,也就是页内偏U,加上q个物理늚物理地址Q就得到了该虚拟地址所对应的物理地址?/FONT>

    一个页目录?024,虚拟地址最高的10bit刚好可以索引1024(2?0ơ方{于1024Q。一个页表也?024,虚拟地址中间部分?0bitQ刚好烦?024V虚拟地址最低的12bitQ??2ơ方{于4096Q,作ؓ内偏移Q刚好可以烦?KBQ也是一个物理页中的每个字节?/FONT>

    一个虚拟地址转换成物理地址的计过E就是,处理器通过CR3扑ֈ当前늛录所在物理页Q取虚拟地址的高10bit,然后把这10bit右移2bitQ因为每个页目录?个字节长Q右U?bit相当于乘4Q得到在该页中的地址Q取地址处PDEQ?个字节)Q就扑ֈ了该虚拟地址对应表所在物理页Q取虚拟地址W?2位到W?1位这10位,然后把这10bit右移2bitQ因为每个页表项4个字节长Q右U?bit相当于乘4Q得到在该页中的地址Q取地址处的PTEQ?个字节)Q就扑ֈ了该虚拟地址对应物理늚地址Q最后加?2bit的页内偏UdC物理地址?BR>
    32bit的一个指针,可以d范围0x00000000-0xFFFFFFFF,4GB大小。也是说一?2bit的指针可以寻址整个4GB地址I间的每一个字节。一个页表项负责4K的地址I间和物理内存的映射Q一个页?024,也就是负?024*4k=4M的地址I间的映。一个页目录,对应一个页表。一个页目录?024,也就对应着1024个页表,每个表负责4M地址I间的映?024个页表负?024*4M=4G的地址I间映射。一个进E有一个页目录。所以以ؓ单位Q页目录和页表可以保?G的地址I间中的每页和物理内存的映射?BR>
    每个q程都有自己?G地址I间Q从 0x00000000-0xFFFFFFFF 。通过每个q程自己的一套页目录和页表来实现。由于每个进E有自己的页目录和页表,所以每个进E的地址I间映射的物理内存是不一L。两个进E的同一个虚拟地址处(如果都有物理内存映射Q的g般是不同的,因ؓ他们往往对应不同的物理页?BR>
    4G地址I间中低2GQ?x00000000-0x7FFFFFFF 是用户地址I间Q?G地址I间中高2GQ?BR>0x80000000-0xFFFFFFFF 是系l地址I间。访问系l地址I间需要程序有ring0的权限?/FONT>

作? JIURL



]]>
const用法http://www.shnenglu.com/ivenher/articles/1643.html爱饭?/dc:creator>爱饭?/author>Fri, 09 Dec 2005 02:54:00 GMThttp://www.shnenglu.com/ivenher/articles/1643.htmlhttp://www.shnenglu.com/ivenher/comments/1643.htmlhttp://www.shnenglu.com/ivenher/articles/1643.html#Feedback7http://www.shnenglu.com/ivenher/comments/commentRss/1643.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/1643.htmlconst主要是ؓ了程序的健壮?SPAN lang=EN-US>,减少E序出错.
最基本的用?SPAN lang=EN-US>:
const int a=100; b的内容不?SPAN lang=EN-US>,b只能?SPAN lang=EN-US>100也就是声明一?SPAN lang=EN-US>intcd的常?SPAN lang=EN-US>(#define b =100)
int const b=100; //和上面作用一?SPAN lang=EN-US> 

const指针和引用一般用在函数的参数?SPAN lang=EN-US>
int* m = &a; //出错,帔R只能用常指针
int c= 1;const int*pc = &c;//
常指针可指向帔R 

const int* pa = &a; //
指针指向的内容ؓ帔R(b的g?SPAN lang=EN-US>)
int const *a = &b; //指针指向的内容ؓ帔R(b的g?SPAN lang=EN-US>)*p=3//error
int* const a = &b; //指针为常?SPAN lang=EN-US>,不能更改指针了如 a++但可以改?SPAN lang=EN-US>*p=3; 

从这可以看出const攑֜*左侧修饰的是指针的内?SPAN lang=EN-US>,const攑֜*右侧修饰的是指针
本n

const
引用的用法和指针一?SPAN lang=EN-US>
int const & a=b; 和指针一?SPAN lang=EN-US>
const int& a=b; 和指针一?SPAN lang=EN-US>
但没?SPAN lang=EN-US> int& const a=b 的用法因为引用不能做UMq算Q但只是Zwarning 

const int* const a = &b; //
l合应用,一般用来传递多l的数组
cdQ?SPAN lang=EN-US>char* init[] = {"Paris","in the","Spring"};
void fun(const int* const a){}
fun(init)//保护参数不被修改 

int A(int)const; //
是常函数Q只能用在类中,调用它的对象不能Ҏ变成员?SPAN lang=EN-US>
const int A(); //q回的是帔R,所以必这么调?SPAN lang=EN-US> cosnt int a=A();
int A(const int); //参数不能改?SPAN lang=EN-US>,可用在Q意函?SPAN lang=EN-US>
int A(const int*);
....
int height() const;//常函数只能由常函数调?SPAN lang=EN-US>
int max(int,int) const;
int Max = max(height(),height()); 

const int* pHeap = new int;
delete pHeap;
p = NULL;//出错
我的解决办法是强制类型{?SPAN lang=EN-US>
const int* pHeap = new int(1);
delete (int*)pHeap;
pHeap = NULL; 

一?SPAN lang=EN-US>const 和引用联合用的时候要注意 

const int a = 1; 
const int& ref1 = a;
const int& ref2 = 1; 

ref1
?SPAN lang=EN-US> ref2 都是正确的,但是他们引用的内容和一般的引用不同
?SPAN lang=EN-US> const int& ref1 = a; 而言Q其实这?SPAN lang=EN-US> ref1 已经?SPAN lang=EN-US> a 没有M关系?SPAN lang=EN-US>
ref1 实际上是对一个旉的引用。同?SPAN lang=EN-US> const int& ref2 = 1; 也是?SPAN lang=EN-US>
一个旉做的引用。当引用临时量是 C++ 的隐式类型{换可以v作用?SPAN lang=EN-US>
临时量的生存期和引用量的生存期相同?SPAN lang=EN-US> 

二、强?SPAN lang=EN-US>const对象可能D无定义行?SPAN lang=EN-US> 

对于优化做的比较好的~译器,代码 const int i = 1;
当后面用到变?SPAN lang=EN-US> i 的时候,~译器会优化掉对 i 的存取,而直接用立x

const int i = 1; 

*(const_cast<int*>(&i)) = 2;
cout << *(int*)&i << endl;
cout << i << endl; 

所以,?SPAN lang=EN-US> const 对象?SPAN lang=EN-US> const_cast 可能D无定义行?SPAN lang=EN-US>
目前我就遇到q些问题Q那位还有补充的?SPAN lang=EN-US> 





能不能把自己的经验也谈谈。大家交交?SPAN lang=EN-US>
q个是我在调错时发现的
int height() const;//
常函数只能由常函数调?SPAN lang=EN-US>
int max(int,int) const;
int Max = max(height(),height()); 





Thinking again in C++Q一Q常量性原?SPAN lang=EN-US> cphjQ原作) 
有些地方很受启发 


1.
不能?SPAN lang=EN-US>const修饰的Q何对象、引用和指针作ؓ赋D辑ּ的左倹{?SPAN lang=EN-US>
const int cx=100;
const int & rcx=cx;
const int * pcx=&cx;
cx=200; //error
rcx=200; //error
*pcx=200; //error 

2.constcd的对象不能直接被non-constcd的别名所引用?SPAN lang=EN-US>
(1)不能?SPAN lang=EN-US>constcd的对象传递给non-constcd的引用?SPAN lang=EN-US>
const int cx=100;
int & rx=cx; //error
(2)不能?SPAN lang=EN-US>constcd的实参传递给形参?SPAN lang=EN-US>non-constcd引用的函数?SPAN lang=EN-US>
void f(int a)
{
}
void g(int & ra)
{
}
const int cx=100;
f(cx); //ok
g(cx); //error
(3)不能?SPAN lang=EN-US>constcd的对象作?SPAN lang=EN-US>non-constcd引用的函数返回倹{?SPAN lang=EN-US>
int & f(const int & rca)
{
return rca; //error
}
int x=100;
f(x); 

3.可以使用constcd别名引用non-const对象。此旉过const引用不能修改对象Q但对象可以通过non-const引用被修攏V?SPAN lang=EN-US>
int x=100;
int & rx=x;
const int & rcx=x; //ok
x=200;
rx=200;
rcx=200; //error 

4.指针的属性有两个Q指针的cd和指针本w的帔R性。其中,指向const对象与指?SPAN lang=EN-US>non-const对象Q是不同的指针类型?SPAN lang=EN-US>
int x=100;
const int * pcx=&x; //[1]
int * px=&x; //[2]
int y=100;
int * const cpy=&y; //[3]
int * py=&y; //[4]
[1][2]两个指针的类型不同;[3][4]两个指针的常量性不同?SPAN lang=EN-US>
对象与指向对象的指针的规则类g对象与引用。即Q?SPAN lang=EN-US>constcd的对象不能直接被non-constcd的指针所指示Q同2Q;可以使用constcd的指针指?SPAN lang=EN-US>non-const对象Q同3Q?SPAN lang=EN-US> 

5.可以相同类型(包括帔R性)?SPAN lang=EN-US>const指针Dl?SPAN lang=EN-US>non-const指针?SPAN lang=EN-US>
int x=100;
int * px;
const int * pcx=&x;
px=pcx; //error
int * const cpx=&x;
px=cpx; //ok 

6.若函数的q回gؓ内徏cd或是指针Q则该返回D动成?SPAN lang=EN-US>const性质。但自定义类型则?SPAN lang=EN-US>non-const性质?SPAN lang=EN-US>
int f() //相当于返?SPAN lang=EN-US>const int
{
return 100;
}
int * g(int & ra) //相当于返?SPAN lang=EN-US>int * const
{
return &ra;
}
class CTest
{
int n;
public:
CTest(int n){this->n=n;}
};
CTest h() //q回的就?SPAN lang=EN-US>CTest
{
return CTest(200);


f()=200; //error 

int x=100;
int y=200;
int * px=&x;
g(y)=px; //error
*g(y)=x; //okQ从q点可以看出g()q回的不?SPAN lang=EN-US>const int * 

CTest t(100);
h()=t; //okQ但却是完全错误的、危险的做法
//
所?SPAN lang=EN-US>h()的正写法是q回const CTest





const int b=100; b
的内容不?SPAN lang=EN-US>,b只能?SPAN lang=EN-US>100
int const b=100; b必须?SPAN lang=EN-US>int?SPAN lang=EN-US>,不能为其他类?SPAN lang=EN-US>?
q?SPAN lang=EN-US>2句话的意思应该是一L?Q?SPAN lang=EN-US> THINKING IN C++是这栯?SPAN lang=EN-US>





const int a=100; a的内容不?SPAN lang=EN-US>,a只能?SPAN lang=EN-US>100Q同样不能类型{换)?SPAN lang=EN-US>
int const b=100; b必须?SPAN lang=EN-US>int?SPAN lang=EN-US>,不能为其他类?SPAN lang=EN-US>?Q同样在使用中不能修改)?SPAN lang=EN-US>
所?SPAN lang=EN-US>a?SPAN lang=EN-US>b是一LQ称为整型常敎ͼ在用中不能被修改,当然都不能{为其他类型了?SPAN lang=EN-US> 
#include <iostream> 

using namespace std; 

int main()
{
  const int a = 100;
  int const b = 100; 

  a = 100; //q四条语句编译时都会出现?SPAN lang=EN-US>Cannot modify a const object 
b = 100; //in function main()”的错误提示Q也是_M企图修改   a = 100.0; //a?SPAN lang=EN-US>bQ其实是一LQ的行ؓ都会出现“灾䏀,在语法上讲就  b = 100.0; //?SPAN lang=EN-US>a?SPAN lang=EN-US>b都不能出现在赋D句的左边Q?SPAN lang=EN-US> 

  cout<<'\n'<<a<<'\n'<<b<<endl; 

  return 0;
}





常函数的调用是这LQ常量对象只能调用常成员函数Q非帔R对象卛_以调常成员函敎ͼ也可以调一般成员函敎ͼ但当某个函数?SPAN lang=EN-US>const和非const两个版本Ӟconst对象?SPAN lang=EN-US>const版本Q非const对象调非const版本
例:
class A
{
public:
int & GetData(){return data;}
const int & GetData()const {return data;}
private:
int data;

A a;
a.GetData();//
调用int & GetData(){return data;}
//
但如果没有这个函敎ͼ也可以调?SPAN lang=EN-US>const int & GetData()const 
const A const_a;
const_a.GetData();//调用const int & GetData()const {return data;}
常函数只能调常函敎ͼ也是׃q个原因





你狠!加两?SPAN lang=EN-US>

一?SPAN lang=EN-US>const 和引用联合用的时候要注意 

const int a = 1; 
const int& ref1 = a;
const int& ref2 = 1; 

ref1
?SPAN lang=EN-US> ref2 都是正确的,但是他们引用的内容和一般的引用不同
?SPAN lang=EN-US> const int& ref1 = a; 而言Q其实这?SPAN lang=EN-US> ref1 已经?SPAN lang=EN-US> a 没有M关系?SPAN lang=EN-US>
ref1 实际上是对一个旉的引用。同?SPAN lang=EN-US> const int& ref2 = 1; 也是?SPAN lang=EN-US>
一个旉做的引用。当引用临时量是 C++ 的隐式类型{换可以v作用?SPAN lang=EN-US>
临时量的生存期和引用量的生存期相同?SPAN lang=EN-US> 

二、强?SPAN lang=EN-US>const对象可能D无定义行?SPAN lang=EN-US> 

对于优化做的比较好的~译器,代码 const int i = 1;
当后面用到变?SPAN lang=EN-US> i 的时候,~译器会优化掉对 i 的存取,而直接用立x

const int i = 1; 

*(const_cast<int*>(&i)) = 2;
cout << *(int*)&i << endl;
cout << i << endl; 

所以,?SPAN lang=EN-US> const 对象?SPAN lang=EN-US> const_cast 可能D无定义行?SPAN lang=EN-US>





#include <iostream.h>
void fun(char b){cout <<"void"<<endl;}
int fun(int const b){cout <<"int"<<endl;} 
int main()
{
fun(1.0);//详细看看重蝲函数?SPAN lang=EN-US> 
fun(4); //想一惌用哪一?SPAN lang=EN-US> 

return 0;
}
我试了一下,会出?SPAN lang=EN-US>? vc_'fun':ambiguous call to overloaded function 





补充的好啊,q个一般不会注意的
const int i = 1;
*(const_cast<int*>(&i)) = 2;
cout << *(int*)&i << endl;
cout << i << endl;
q个可真有意思,调试时两个都?SPAN lang=EN-US>2Q可~译是2Q?SPAN lang=EN-US>1?SPAN lang=EN-US>
const的永q都?SPAN lang=EN-US>const,q样能更改就不错了,不然p相矛盾了
奇怪的?SPAN lang=EN-US> pi ?SPAN lang=EN-US> &i地址一样啊Q就像楼上说的这是编译时的优?SPAN lang=EN-US>
处理
const int i = 1;
int* pi=const_cast<int*>(&i);
*pi=2;
cout << *pi << endl;
cout << i << endl; 





那个主要是隐式{?SPAN lang=EN-US>
你可依次把两个函数注掉看看调?SPAN lang=EN-US>
#include <iostream.h>
//void fun(char b){cout <<"void"<<endl;}
void fun(int b){cout <<"int"<<endl;}
int main()
{
fun('a');
fun(4); 
return 0;
}

]]>
操作内存 http://www.shnenglu.com/ivenher/articles/1069.html爱饭?/dc:creator>爱饭?/author>Fri, 11 Nov 2005 05:11:00 GMThttp://www.shnenglu.com/ivenher/articles/1069.htmlhttp://www.shnenglu.com/ivenher/comments/1069.htmlhttp://www.shnenglu.com/ivenher/articles/1069.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/1069.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/1069.html
操作内存

在前面的章节中,我们已经了解了寄存器的基本用方法。而正如结提到的那样Q仅仅用寄存器做一点运是没有什么太大意义的Q毕竟它们不能保存太多的数据Q因此,对编Eh员而言Q他肯定q切地希望访问内存,以保存更多的数据?/P>

我将分别介绍如何在保护模式和实模式操作内存,然而在此之前,我们先熟悉一下这两种模式中内存的l构?/P>

3.1 实模?/H3>

事实上,在实模式中,内存比保护模式中的结构更令h困惑。内存被分割成段Qƈ且,操作内存Ӟ需要指定段和偏U量。不q,理解q些概念是非常容易的事情。请看下面的图:

D?寄存器这U格局是早期硬件电路限制留下的一个伤疤。地址ȝ在当时有20-bit?/P>

然?0-bit的地址不能攑ֈ16-bit的寄存器里,q意味着?-bit必须攑ֈ别的地方。因此,Z讉K所有的内存Q必M用两?6-bit寄存器?/P>

q一设计上的折衷ҎD了今天的D?偏移量格局。最初的设计中,其中一个寄存器只有4-bit有效Q然而ؓ了简化程序,两个寄存器都?6-bit有效Qƈ在执行时求出加权和来标识20-bit地址?/P>

偏移量是16-bit的,因此Q一个段?4KB。下面的囑֏以帮助你理解20-bit地址是如何Ş成的Q?/P>

D?偏移量标识的地址通常记做 D?偏移?的Ş式?/P>

׃q样的结构,一个内存有多个对应的地址。例如,0000:0010?001:0000指的是同一内存地址。又如,

0000:1234 = 0123:0004 = 0120:0034 = 0100:0234
0001:1234 = 0124:0004 = 0120:0044 = 0100:0244

作ؓ负面影响之一Q在D上?相当于在偏移量上?6Q而不是一个“全新”的Dc反之,在偏U量上加16也和在段上加1{h。某些时候,据此认ؓD늚“粒度”是16字节?/P>

l习?BR>试一下将下面的地址转化?0bit的地址Q?/P>
2EA8:D678 26CF:8D5F 453A:CFAD 2933:31A6 5924:DCCF
694E:175A 2B3C:D218 728F:6578 68E1:A7DC 57EC:AEEA

E高一些的要求是,写一个程序将DؓAX、偏U量为BX的地址转换?0bit的地址Qƈ保存于EAX中?/P>

[上面习题的答?/SPAN>]

我们现在可以写一个真正的E序了?/P>

l典E序QHello, world

;;; 应该得到一?9字节?com文g

.MODEL TINY
.CODE

CR equ 13
LF equ 10
TERMINATOR equ '$'

ORG 100h

Main PROC
mov dx,offset sMessage
mov ah,9
int 21h
mov ax,4c00h
int 21h
Main ENDP

sMessage:
  DB 'Hello, World!'
  DB CR,LF,TERMINATOR

END Main


; .COM文g的内存模型是‘TINY?BR>; 代码D开?BR>
; 回R
; 换行
; DOS字符串结束符

; 代码起始地址为CS:0100h


; 令DS:DX指向Message
; int 21h(DOS中断)功能9 -
; 昄字符串到标准输出讑֤
; int 21h功能4ch -
; l止E序q返回AL的错误代?BR> 

 


; E序l束的同时指定入口点为Main

那么Q我们需要解释很多东ѝ?/P>

首先Q作为汇~语a的抽象,C语言拥有“指针”这个数据类型。在汇编语言中,几乎所有对内存的操作都是由对给定地址的内存进行访问来完成的。这P在汇~语a中,l大多数操作都要和指针生或多或的联系?/P>

q里我想的是Q由于这一Ҏ,汇编语言中同样会出现CE序中常见的~冲区溢出问题。如果你正在设计一个与安全有关的系l,那么最好是仔细查你用到的每一个串Q例如,它们是否一定能够以你预期的方式l束Q以及(如果使用的话Q你的缓冲区是否能保证实际可能输入的数据不被写入到它以外的地斏V作Z个汇~语aE序员,你有义务查每一行代码的可用性?/P>

E序中的equ伪指令是宏汇~特有的Q它的意思接q于C或Pascal中的constQ常量)。多数情况下Qequ伪指令ƈ不ؓW号分配I间?/P>

此外Q汇~程序执行一Ҏ作是非常J琐的,通常Q在对与效率要求不高的地方,我们习惯使用pȝ提供的中断服务来完成d。例如本例中的中?1hQ它是DOS时代的中断服务,在Windows中,它也被认为是Windows API的一部分Q这一点可以在Microsoft的文档中查到Q。中断可以被理解为高U语a中的子程序,但又不完全一样——中断用系l栈来保存当前的机器状态,可以q件发P通过修改机器状态字来反馈信息,{等?/P>

那么Q最后一D通过DB存放的数据到底保存在哪里了呢Q答案是紧挨着代码存放。在汇编语言中,DB和普通的指o的地位是相同的。如果你的汇~程序ƈ不知道新的助记符Q例如,新的处理器上的CPUID指oQ,而你很清楚,那么可以用DB 机器码的方式写下指o。这意味着Q你可以越汇编器的能力撰写汇编E序Q然而,直接用机器码~程是几乎肯定是一件费力不讨好的事——汇~器厂商会经常更新它所支持的指令集以适应市场需要,而且Q你可以期待你的汇编其能够生正的代码Q因为机器查表是不会出错的。既然机器能够帮我们做将E序转换Z码这件事情,那么Z么不让它来做呢?

l心的读者不隑֏玎ͼ在程序中我们没有对DSq行赋倹{那么,q是否意味着E序的结果将是不可预的呢?{案是否定的。DOSQ或Windows中的MS-DOS VMQ在加蝲.com文g的时候,会对寄存器进行很多初始化?com文g被限制ؓ于64KBQ这P它的代码Dc数据段都被装入同样的数|卻I初始状态下DS=CSQ?/P>

也许会有Q“嘿Q这听v来不太好Q一?4KB的程序能做得了什么呢Q还有,你吹得天׃ؕ坠的堆栈D在什么地方?”那么,我们来看看下面这个新的Hello worldE序Q它是一个EXE文gQ在DOS实模式下q行?/P>
;;; 应该得到一?61 字节的EXE文g

.MODEL SMALL
.STACK 200h

CR equ 13
LF equ 10
TERMINATOR equ '$'

.DATA

Message DB 'Hello, World !'
  DB CR,LF,TERMINATOR

.CODE

Main PROC
mov ax, DGROUP
mov ds, ax

mov dx, offset Message
mov ah, 9
int 21h

mov ax, 4c00h
int 21h
Main ENDP

END main
 

; 采用“SMALL”内存模?BR>; 堆栈D?/FONT>

; 回R
; 换行
; DOS字符串结束符

; 定义数据D?/FONT>

; 定义昄?/FONT>

; 定义代码D?/FONT>


; 数据段
; 加蝲到DS寄存?/FONT>

; 讄DX
; 昄


; l止E序

561字节Q实现相同功能的E序大了q么多!Z么呢Q我们看刎ͼE序拥有了完整的堆栈Dc数据段、代码段Q其中堆栈段占掉?12字节Q其余的基本上没什么变化?/P>

分成多个D|什么好处呢Q首先,它让E序昑־更加清晰——你肯定更愿意看一个结构清楚的E序Q代码中hard-coded的字W串、数据让得费解。比如,mov dx, 0152h肯定不如mov dx, offset Message来的亲切。此外,通过分段你可以用更多的内存Q比如,代码D腾出的I间可以做更多的事情。exe文g另一个吸引h的地Ҏ它能够实现“重定位”。现在你不需要指定程序入口点的地址了,因ؓpȝ会找C的程序入口点Q而不是死板的100h?/P>

E序中的W号也会在系l加载的时候重新赋予新的地址。exeE序能够保证你的设计Ҏ地被实现Q不需要考虑太多的细节?/P>

当然Q我们的主要目的是将汇编语言作ؓ高语言的一个有用的补充。如我在开始提到的那样Q真正完全用汇编语言实现的程序不一定就好,因ؓ它不便于l护Q而且Q由于结构的原因Q你也不太容易确保它是正的Q汇~语a是一U非l构化的语言Q调试一个精心设计的汇编语言E序Q即使对于一个老手来说也不L一场恶梦,因ؓ你很可能掉到别h预设的“陷阱”中——这些技巧确实提高了代码性能Q然而你很可能不理解它,于是你把它改掉,接着发现程序彻底|掉了。用汇~语a加强高语言E序Ӟ你要做的通常只是使用汇编指oQ而不必搭建完整的汇编E序。绝大多敎ͼ也是目前我遇到的全部QC/C++~译器都支持内嵌汇编Q即在程序中使用汇编语言Q而不必撰写单独的汇编语言E序——这可以节省你的不少_֊Q因为前面讲q的那些伪指令,如equ{,都可以用你熟悉的高语言方式来编写,~译器会把它转换为适当的Ş式?/P>

需要说明的是,在高U语a中一定要注意~译l果。编译器会对你的汇编E序做一些修改,q不一定符合你的要求(附带说一句,有时~译器会很聪明地调整指o序来提高性能Q这U情况下最好测试一下哪U写法的效果更好Q,此时需要做一些更深入的修改,或者用db来强制编码?/P>

3.2 保护模式

实模式的东西说得太多了,管我已l删掉了许多东西Qƈ把一些原则性的问题拿到了这一节讨论。这样做不是没有理由的——保护模式才是现在的E序Q除了操作系l的底层启动代码Q最常用的CPU模式。保护模式提供了很多令h耳目一新的功能Q包括内存保护(q是保护模式q个名字的来源)、进E支持、更大的内存支持Q等{?/P>

对于一个编Eh员来_能“偷懒”是一件o人愉快的事情。这里“偷懒”是说把“应该”由pȝ做的事情做的事情全都交给pȝ。ؓ什么呢Q这一个基本思想——hL犯错误的时候,然而规则不会,正确C解规则之后,你可以期待它像你所了解的那h行。对于CE序来说Q你自己用C语言写的实现相同功能的函数通常没有pȝ提供的函数性能好(除非你用了比函数库好很多的算法)Q因为系l的函数往往使用了更好的优化Q甚臛_能不是用C语言直接~写的?/P>

当然Q“偷懒”的意思是_把那些应该让机器做的事情交给计算机来做,因ؓ它做得更好。我们应该把_֊集中到设计算法,而不是编写源代码本n上,因ؓ~译器几乎只能做{h优化Q而实现相同功能,但用更好算法的E序实现Q则几乎只能׃h自己完成?/P>

举个例子Q这样一个函敎ͼ

int fun(){
  int a=0;
  register int i;
  for(i=0; i<1000; i++) a+=i;
  return a;
}

在某U编译模式[DEBUG]下被~译?/P>
push ebp
mov ebp,esp
sub esp,48h
push ebx
push esi
push edi
lea edi,[ebp-48h]
mov ecx,12h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
mov dword ptr [ebp-4],0
mov dword ptr [ebp-8],0
jmp fun+31h
mov eax,dword ptr [ebp-8]
add eax,1
mov dword ptr [ebp-8],eax
cmp dword ptr [ebp-8],3E8h
jge fun+45h
mov ecx,dword ptr [ebp-4]
add ecx,dword ptr [ebp-8]
mov dword ptr [ebp-4],ecx
jmp fun+28h
mov eax,dword ptr [ebp-4]
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
; 子程序入?/FONT>


; 保护现场


; 初始化变?调试版本Ҏ?BR>; 本质是在堆中挖一块地儿,存CCCCCCCC?BR>; 用串操作q行Q这发挥Intel处理器优?BR>; ‘a=0?BR>; ‘i=0?/FONT>

; 走着
; i++


; i<1000?


; a+=i;

; return a;

; 恢复现场

 

; q回

而在另一U模式[RELEASE/MINSIZE]下却被编译ؓ

xor eax,eax
xor ecx,ecx
add eax,ecx
inc ecx
cmp ecx,3E8h
jl fun+4
ret

; a=0;
; i=0;
; a+=i;
; i++;
; i<1000?
; ?>l箋l箋
; return a

如果让我来写Q多半会写成

mov eax, 079f2ch
ret

; return 499500

Z么这样写呢?我们看到Qi是一个外界不能媄响、也无法L的内部状态量。作D늨序来_对它的计对于结果ƈ没有直接的媄响——它的存在不q是方便法描述而已。ƈ且我们看到的Q这D늨序实际上无论执行多少ơ,其结果都不会发生变化Q因此,直接q回计算l果可以了Q计是多余的(如果说一定要,那么应该是编译器在编译过E中完成它)?/P>

更进一步,我们甚至希望~译器能够直接把q个函数变成一个符号常量,q样q操作堆栈的q程也省掉了?/P>

W三U结果属于“等效”代码,而不是“等价”代码。作为用P很多时候是希望~译器这样做的,然而由于目前的技术尚不成熟,有时q种做法会造成一些问题(gcc和g++的顶U优化可以造成~译出的FreeBSD内核行ؓ异常Q这是我在FreeBSD上遇到的唯一一ơY件原因的kernel panicQ,因此Qƈ不是所有的~译器都q样做(另一斚w的原因是Q如果编译器在这斚w做的太过火,例如自动求解全部“固定”问题,那么如果你的E序是解军_定的问题“很大”,如求解迷宫,那么在编译过E中你就会找锤子来砸计算ZQ。然而,作ؓ~译器制造商Qؓ了提高自q产品的竞争力Q往往会用第三种代码来做函数库。正如前面所提到的那Pq种优化往往不是~译器本w的作用Q尽现代编译程序拥有编译执行、@环代码外提、无用代码去除等诸多优化功能Q但它都不能保证E序最优。最后一U代码恐怕很有~译器能够做刎ͼ不信你可以用自己常用的编译器加上各种优化选项试试:)

发现什么了吗?三种代码中,对于内存的访问一个比一个少。这样做的理由是Q尽可能地利用寄存器q减对内存的访问,可以提高代码性能。在某些情况下,使代码既又快是可能的?/P>

书归正传Q我们来说说保护模式的内存模型。保护模式的内存和实模式有很多共同之处?/P>

毫无疑问Q以'protected mode'(保护模式), 'global descriptor table'(全局描述W表), 'local descriptor table'(本地描述W表)?selector'(选择?搜烦Q你会得到完整介l它们的大量信息?/P>

保护模式与实模式的内存类|然而,它们之间最大的区别是保护模式的内存是“线性”的?/P>

新的计算ZQ?2-bit的寄存器已经不是什么新鲜事Q如果你哪天听说你的CPU的寄存器不是32-bit的,那么它——简直可以肯定地说——的字长要比32-bitq要多。新的个人机上已l开始逐步采用64-bit的CPU了)Q换a之,实际上段/偏移量这一格局已经不再需要了。尽如此,在l看保护模式内存l构Ӟ仍请CD?偏移量的概念。不妨把D寄存器看作对于保护模式中的选择器的一个模拟。选择器是全局描述W表(Global Descriptor Table, GDT)或本地描q符?Local Descriptor Table, LDT)的一个指针?/P>

如图所C,GDT和LDT的每一个项目都描述一块内存。例如,一个项目中包含了某块被描述的内存的物理的基地址、长度,以及其他一些相关信息?/P>

保护模式是一个非帔R要的概念Q同时也是目前撰写应用程序时Q最常用的CPU模式Q运行在新的计算Z的操作系l很有在实模式下运行的Q?/P>

Z么叫保护模式呢?它“保护”了什么?{案是进E的内存。保护模式的主要目的在于允许多个q程同时q行Qƈ保护它们的内存不受其他进E的늊。这有点cM于C++中的机制Q然而它的强制力要大得多。如果你的进E在保护模式下以不恰当的方式讉K了内存(例如Q写了“只诠Z内存,或读了不可读的内存,{等Q,那么CPU׃产生一个异常。这个异常将交给操作pȝ处理Q而这U处理,假如你的E序没有特别说明操作pȝ该如何处理的话,一般就是杀掉做错了事情的进E?/P>

我像q样的对话框大家一定非常熟悉(临时写了一个程序故意造成的错误)Q?/P>

好的Q只是一个程序崩溃了Q而操作系l的其他q程照常q行Q同LE序在DOS中几乎是板上钉钉的死机,因ؓNULL指针的位|恰好是中断向量表)Q你甚至q可以调试它?/P>

保护模式q有其他很多好处Q在此就不一一赘述了。实模式和保护模式之间的切换问题我打放在后面的“高U技巧”一章来Ԍ因ؓ多数E序q不涉及q个?/P>

了解了内存的格局Q我们就可以q入下一节——操作内存了?/P>

3.3 操作内存

前两节中Q我们介l了实模式和保护模式中用的不同的内存格局。现在开始解释如何用这些知识?/P>

回忆一下前面我们说q的Q寄存器可以用作内存指针。现在,是他们发挥作用的时候了?/P>

可以内存想象ؓ一个顺序的字节。用指针,可以L地操作(dQ内存?/P>

现在我们需要一些其他的指o格式来描q对于内存的操作。操作内存时Q首先需要的是它的地址?/P>

让我们来看看下面的代码:

mov ax,[0]

Ҏ可C,里面的表辑ּ指定的不是立xQ而是偏移量。在实模式中QDS:0中的那个字(16-bit长)被装入AX?/P>

然?是一个常敎ͼ如果需要在q行的时候加以改变,需要一些特D的技巧,比如E序自修攏V汇~支持这个特性,然而我个hq不推荐q种Ҏ——自修改大大降低E序的可L,q且q降低稳定性,性能q不一定好。我们需要另外的技术?/P>
mov bx,0
mov ax,[bx]

看v来舒服了一些,不是吗?BX寄存器的内容可以随时更改Q而不需要用冗长的代码去修改自nQ更不用担心由此带来的不E_问题?/P>

同样的,mov指o也可以把数据保存到内存中Q?/P>
mov [0],ax

在存储器与寄存器之间交换数据应该_清楚了?/P>

有些时候我们会需要操作符来描q内存数据的宽度Q?/P>
操作W?/FONT> 意义
byte ptr 一个字?8-bit, 1 byte)
word ptr 一个字(16-bit)
dword ptr 一个双?32-bit)

例如Q在DS:100h处保?234hQ以字存放:

mov word ptr [100h],01234h

于是我们mov指o扩展为:

mov reg(8,16,32), mem(8,16,32)
mov mem(8,16,32), reg(8,16,32)
mov mem(8,16,32), imm(8,16,32)

需要说明的是,加减同样也可以在[]中用,例如Q?/P>
mov ax,[bx+10]
mov ax,[bx+si]
mov ax,es:[di+bp]

{等。我们看刎ͼ对于内存的操作,即使用MOV指oQ也有许多种可能的方式。下一节中Q我们将介绍如何操作丌Ӏ?/P>

感谢 |友 水杉 指出此答案中的一处错误?BR>感谢 Heallven 指出.COME序实例~译p|的问?/P>

2EA8:D678 -> 物理?3C0F8
694E:175A -> 物理?6AC4A
26CF:8D5F -> 物理?2FA4F
2B3C:D218 -> 物理?385E8
453A:CFAD -> 物理?5235D
728F:6578 -> 物理?78E68
2933:31A6 -> 物理?2C4D6
68E1:A7DC -> 物理?735FC

~程
shl eax,4
add eax,bx



]]>使用寄存?http://www.shnenglu.com/ivenher/articles/1068.html爱饭?/dc:creator>爱饭?/author>Fri, 11 Nov 2005 05:09:00 GMThttp://www.shnenglu.com/ivenher/articles/1068.htmlhttp://www.shnenglu.com/ivenher/comments/1068.htmlhttp://www.shnenglu.com/ivenher/articles/1068.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/1068.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/1068.html使用寄存?/FONT>


2.2 使用寄存?/H3>

在前一节中的x86基本寄存器的介绍Q对于一个汇~语a~程人员来说是不可或~的。现在你知道Q寄存器是处理器内部的一些保存数据的存储单元。仅仅了解这些是不以写Z个可用的汇编语言E序的,但你已经可以大致L一般汇~语aE序了(不必惊讶Q因为汇~语a的祝记符和英文单词非常接q)Q因Z已经了解了关于基本寄存器的绝大多数知识?/P>

在正式引入第一个汇~语aE序之前Q我_略Cl一下汇~语a中不同进制整数的表示Ҏ。如果你不了解十q制以外的其他进Ӟh鼠标Ud?SPAN class=tip id=oRadixes>q里?/P>

  汇编语言中的整数帔R表示
  • 十进制整?BR>q是汇编器默认的数制。直接用我们熟悉的表C方式表C即可。例如,1234表示十进制的1234。不q,如果你指定了使用其他数制Q或者有凡事都进行完整定义的爱好,也可以写成[十进制数]d或[十进制数]D的Ş式?
  • 十六q制?BR>q是汇编E序中最常用的数Ӟ我个人比较偏׃用十六进制表C数据,至于Z么,以后我会作说明。十六进制数表示?[十六q制数]h?[十六q制数]HQ其中,如果十六q制数的W一位是数字Q则开头的0可以省略。例如,7fffh, 0ffffhQ等{?
  • 二进制数
    q也是一U常用的数制。二q制数表CZؓ[二进制数]b或[二进制数]B。一般程序中用二q制数表C掩码(mask codeQ等数据非常的直观,但需要些很长的数据(4位二q制数相当于一位十六进制数Q。例如,1010110b?
  • 八进制数
    八进制数现在已经不是很常用了Q确实还在用Q一个典型的例子是Unix的文件属性)。八q制数的形式是[八进制数]q、[八进制数]Q、[八进制数]o、[八进制数]O。例如,777Q?/LI>

需要说明的是,q些Ҏ是针对宏汇编器(例如QMASM、TASM、NASMQ说的,调试器默认用十六进制表C整敎ͼq且不需要特别的声明Q例如,在调试器中直接用FFFF表示十进制的65535Q用10表示十进制的16Q?/P>

现在我们来写一段汇编E序Q修改EAX、EBX、ECX、EDX的数倹{?/P>

我们假定E序执行之前Q寄存器中的数值是?Q?/P>
  ? X
H L
EAX 0000 00 00
EBX 0000 00 00
ECX 0000 00 00
EDX 0000 00 00

正如前面提到的,EAX的高16bit是没有办法直接访问的Q而AX对应它的?6bitQAH、AL分别对应AX的高、低8bit?/P>
mov eax, 012345678h
mov ebx, 0abcdeffeh
mov ecx, 1
mov edx, 2
; ?12345678h送入eax
; ?abcdeffeh送入ebx
; ?00000001h送入ecx
; ?00000002h送入edx

则执行上q程序段之后Q寄存器的内容变为:

  ? X
H L
EAX 1234 56 78
EBX abcd ef fe
ECX 0000 00 01
EDX 0000 00 02

那么Q你已经了解了movq个指oQmov是move的羃写)的一U用法。它可以数送到寄存器中。我们来看看下面的代码:

mov eax, ebx
mov ecx, edx
; ebx内容送入eax
; edx内容送入ecx

则寄存器内容变ؓQ?/P>
  ? X
H L
EAX abcd ef fe
EBX abcd ef fe
ECX 0000 00 02
EDX 0000 00 02

我们可以看到Q“move”之后,数据依然保存在原来的寄存器中。不妨把mov指o理解为“送入”,或“装入”?/P>

l习?/B>

把寄存器恢复成都为全0的状态,然后执行下面的代码:

mov eax, 0a1234h
mov bx, ax
mov ah, bl
mov al, bh
; ?a1234h送入eax
; ax的内定w入bx
; bl内容送入ah
; bh内容送入al

思考:此时QEAX的内容将是多?[{案]

下面我们介l一些指令。在介绍指o之前Q我们约定:


   使用Intel文档中的寄存器表C方?/B>

  • reg32 32-bit寄存器(表示EAX、EBX{)
  • reg16 16-bit寄存器(?2位处理器中,qAX、BX{)
  • reg8  8-bit寄存器(表示AL、BH{)
  • imm32 32-bit立即敎ͼ可以理解为常敎ͼ
  • imm16 16-bit立即?
  • imm8  8-bit立即?/LI>

在寄存器中蝲入另一寄存器,或立x的|

mov reg32, (reg32 | imm8 | imm16 | imm32)
mov reg32, (reg16 | imm8 | imm16)
mov reg8, (reg8 | imm8)

例如Qmov eax, 010h表示Q在eax中蝲?0000010h。需要注意的是,如果你希望在寄存器中装入0Q则有一U更快的ҎQ在后面我们提到?/P>

交换寄存器的内容Q?/P>
xchg reg32, reg32
xchg reg16, reg16
xchg reg8, reg8

例如Qxchg ebx, ecxQ则ebx与ecx的数值将被交换。由于系l提供了q个指oQ因此,采用其他Ҏ交换Ӟ速度会较慢Qƈ需要占用更多的存储I间Q编E时要避免这U情况,卻I量利用pȝ提供的指令,因ؓ多数情况下,q意味着更小、更快的代码Q同时也杜绝了错误(如果说Intel的CPU在交换寄存器内容的时候也会出错,那么它就不用卖CPU了。而对于你来说Q检查一行代码的正确性也昄比检查更多代码的正确性要ҎQ刚才的习题的程序用下面的代码将更有效:

mov eax, 0a1234h
mov bx, ax
xchg ah, al
; ?a1234h送入eax
; ax内容送入bx
; 交换ah, al的内?/FONT>

递增或递减寄存器的|

inc reg(8,16,32)
dec reg(8,16,32)

q两个指令往往用于循环中对指针的操作。需要说明的是,某些时候我们有更好的方法来处理循环Q例如用loop指oQ或rep前缀。这些将在后面的章节中介l?/P>

寄存器的数g另一寄存器,或立x的值相加,q存回此寄存器:

add reg32, reg32 / imm(8,16,32)
add reg16, reg16 / imm(8,16)
add reg8, reg8 / imm(8)

例如Qadd eax, edxQ将eax+edx的值存入eax。减法指令和加法cMQ只是将add换成sub?/P>

需要说明的是,与高U语a不同Q汇~语a中,如果要计两C和(差、积、商Q或一般地_q算l果Q,那么必然有一个寄存器被用来保存结果。在PASCAL中,我们可以用nA := nB + nC来让nA保存nB+nC的结果,然而,汇编语言q不提供q种Ҏ。如果你希望保持寄存器中的结果,需要用另外的指令。这也从另一个侧面反映了“寄存器”这个名字的意义。数据只是“寄存”在那里。如果你需要保存数据,那么需要将它放到内存或其他地方?/P>

cM的指令还有and、or、xorQ与Q或Q异或){等。它们进行的是逻辑q算?/P>

我们Uadd、mov、sub、and{称Zؓ指o助记W(q么叫是因ؓ它比机器语言Ҏ记忆Q而v作用是方便忆,某些资料中也UCؓ指o、操作码、opcode[operation code]{)Q后面的参数成ؓ操作敎ͼ一个指令可以没有操作数Q也可以有一两个操作敎ͼ通常有一个操作数的指令,q个操作数就是它的操作对象;而两个参数的指oQ前一个操作数一般是保存操作l果的地方,而后一个是附加的参数?/P>

我不打算在这份教E中用大量的幅介绍指o——很多h做得比我更好Q而且指o本nq不是重点,如果你学会了如何l织语句Q那么只要稍加学习就能轻易掌握其他指令。更多的指o可以参?A >Intel提供的资料。编写程序的时候,也可以参考一些在U参考手册。Tech!Help和HelpPC 2.10管已经很旧Q但以应付l大多数需要?/P>

聪明的读者也许已l发玎ͼ使用sub eax, eaxQ或者xor eax, eaxQ可以得Cmov eax, 0cM的效果。在高语言中,你大概不会选择用a=a-a来给a赋|因ؓ试会告诉你q么做更慢,直就是在自找ȝQ然而在汇编语言中,你会得到相反的结论,多数情况下,以由快到慢的速度排列Q这三条指o是xor eax, eax、sub eax, eax和mov eax, 0?/P>

Z么呢Q处理器在执行指令时Q需要经q几个不同的阶段Q取指、译码、取数、执行?/P>

我们反复Q寄存器是CPU的一部分。从寄存器取敎ͼ光度很显然要比从内存中取数快。那么,不难理解Qxor eax, eax要比mov eax, 0更快一些?/P>

那么Qؓ什么a=a-a通常要比a=0慢一些呢Q这和编译器的优化有一定关pR多数编译器会把a=a-a译成类g面的代码(通常Q高U语a通过ebp和偏U量来访问局部变量;E序中,x为a相对于本地堆的偏U量Q在只包含一?2-bit整Ş变量的程序中Q这个值通常?)Q?/P>

mov eax, dword ptr [ebp-x]
sub eax, dword ptr [ebp-x]
mov dword ptr [ebp-x],eax

而把a=0译?/P>

mov dword ptr [ebp-x], 0

上面的翻译只是示意性的Q略M很多必要的步骤,如保护寄存器内容、恢复等{。如果你对与~译E序的实现过E感兴趣Q可以参考相应的书籍。多数编译器Q特别是C/C++~译器,如Microsoft Visual C++Q都提供了从源代码到宏汇~语aE序的附加编译输出选项。这U情况下Q你可以很方便地了解~译E序执行的输出结果;如果~译E序没有提供q样的功能也没有关系Q调试器会让你看到编译器的编译结果?/P>

如果你明地知道~译器编译出的结果不是最优的Q那可以着手用汇编语言来重写那D代码了。怎么认是否应该用汇~语a重写呢?


  使用汇编语言重写代码之前需要确认的几g事情
  • 首先Q这U优?B>最?/B>?B>明显的效?/B>。比如,一D@环中的计,{等。一条语句的执行旉是很短的Q现在新的CPU的指令周期都?.000000001s以下QIntel甚至已经做出?GHz主频Q主频的倒数是时钟周期)的CPUQ如果你的代码自始至l只执行一ơ,q且你只是减了几个旉周期的执行时_那么改变是无法让h察觉的;很多情况下,q种“优化”ƈ不被提倡,管它确实减了执行旉Q但为此需要付出大量的旉、h力,多数情况下得不偿失(极端情况Q比如你的设备内存h格非常昂늚时候,q种优化也许会有意义Q?
  • 其次Q确认你已经使用?B>最好的法Qƈ且,你优化的E序的实现是正确的。汇~语a能够提供同样法的最快实玎ͼ然而,它ƈ不是万金油,更不是解决一切的灵丹妙药。用高语言实现一U好的算法,不一定会比汇~语a实现一U差的算法更慢。不q需要注意的是,旉、空间复杂度最的法不一定就是解x一特定问题的最佳算法。D例说Q快速排序在完全逆序的情况下{h于冒泡排序,q时其他Ҏ比它快。同Ӟ用汇~语a优化一个不正确的算法实玎ͼ给调试带来很大的麻烦?
  • 最后,认?B>已经高U语a~译器的性能发挥到极?/B>。Microsoft的编译器在RELEASE模式和DEBUG模式会有差异相当大的输出Q而对于GNUpd的编译器而言Q不同别的优化也会生成几乎完全不同的代码。此外,在编E时对于问题的严格定义,可以极大地帮助编译器的优化过E。如何优化高U语a代码Q其编译结果最优超Z本教E的范围Q但如果你不能确认已l发挥了~译器的最大效能,用汇~语a往往是一U更力的Ҏ?
  • q有一炚w帔R要,那就是你明白自己做的是什么?/B>好的高语言~译器有时会有一些让人难以理解的行ؓQ比如,重新排列指o序Q等{。如果你发现q种情况Q那么优化的时候就应该心——编译器很可能比你拥有更多的关于处理器的知识Q例如,对于一个超标量处理器,~译器会Ҏ令序列进行“封包”,使他们尽可能的ƈ行执行;此外Q宏汇编器有时会自动插入一些nop指oQ其作用是将指o凑成整数字长Q?2-bitQ对?6-bit处理器,?6-bitQ。这些都是提高代码性能的必要措施,如果你不了解处理器,那么最好不要改动编译器生成的代码,因ؓq种情况下,盲目的修改往往不会得到预期的效果?/LI>

曄在一份杂志上看到q有人用U机器语a~写E序。不清楚到底q是不是~辑的失误,因ؓ一个头脑正常的人恐怕不会这么做E序Q即使它不长、也不复杂。首先,汇编器能够完成某些封包操作,即不行Q也可以用db伪指令来写指令;用汇~语a写程序可以防止很多错误的发生Q同Ӟ它还减轻了h的负担,很显Ӟ“完全用机器语言写程序”是完全没有必要的,因ؓ汇编语言可以做出完全一L事情Qƈ且你可以依赖它,因ؓ计算Z会出错,而hL出错的时候。此外,如前面所aQ如果用高语言实现E序的代价不大(例如Q这D代码在E序的整个执行过E中只执行一遍,q且Q这一遍的执行旉也小于一U)Q那么,Z么不用高U语a实现呢?

一些比较狂热的~程爱好者可能不太喜Ƣ我的这U观炏V比方说Q他们可能希望精益求_֜优化每一字节的代码。但多数情况下我们有更重要的事情Q例如,你的法是最优的吗?你已l把E序在高U语a许可的范围内优化到尽头了吗?q不是所有的人都有资D栯。汇~语a是这样一件东西,它够的强大Q能够控制计机Q完成它能够实现的Q何功能;同时Q因为它的强大,也会提高开发成本,q且Q难于维护。因此,我个人的是,如果在Y件开发中使用汇编语言Q则应在软g接近完成的时候用,q样可以减少很多不必要的投入?/P>

W二章中Q我介绍了x86pd处理器的基本寄存器。这些寄存器对于x86兼容处理器仍然是有效的,如果你偏爱AMD的CPUQ那么用这些寄存器的程序同样也可以正常q行?/P>

不过现在说用汇编语言q行优化qؓ时尚早——不可能写程序,而只操作q些寄存器,因ؓq样只能完成非常单的操作Q既然是单的操作Q那可能׃让h觉得乏味Q甚x一台够快的机器穷丑֮的所有结果(如果可以ID的话Q,q直接写E序调用Q因样通常会更快。但话说回来Q看完接下来的两章——内存和堆栈操作Q你可以独立完成几乎所有的d了,配合W五章中断、第六章子程序的知识Q你知道如何驾驭处理器Qƈ让它Z工作?/P>数字计算机内?B>只支?/B>二进制数Q因栯机
只需要表CZU?某些情况?U,q一内容过?BR>q䆾教程的范_如果您感兴趣Q可以参考数字?BR>辑电路的相关书籍)状?  对于电\而言Q这表现
为高、低电^Q或者开、关Q分别非常明显,因?BR>工作比较E_Q另一斚wQ由于只有两U状态,?BR>计v来也比较单。这P使用二进制意味着低成
本、稳定,多数情况下,q也意味着快速?

与十q制cMQ我们可以用下面的式子来换算Z
个Q意Ş如am-1……a3a2a1a0 的m位rq制数对应的
数值nQ?/P>

E序设计中常用十六进制和八进制数字代替二q制
敎ͼ其原因在于,16??的整ơ方q,q样Q一
位十六或八进制数可以表示整数个二q制位。十?BR>q制中, 使用字母A、B、C、D、E、F表示10-15Q?BR>而十六进制或八进制数制表C的的数字比二进制数
更短一些?/P>

EAX的内容ؓ000A3412h.



]]>
寄存?http://www.shnenglu.com/ivenher/articles/1067.html爱饭?/dc:creator>爱饭?/author>Fri, 11 Nov 2005 05:06:00 GMThttp://www.shnenglu.com/ivenher/articles/1067.htmlhttp://www.shnenglu.com/ivenher/comments/1067.htmlhttp://www.shnenglu.com/ivenher/articles/1067.html#Feedback0http://www.shnenglu.com/ivenher/comments/commentRss/1067.htmlhttp://www.shnenglu.com/ivenher/services/trackbacks/1067.html 

1. 通用寄存?/FONT>:

EAX

(accumulator)"累加?,很多加法乘法指o的缺?FONT style="BACKGROUND-COLOR: #316ac5" color=#ffffff>寄存?/FONT>.

EBX

(base)"基地址"寄存?/FONT>, 在内存寻址时存攑֟地址.

 

ECX

(counter)计数? 是重?REP)前缀指o和LOOP指o的内定计?/P>

EDX

用来放整数除法生的余数.

?6?AX,BX,CX和DX

??:AL,BL,CL和DL

??:AH,BH,CH和DH

ESI

(source index)"源烦?FONT style="BACKGROUND-COLOR: #316ac5" color=#ffffff>寄存?/FONT>", DS:ESI指向源串,?字符串操作指令中,

EDI

(destination index)"目标索引寄存?/FONT>",ES:EDI指向目标?/P>

EBP

(BASE POINTER)"基址指针",被用作高U语a函数调用?BR>

,ESP(q个虽然通用,?BR>很少被用做除了堆栈指针外的用? q些32位可以被用作多种4?FONT style="BACKGROUND-COLOR: #316ac5" color=#ffffff>寄存?/FONT>的又?/P>

 函数的返回值经常被攑֜EAX? ESI/EDI分别叫做"?目标索引寄存?/FONT>"(source/destination index

),因ؓ在很多字W串操作指o? DS:ESI指向源串,? EBP?基址指针"(BASE POINTER), 它最l常"

框架指针"(frame pointer). 在破解的时?l常可以看见一个标准的函数起始代码: push ebp ;保存当前ebp mov ebp,esp ;EBP设ؓ当前堆栈指针

sub esp, xxx ;预留xxx字节l函C时变? ... q样一?EBP 构成了该函数的一个框? 在EBP上方分别是原来的EBP, q回地址和参? EBP?BR>
方则是时变? 函数q回时作 mov esp,ebp/pop ebp/ret 卛_. ESP 专门用作堆栈指针. 2. D?FONT style="BACKGROUND-COLOR: #316ac5" color=#ffffff>寄存?/FONT>: CS(Code SegmentQ代码段) 指定当前?BR>
行的代码D? EIP (Instruction pointer, 指o指针)则指向该D中一个具体的指o. CS:EIP指向哪个指o, CPU 执行它. 一般只能用jmp, ret,

jnz, call {指令来改变E序程,而不能直接对它们赋? DS(DATA SEGMENT, 数据D? 指定一个数据段. 注意:在当前的计算机系l中, 代码和数

据没有本质差? 都是一串二q制? 区别只在于你如何用它. 例如, CS 制定的段L被用作代? 一般不能通过CS指定的地址M改该D? 然?BR>
,你可以ؓ同一个段甌一个数据段描述W?别名"而通过DS来访?修改. 自修改代码的E序常如此做. ES,FS,GS 是辅助的D?FONT style="BACKGROUND-COLOR: #316ac5" color=#ffffff>寄存?/FONT>, 指定附加的数

据段. SS(STACK SEGMENT)指定当前堆栈D? ESP 则指D中当前的堆栈顶. 所有push/pop pd指o都只对SS:ESP指出的地址q行操作. 3. 标志

寄存?/FONT>(EFLAGS): ?FONT style="BACKGROUND-COLOR: #316ac5" color=#ffffff>寄存?/FONT>?2?l合了各个系l标? EFLAGS一般不作ؓ整体讉K, 而只对单一的标志位感兴? 常用的标志有: q位标志C(

CARRY), 在加法生进位或减法有借位时置1, 否则?. 零标志Z(ZERO), 若运结果ؓ0则置1, 否则? W号位S(SIGN), 若运结果的最高位|?BR>
1, 则该位也|?. 溢出标志O(OVERFLOW), ?带符?q算l果出可表C? 则置1. JXX pd指o是Ҏq些标志来决定是否要跌{, 从?BR>
实现条g分枝. 要注?很多JXX 指o是等L, 对应相同的机器码. 例如, JE 和JZ 是一L,都是当Z=1是蟩? 只有JMP 是无条g跌{. JXX ?BR>
令分Zl? 分别用于无符h作和带符h? JXX 后面?XX" 有如下字? 无符h? 带符h? A = "ABOVE", 表示"高于" G = "

GREATER", 表示"大于" B = "BELOW", 表示"低于" L = "LESS", 表示"于" C = "CARRY", 表示"q位"?借位" O = "OVERFLOW", 表示"溢出" S

= "SIGN", 表示"? 通用W号: E = "EQUAL" 表示"{于", {h于Z (ZERO) N = "NOT" 表示"?, x志没有置? 如JNZ "如果Z没有|位则蟩

? Z = "ZERO", 与E? 如果仔细想一?׃发现 JA = JNBE, JAE = JNB, JBE = JNA, JG = JNLE, JGE= JNL, JL= JNGE, .... 4. 端口 端口

是直接和外部讑֤通讯的地斏V外设接入系l后Q系l就会把外设的数据接口映到特定的端口地址I间Q这P从该端口d数据是从外设读

入数据,而向外设写入数据是向端口写入数据。当然这一切都必须遵@外设的工作方式。端口的地址I间与内存地址I间无关Q系ld提供?BR>
64K?位端口的讉KQ编?-65535. 盔R?位端口可以组成成一?6位端口,盔R?6位端口可以组成一?2位端口。端口输入输出由指o

IN,OUT,INS和OUTS实现Q具体可参考汇~语a书籍?BR>

 

中央处理?CPU)在微机系l处于“领导核心”的C。汇~语a被编译成机器语言之后Q将由处理器来执行。那么,首先让我们来了解一下处理器的主要作用,q将帮助你更好地N它?/P>

典型的处理器的主要Q务包?/B>
  • 从内存中获取机器语言指oQ译码,执行
  • Ҏ指o代码理它自q寄存?
  • Ҏ指o或自q的需要修改内存的内容
  • 响应其他g的中断请?

一般说来,处理器拥有对整个pȝ的所有ȝ的控制权。对于Intelq_而言Q处理器拥有Ҏ据、内存和控制ȝ的控制权Q根据指令控制整个计机的运行。在以后的章节中Q我们还讨论系l中同时存在多个处理器的情况?/P>

处理器中有一些寄存器Q这些寄存器可以保存特定长度的数据。某些寄存器中保存的数据对于pȝ的运行有Ҏ的意义?/P>

新的处理器往往拥有更多、具有更大字长的寄存器,提供更灵zȝ取指、寻址方式?/P>

寄存?/B>

如前所qͼ处理器中有一些可以保存数据的地方被称作寄存器?/P>

寄存器可以被装入数据Q你也可以在不同的寄存器之间Udq些数据Q或者做cM的事情。基本上Q像四则q算、位q算{这些计操作,都主要是针对寄存器进行的?/P>

首先让我来介l一?0386上最常用?个通用寄存器。先瞧瞧下面的图形,试着理解一下:

上图中,数字表示的是位。我们可以看出,EAX是一?2-bit寄存器。同Ӟ它的?6-bit又可以通过AXq个名字来访问;AX又被分ؓ高、低8bit两部分,分别由AH和AL来表C?/P>

对于EAX、AX、AH、AL的改变同时也会媄响与被修改的那些寄存器的倹{从而事实上只存在一?2-bit的寄存器EAXQ而它可以通过4U不同的途径讉K?/P>

也许通过名字能够更容易地理解q些寄存器之间的关系。EAX中的E的意思是“扩展的”,整个EAX的意思是扩展的AX。X的意思Intel没有明示Q我个h认ؓ表示它是一个可变的?。而AH、AL中的H和L分别代表高和??/P>

Z么要q么做呢Q主要由于历史原因。早期的计算机是8位的Q?086是第一?6位处理器Q其通用寄存器的名字是AXQBX{等Q?0386是Intel推出的第一ƾIA-32pd处理器,所有的寄存器都被扩充ؓ32位。ؓ了能够兼容以前的16位应用程序,80386不能这些寄存器依旧命名为AX、BXQƈ且简单地他们扩充ؓ32位——这增加处理器在处理指令方面的成本?/P>

Intel微处理器的寄存器列表Q在本章先只介绍80386的寄存器QMMX寄存器以及其他新一代处理器的新寄存器将在以后的章节介绍Q?/P>

通用寄存?/B>
下面介绍通用寄存器及其习惯用法。顾名思义Q通用寄存器是那些你可以根据自q意愿使用的寄存器Q修改他们的值通常不会对计机的运行造成很大的媄响。通用寄存器最多的用途是计算?/P>

EAX
32-bit?/P>

通用寄存器。相对其他寄存器Q在q行q算斚w比较常用。在保护模式中,也可以作为内存偏UL针(此时QDS作ؓD?寄存器或选择器)

EBX
32-bit?/P>

通用寄存器。通常作ؓ内存偏移指针使用Q相对于EAX、ECX、EDXQ,DS是默认的D寄存器或选择器。在保护模式中,同样可以赯个作用?/TD>

ECX
32-bit?/P>

通用寄存器。通常用于特定指o的计数。在保护模式中,也可以作为内存偏UL针(此时QDS作ؓ 寄存器或D选择器)?/TD>

EDX
32-bit?/P>

通用寄存器。在某些q算中作为EAX的溢出寄存器Q例如乘、除Q。在保护模式中,也可以作为内存偏UL针(此时QDS作ؓD?寄存器或选择器)?/TD>

上述寄存器同EAX一样包括对应的16-bit?-bit分组?/P>

用作内存指针的特D寄存器

ESI
32-bit?

通常在内存操作指令中作ؓ“源地址指针”用。当ӞESI可以被装入Q意的数|但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器?/TD>

EDI
32-bit?/P>

通常在内存操作指令中作ؓ“目的地址指针”用。当ӞEDI也可以被装入L的数|但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器?/TD>

EBP
32-bit?/P>

q也是一个作为指针的寄存器。通常Q它被高U语a~译器用以徏造‘堆栈’来保存函数或过E的局部变量,不过Q还是那句话Q你可以在其中保存你希望的Q何数据。SS是它的默认段寄存器或选择器?/TD>

注意Q这三个寄存器没有对应的8-bit分组。换a之,你可以通过SI、DI、BP作ؓ别名讉K他们的低16位,却没有办法直接访问他们的?位?/P>

D寄存器和选择?/B>

实模式下的段寄存器到保护模式下摇w一变就成了选择器。不同的是,实模式下的“段寄存器”是16-bit的,而保护模式下的选择器是32-bit的?/P>

CS 代码D,或代码选择器。同IP寄存?E后介绍)一同指向当前正在执行的那个地址。处理器执行时从q个寄存器指向的D(实模式)或内存(保护模式Q中获取指o。除了蟩转或其他分支指o之外Q你无法修改q个寄存器的内容?/TD>
DS 数据D,或数据选择器。这个寄存器的低16 bitq同ESI一同指向的指o要处理的内存。同Ӟ所有的内存操作指o 默认情况下都用它指定操作D?实模?或内?作ؓ选择器,在保护模式。这个寄存器可以被装入Q意数|然而在q么做的时候需要小心一些。方法是Q首先把数据送给AXQ然后再把它从AX传送给DS(当然Q也可以通过堆栈来做).
ES 附加D,或附加选择器。这个寄存器的低16 bitq同EDI一同指向的指o要处理的内存。同LQ这个寄存器可以被装入Q意数|Ҏ和DScM?/TD>
FS FD|F选择?推测F可能是Free?)。可以用q个寄存器作为默认段寄存器或选择器的一个替代品。它可以被装入Q何数|Ҏ和DScM?/TD>
GS GD|G选择?G的意义和F一P没有在Intel的文档中解释)。它和FS几乎完全一栗?/TD>
SS 堆栈D|堆栈选择器。这个寄存器的低16 bitq同ESP一同指向下一ơ堆栈操?push和pop)所要用的堆栈地址。这个寄存器也可以被装入L数|你可以通过入栈和出栈操作来l他赋|不过׃堆栈对于很多操作有很重要的意义,因此Q不正确的修Ҏ可能造成对堆栈的破坏?/TD>

* 注意 一定不要在初学汇编的阶D|q些寄存器弄淗他们非帔R要,而一旦你掌握了他们,你就可以对他们做L的操作了。段寄存器,或选择器,在没有指定的情况下都是用默认的那个。这句话在现在看来可能有点稀里糊涂,不过你很快就会在后面知道如何d?/P>

Ҏ寄存?指向到特定段或内存的偏移?Q?/P>

EIP q个寄存器非常的重要。这是一?2位宽的寄存器 Q同CS一同指向即执行的那条指o的地址。不能够直接修改q个寄存器的|修改它的唯一Ҏ是蟩转或分支指o?CS是默认的D|选择?
ESP q个32位寄存器指向堆栈中即被操作的那个地址。尽可以修改它的|然而ƈ不提倡这样做Q因为如果你不是非常明白自己在做什么,那么你可能造成堆栈的破坏。对于绝大多数情况而言Q这对程序是致命的?SS是默认的D|选择?

IP: Instruction Pointer, 指o指针
SP: Stack Pointer, 堆栈指针

好了Q上面是最基本的寄存器。下面是一些其他的寄存器,你甚臛_能没有听说过它们?都是32位宽)Q?/P>

CR0, CR2, CR3(控制寄存?。D一个例子,CR0的作用是切换实模式和保护模式?/P>

q有其他一些寄存器QD0, D1, D2, D3, D6和D7(调试寄存?。他们可以作试器的硬件支持来讄条g断点?/P>

TR3, TR4, TR5, TR6 ?TR? 寄存?试寄存?用于某些条g试?/P>

最后我们要说的是一个在E序设计中v着非常关键的作用的寄存器:标志寄存器?/P>



]]>预处理过E?/title><link>http://www.shnenglu.com/ivenher/articles/916.html</link><dc:creator>爱饭?/dc:creator><author>爱饭?/author><pubDate>Thu, 03 Nov 2005 03:30:00 GMT</pubDate><guid>http://www.shnenglu.com/ivenher/articles/916.html</guid><wfw:comment>http://www.shnenglu.com/ivenher/comments/916.html</wfw:comment><comments>http://www.shnenglu.com/ivenher/articles/916.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ivenher/comments/commentRss/916.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ivenher/services/trackbacks/916.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 9pt; FONT-FAMILY: 宋体; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">                                                 <FONT color=#000000 size=5><STRONG> 预处理过E?/STRONG></FONT><BR><BR>预处理过E扫描源代码Q对其进行初步的转换Q生新的源代码提供l编译器。可见预处理q程先于~译器对源代码进行处理?SPAN lang=EN-US><BR></SPAN>?SPAN lang=EN-US>C</SPAN>语言中,q没有Q何内在的机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码。要完成q些工作Q就需要用预处理E序。尽在目前l大多数~译器都包含了预处理E序Q但通常认ؓ它们是独立于~译器的。预处理q程d源代码,查包含预处理指o的语句和宏定义,q对源代码进行响应的转换。预处理q程q会删除E序中的注释和多余的I白字符?SPAN lang=EN-US><BR></SPAN>预处理指令是?SPAN lang=EN-US>#</SPAN>号开头的代码行?SPAN lang=EN-US>#</SPAN>号必L该行除了MI白字符外的W一个字W?SPAN lang=EN-US>#</SPAN>后是指o关键字,在关键字?SPAN lang=EN-US>#</SPAN>号之间允许存在Q意个数的I白字符。整行语句构成了一条预处理指oQ该指o在~译器进行编译之前对源代码做某些转换。下面是部分预处理指令:<SPAN lang=EN-US><BR><BR>        </SPAN>指o<SPAN lang=EN-US>             </SPAN>用?SPAN lang=EN-US><BR>         #           </SPAN>I指令,无Q何效?SPAN lang=EN-US><BR>         #include    </SPAN>包含一个源代码文g<SPAN lang=EN-US><BR>         #define     </SPAN>定义?SPAN lang=EN-US><BR>         #undef      </SPAN>取消已定义的?SPAN lang=EN-US><BR>         #if         </SPAN>如果l定条g为真Q则~译下面代码<SPAN lang=EN-US><BR>         #ifdef      </SPAN>如果宏已l定义,则编译下面代?SPAN lang=EN-US><BR>         #ifndef     </SPAN>如果宏没有定义,则编译下面代?SPAN lang=EN-US><BR>         #elif       </SPAN>如果前面?SPAN lang=EN-US>#if</SPAN>l定条g不ؓ真,当前条g为真Q则~译下面代码<SPAN lang=EN-US><BR>         #endif      </SPAN>l束一?SPAN lang=EN-US>#if</SPAN>…?SPAN lang=EN-US>#else</SPAN>条g~译?SPAN lang=EN-US><BR>         #error      </SPAN>停止~译q显C错误信?SPAN lang=EN-US><BR><BR></SPAN>一、文件包?SPAN lang=EN-US><BR>    #include</SPAN>预处理指令的作用是在指o处展开被包含的文g。包含可以是多重的,也就是说一个被包含的文件中q可以包含其他文件。标?SPAN lang=EN-US>C</SPAN>~译器至支持八重嵌套包含?SPAN lang=EN-US><BR>    </SPAN>预处理过E不查在转换单元中是否已l包含了某个文gq止对它的多次包含。这样就可以在多ơ包含同一个头文gӞ通过l定~译时的条g来达C同的效果。例如:<SPAN lang=EN-US><BR><BR>        #define AAA<BR>        #include "t.c"<BR>        #undef AAA<BR>        #include "t.c"<BR><BR>    </SPAN>Z避免那些只能包含一ơ的头文件被多次包含Q可以在头文件中用编译时条g来进行控制。例如:<SPAN lang=EN-US><BR>        /*my.h*/<BR>        #ifndef MY_H<BR>        #define MY_H<BR>          </SPAN>…?SPAN lang=EN-US><BR>        #endif<BR><BR>    </SPAN>在程序中包含头文件有两种格式Q?SPAN lang=EN-US><BR>        #include <my.h><BR>        #include "my.h"<BR>    </SPAN>W一U方法是用尖括号把头文g括v来。这U格式告诉预处理E序在编译器自带的或外部库的头文件中搜烦被包含的头文件。第二种Ҏ是用双引h头文件括h。这U格式告诉预处理E序在当前被~译的应用程序的源代码文件中搜烦被包含的头文Ӟ如果找不刎ͼ再搜索编译器自带的头文g?SPAN lang=EN-US><BR>    </SPAN>采用两种不同包含格式的理由在于,~译器是安装在公共子目录下的Q而被~译的应用程序是在它们自qU有子目录下的。一个应用程序既包含~译器提供的公共头文Ӟ也包含自定义的私有头文g。采用两U不同的包含格式使得~译器能够在很多头文件中区别Zl公q头文件?SPAN lang=EN-US><BR><BR></SPAN>二、宏<SPAN lang=EN-US><BR>    </SPAN>宏定义了一个代表特定内容的标识W。预处理q程会把源代码中出现的宏标识W替换成宏定义时的倹{宏最常见的用法是定义代表某个值的全局W号。宏的第二种用法是定义带参数的宏Q这L宏可以象函数一栯调用Q但它是在调用语句处展开宏,q用调用时的实际参数来代替定义中的Ş式参数?SPAN lang=EN-US><BR>    1.#define</SPAN>指o<SPAN lang=EN-US><BR>        #define</SPAN>预处理指令是用来定义宏的。该指o最单的格式是:首先明一个标识符Q然后给个标识符代表的代码。在后面的源代码中,qq些代码来替代该标识W。这U宏把程序中要用到的一些全局值提取出来,赋给一些记忆标识符?SPAN lang=EN-US><BR>            #define MAX_NUM 10<BR>            int array[MAX_NUM];<BR>            for(i=0;i<MAX_NUM;i++)  /*</SPAN>…?SPAN lang=EN-US>*/<BR>        <BR>        </SPAN>在这个例子中Q对于阅读该E序的h来说Q符?SPAN lang=EN-US>MAX_NUM</SPAN>有特定的含义,它代表的值给Z数组所能容U的最大元素数目。程序中可以多次使用q个倹{作ZU约定,习惯上L全部用大写字母来定义宏,q样易于把程序红的宏标识W和一般变量标识符区别开来。如果想要改变数l的大小Q只需要更改宏定义q新编译程序即可?SPAN lang=EN-US><BR>        </SPAN>宏表C的值可以是一个常量表辑ּQ其中允许包括前面已l定义的宏标识符。例如:<SPAN lang=EN-US><BR>            #define ONE 1<BR>            #define TWO 2<BR>            #define THREE (ONE+TWO)<BR>        </SPAN>注意上面的宏定义使用了括受尽它们ƈ不是必须的。但Z谨慎考虑Q还是应该加上括L。例如:<SPAN lang=EN-US><BR>            six=THREE*TWO;<BR>        </SPAN>预处理过E把上面的一行代码{换成Q?SPAN lang=EN-US><BR>            six=(ONE+TWO)*TWO;<BR>        </SPAN>如果没有那个括号Q就转换?SPAN lang=EN-US>six=ONE+TWO*TWO;</SPAN>了?SPAN lang=EN-US><BR>        </SPAN>宏还可以代表一个字W串帔RQ例如:<SPAN lang=EN-US><BR>            #define VERSION "Version 1.0 Copyright(c) 2003"<BR>    2.</SPAN>带参数的<SPAN lang=EN-US>#define</SPAN>指o<SPAN lang=EN-US><BR>        </SPAN>带参数的宏和函数调用看v来有些相伹{看一个例子:<SPAN lang=EN-US><BR>            #define Cube(x) (x)*(x)*(x)<BR>        </SPAN>可以时Q何数字表辑ּ甚至函数调用来代替参?SPAN lang=EN-US>x</SPAN>。这里再ơ提醒大家注意括L使用。宏展开后完全包含在一Ҏ号中Q而且参数也包含在括号中,q样׃证了宏和参数的完整性。看一个用法:<SPAN lang=EN-US><BR>            int num=8+2;<BR>            volume=Cube(num);<BR>        </SPAN>展开后ؓ<SPAN lang=EN-US>(8+2)*(8+2)*(8+2);<BR>        </SPAN>如果没有那些括号变?SPAN lang=EN-US>8+2*8+2*8+2</SPAN>了?SPAN lang=EN-US><BR>        </SPAN>下面的用法是不安全的Q?SPAN lang=EN-US><BR>            volume=Cube(num++);<BR>        </SPAN>如果<SPAN lang=EN-US>Cube</SPAN>是一个函敎ͼ上面的写法是可以理解的。但是,因ؓ<SPAN lang=EN-US>Cube</SPAN>是一个宏Q所以会产生副作用。这里的擦书不是单的表达式,它们生意想不到的l果。它们展开后是q样的:<SPAN lang=EN-US><BR>            volume=(num++)*(num++)*(num++);<BR>        </SPAN>很显Ӟl果?SPAN lang=EN-US>10*11*12,</SPAN>而不?SPAN lang=EN-US>10*10*10;<BR>        </SPAN>那么怎样安全的?SPAN lang=EN-US>Cube</SPAN>宏呢Q必L可能产生副作用的操作Ud宏调用的外面q行Q?SPAN lang=EN-US><BR>            int num=8+2;<BR>            volume=Cube(num);<BR>            num++;<BR>    3.#</SPAN>q算W?SPAN lang=EN-US><BR>        </SPAN>出现在宏定义中的<SPAN lang=EN-US>#</SPAN>q算W把跟在其后的参数{换成一个字W串。有时把q种用法?SPAN lang=EN-US>#</SPAN>UCؓ字符串化q算W。例如:<SPAN lang=EN-US><BR><BR>            #define PASTE(n) "adhfkj"#n<BR><BR>            main()<BR>            {<BR>               printf("%s\n",PASTE(15));<BR>            }<BR>        </SPAN>宏定义中?SPAN lang=EN-US>#</SPAN>q算W告诉预处理E序Q把源代码中M传递给该宏的参数{换成一个字W串。所以输出应该是<SPAN lang=EN-US>adhfkj15</SPAN>?SPAN lang=EN-US><BR>    4.##</SPAN>q算W?SPAN lang=EN-US><BR>        ##</SPAN>q算W用于把参数q接C赗预处理E序把出现在<SPAN lang=EN-US>##</SPAN>两侧的参数合q成一个符受看下面的例子:<SPAN lang=EN-US><BR><BR>            #define NUM(a,b,c) a##b##c<BR>            #define STR(a,b,c) a##b##c<BR><BR>            main()<BR>            {<BR>                printf("%d\n",NUM(1,2,3));<BR>                printf("%s\n",STR("aa","bb","cc"));<BR>            }<BR><BR>        </SPAN>最后程序的输出?SPAN lang=EN-US>:<BR>                 123<BR>                 aabbcc<BR>        </SPAN>千万别担心,除非需要或者宏的用法恰好和手头的工作相养I否则很少有程序员会知?SPAN lang=EN-US>##</SPAN>q算W。绝大多数程序员从来没用q它?SPAN lang=EN-US><BR><BR></SPAN>三、条件编译指?SPAN lang=EN-US><BR>    </SPAN>条g~译指o决定那些代码被~译Q而哪些是不被~译的。可以根据表辑ּ的值或者某个特定的宏是否被定义来确定编译条件?SPAN lang=EN-US><BR>    1.#if</SPAN>指o<SPAN lang=EN-US><BR>        #if</SPAN>指o跟在制造另关键字后的常量表辑ּ。如果表辑ּ为真Q则~译后面的代码,知道出现<SPAN lang=EN-US>#else</SPAN>?SPAN lang=EN-US>#elif</SPAN>?SPAN lang=EN-US>#endif</SPAN>为止Q否则就不编译?SPAN lang=EN-US><BR>    2.#endif</SPAN>指o<SPAN lang=EN-US><BR>        #endif</SPAN>用于l止<SPAN lang=EN-US>#if</SPAN>预处理指令?SPAN lang=EN-US><BR><BR>            #define DEBUG 0<BR>            main()<BR>            {<BR>                #if DEBUG<BR>                    printf("Debugging\n");<BR>                #endif<BR>                    printf("Running\n");<BR>            }<BR><BR>        </SPAN>׃E序定义<SPAN lang=EN-US>DEBUG</SPAN>宏代?SPAN lang=EN-US>0</SPAN>Q所?SPAN lang=EN-US>#if</SPAN>条g为假Q不~译后面的代码直?SPAN lang=EN-US>#endif</SPAN>Q所以程序直接输?SPAN lang=EN-US>Running</SPAN>?SPAN lang=EN-US><BR>        </SPAN>如果L<SPAN lang=EN-US>#define</SPAN>语句Q效果是一L?SPAN lang=EN-US><BR>    3.#ifdef</SPAN>?SPAN lang=EN-US>#ifndef<BR>        #define DEBUG<BR><BR>        main()<BR>        {<BR>            #ifdef DEBUG<BR>                printf("yes\n");<BR>            #endif<BR>            #ifndef DEBUG<BR>                printf("no\n");<BR>            #endif<BR>        }<BR>        #if defined</SPAN>{h?SPAN lang=EN-US>#ifdef; #if !defined</SPAN>{h?SPAN lang=EN-US>#ifndef<BR>    4.#else</SPAN>指o<SPAN lang=EN-US><BR>        #else</SPAN>指o用于某个<SPAN lang=EN-US>#if</SPAN>指o之后Q当前面?SPAN lang=EN-US>#if</SPAN>指o的条件不为真Ӟq?SPAN lang=EN-US>#else</SPAN>后面的代码?SPAN lang=EN-US>#endif</SPAN>指o中指上面的条g块?SPAN lang=EN-US><BR><BR>        #define DEBUG<BR><BR>        main()<BR>        {<BR>            #ifdef DEBUG<BR>                printf("Debugging\n");<BR>            #else<BR>                printf("Not debugging\n");<BR>            #endif<BR>                printf("Running\n");<BR>       }<BR><BR>    5.#elif</SPAN>指o<SPAN lang=EN-US><BR>        #elif</SPAN>预处理指令综合了<SPAN lang=EN-US>#else</SPAN>?SPAN lang=EN-US>#if</SPAN>指o的作用?SPAN lang=EN-US><BR><BR>        #define TWO<BR><BR>        main()<BR>        {<BR>            #ifdef ONE<BR>                printf("1\n");<BR>            #elif defined TWO<BR>                printf("2\n");<BR>            #else<BR>                printf("3\n");<BR>            #endif<BR>        }<BR>        </SPAN>E序很好理解Q最后输出结果是<SPAN lang=EN-US>2</SPAN>?SPAN lang=EN-US><BR><BR>    6.</SPAN>其他一些标准指?SPAN lang=EN-US><BR>        #error</SPAN>指oɾ~译器显CZ条错误信息,然后停止~译?SPAN lang=EN-US><BR>        #line</SPAN>指o可以改变~译器用来指告和错误信息的文件号和行受?SPAN lang=EN-US><BR>        #pragma</SPAN>指o没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦h的警告信息?/SPAN><img src ="http://www.shnenglu.com/ivenher/aggbug/916.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ivenher/" target="_blank">爱饭?/a> 2005-11-03 11:30 <a href="http://www.shnenglu.com/ivenher/articles/916.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.hbjyhg.cn" target="_blank">þﶼǾƷ</a>| <a href="http://www.niluoya.cn" target="_blank">ҹƷþþþþ99</a>| <a href="http://www.shuwangou.cn" target="_blank">þùƷҰAV</a>| <a href="http://www.snailwr.cn" target="_blank">þAVԴվ</a>| <a href="http://www.drlf.net.cn" target="_blank">þþþþþòҰ¸߳</a>| <a href="http://www.heqiaoluo.cn" target="_blank">ҹþþӰԺ</a>| <a href="http://www.zhuangzhua.cn" target="_blank">7ŷպۺľþþþ </a>| <a href="http://www.tfy8.cn" target="_blank">پþþƷþ</a>| <a href="http://www.156v7.cn" target="_blank">þþù99þùһ</a>| <a href="http://www.tthzk.cn" target="_blank">˺ݺۺϾþ88</a>| <a href="http://www.shuwangou.cn" target="_blank">Ʒþþþþþ˿</a>| <a href="http://www.rcjmwj.com.cn" target="_blank">996þùƷ߹ۿ</a>| <a href="http://www.ireboot.cn" target="_blank">뾫ƷþɪӰ </a>| <a href="http://www.mechuo.cn" target="_blank">þAV뾫Ʒɫҹ鶹 </a>| <a href="http://www.hefxxw.cn" target="_blank">þ</a>| <a href="http://www.yanggroup.cn" target="_blank">ŮƷþþþ</a>| <a href="http://www.kqtao.cn" target="_blank">97Ƶþþ</a>| <a href="http://www.broadbean.cn" target="_blank">ھƷþþþù</a>| <a href="http://www.chizhou8.cn" target="_blank">þþƷ޾Ʒɫ </a>| <a href="http://www.bandetek.cn" target="_blank">ۺɫۺϾþۺ</a>| <a href="http://www.hkrczp.cn" target="_blank">þѵľƷV</a>| <a href="http://www.tjpengzhi.cn" target="_blank">ɫɫۺϾþҹҹ</a>| <a href="http://www.ppmm163.cn" target="_blank">2021þþƷѹۿ</a>| <a href="http://www.syworld.com.cn" target="_blank">þavСݲ</a>| <a href="http://www.gmmk.net.cn" target="_blank">69Ʒþþþ777</a>| <a href="http://www.9ii8.cn" target="_blank">þþ뾫ƷպĦ</a>| <a href="http://www.xnhtml.com.cn" target="_blank">þɫۺϼ</a>| <a href="http://www.jiudexn.cn" target="_blank">þþƷһ</a>| <a href="http://www.hunxueer.cn" target="_blank">þþƷƷapp</a>| <a href="http://www.bleg.cn" target="_blank">Ʒþþþþþ</a>| <a href="http://www.m28587.cn" target="_blank">þۺϾƷþ</a>| <a href="http://www.a-house.com.cn" target="_blank">ŵþ</a>| <a href="http://www.akeyu.cn" target="_blank">þþþŮۺ</a>| <a href="http://www.fjqcbks.cn" target="_blank">91þþþþþ</a>| <a href="http://www.hydqgs.cn" target="_blank">99þҹɫƷվ</a>| <a href="http://www.duange.com.cn" target="_blank">þŮվ</a>| <a href="http://www.adultr.cn" target="_blank">þþƷƷapp</a>| <a href="http://www.zhihuzhuanlan.com.cn" target="_blank">99Ʒþ</a>| <a href="http://www.stude.cn" target="_blank">ձþþþƷĻ</a>| <a href="http://www.qq577.cn" target="_blank">Ʒ99þò</a>| <a href="http://www.uqknet.cn" target="_blank">wwþþþþþþþ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>