用c++内嵌汇编如何实现大数据块的移位实现除法?

最近的浏览历史
浏览此书的还浏览过
购买此书的人还购买过
书  名:C++类和数据结构
作  者: 张杰良
出版时间:
出 版 社: 清华大学出版社
字  数: 550 千字
印  次: 1-1
印  张: 26.5
开  本: 16开
ISBN: 7
装  帧: 平装
定  价:¥49.99
电子书价:¥35.00
节省:¥14.99
vip价:¥35.00
配套资源下载:
点击图标下载
共有商品评论0条
  这是一本适合于学生的C++数据结构指南,它基于现代软件发展的现实和职业程序员的需求。本书首
先从类的全面介绍入手,提供学生成功使用数据结构所需的基础知识。接下来介绍了创建数据结构的方
法,包括链表和可扩展/收缩的动态数组。解释了时间复杂度对执行速度的影响方式,帮助程序员理解关键
性能之间的权衡考虑。然后以这些为基础,从散列表到二叉搜索树,详细介绍了每一种常见的数据结构。
本书还详细设计了各种概念性的解释,以帮助程序员使用任何现代程序语言。
  本书可作为计算机类专业或信息类相关专业的本科或专科教材,也可供从事计算机工程与应用工作的
科技工作者参考。
  编写本书的原因非常简单。当我在讲授数据结构课程时,通常无法在一本书中找到我喜欢讲授的所有主题。而且各个主题通常也不是按我喜欢的顺序排列,数据结构也不总是按我喜欢的方式建立。因此,我最终决定自己编写一本这方面的书,就是本书了。我相信这是一本优秀的书。
  或许前言太简单了吧。如果我写的前言只有一小段,出版商会对我怒目而视的,因此我再多写一些内容,也好让大家确信这是一本优秀的图书。首先谈一下我本人讲授数据结构的方法[0]。
  C++语言是一门非常适合于数据结构的语言,但是,作为教师,如何清晰地讲授那些C++中难以掌握的概念呢?实际上,这个困难是C++语言少有的缺点之一。这里我的方法就是首先激发学生学习概念。任何有过一年任教经验的人都知道,如果看上去所讲授的概念没有所指的话,那么学习它们的动机就会无力。本书自始至终都提供促人奋发学习的动机,而且我鼓励大家在自己的课堂中使用它们。
  本书自始至终对那些许多人觉得难以讲授的概念提供了清晰的解释,我还知道您会对此感到满意;它们以一种适合于学生的风格书写。本书中介绍基本概念的方式,没有过分考虑它们在技术上是否100%恰当;它们大部分在技术上是恰当的,但是您知道我所谈论的恰当性指的是什么—— 它们虽然能够正确地表示所讲授的概念,但是却将那些适合于学生理解的表示方法转变成晦涩难懂的表示。学生已经不能理解概念了!本书的读者是学生,不是那些计算机科学方面的专家教授。本书还附有幻灯片,可以使这些概念更加清晰;我称这些幻灯片为“动作幻灯片(action slide)”。幻灯片都像旧电影书的页面一样,快速翻阅它们可以看到事物在运动,例如马在奔跑。只是这里翻阅幻灯片看到的是那些当前讨论的代码和数据结构。
  除了更清晰讲授C++的方法外,我还有另一种方法,就是将关注点放在客户上—— 那些使用类或者数据结构的程序员。在他们第一门或者第二门编程方面的课程中,学生会满怀希望地问自己诸如下述类型的问题:“我怎么做才能使程序对用户更友好呢?”或者“我怎样做才能使程序的输出更整洁?”在这本书中,我希望学生问这样的问题:“我怎样才能使这个类(或者数据结构)让客户更方便使用呢?”或者“我怎样做才能使这个类(或者数据结构)对客户尽可能灵活呢?”从计算机用户到客户程序员,关注点发生了一个转变。因此,在本书中,当考虑实现问题时都会考虑客户。
  本书中所有的程序、类和数据结构都是在考虑客户的情况下从头开始编写的。代码使用Microsoft Visual Studio 2005 C++编译和测试。在本书中,代码都编有行号,用来在正文中引用。建议学生这样利用代码编号,即首先分析代码,查看自己是否能够理解代码。如果有一两行代码不能完全掌握,就可以在正文中查找代码编号,阅读这些代码行的讨论。但是,整个讨论还是应该阅读的,以查看自己的理解正确与否。
  在本书中,提供了一些帮助学生为应用程序选择合适数据结构,以及为数据结构选择合适实现的信息。当然,速度问题是必须考虑的,内存问题也是如此。实现开销使用的内存数量必须仔细权衡。确实今天的计算机拥有很多内存,但是纵观历史,当计算机的平均内存容量增加时,程序的平均规模也相应增加。内存问题非常重要,未来也一直重要。
  本书从各方面努力,尽可能使各个练习有意义。下面举一个没有意义的练习例子,例如“将栈中的元素翻转,从而栈顶的元素位于栈底。”这会得到什么结果呢?它的目的何在呢?我试图确保各个练习都有一定的目的,从而不会使学生感到厌烦,认为数据结构无用。
  本书自始至终,我都一直强调不管客户是否误用了函数,在编写类函数时,应该防止函数的溃崩。这是一个极高的目标,但这也是一个必须为之奋斗的目标,不得不面对它,因为客户调用函数时通常只需一行代码,这一行代码相当简单,通常不会发生错误,但是当程序溃崩时,客户在调试过程中就不知道问题出在哪里。
  我的想法就是,通过示范如何设计一个类或者数据结构,从而使学生在设计自己的专用数据结构时知道考虑些什么问题。如果不希望学生设计自己的数据结构,那么就没有必要向他们示范数据结构的代码,只需使用它们即可。出于这方面的原因,在本书中,随数据结构一起介绍的问题包括客户、内存和速度。这些问题都是设计数据结构时需要考虑的重要因素。
  我尽量缩减本书的篇幅,不让它成为一本难啃的大部头,我个人认为这是一件使学生和教师都满意的事情;教师可以讲授本书的所有内容,学生也能够消化吸收本书中的内容。本书和其他数据结构方面的书不同,其他书中有许多不同的主题,以满足各类人的需求,从而形成一本很厚的书。但是,实际上,从某种意义上讲,它没有满足任何人。
  尽管本书使用C++语言描述,但是我意识到在学生进入工作环境中以后,他们可能需要使用其他语言编写数据结构。因此,一个无法脱离C++语言而单独思考数据结构的学生可能需要度过一段艰难的时间。因此,在讨论数据结构时,直至数据结构的实际实现,我都试图避免使用C++术语来讨论数据结构。
  我没有采用这样的方法,即从一个简单的(但是可能不切合实际)数据结构(例如栈)实现开始,然后再以它为基础,逐步添加更多的功能。我发现许多学生对这种方法已经不感兴趣;学生们努力去理解数据结构,在理解了之后他们意识到这个设计没有那么出色,需要改进,所以接下来给他们提供一个更出色的设计,学生又需要从头开始理解。从一个简单的例子入手,并不能帮助学生更好地理解所提出的思想;相反,他们可能会留下这样的印象,即他们在特定时刻学到的东西可能并不出色。尽管从某种意义上讲,我实际上采取了这种方法,但是我采用了不同的方式。例如,我向他们讲授为了理解一个复杂的栈需要知道的所有内容,只是在第1章中我使用的是一个普通的、常见的类。
如何使用本书
  本书应该作为数据结构的第一门课程,根据课程安排,可以将本书用于第二学期或者第三学期的课程。第1~6章主要注重于对类的理解,从而为数据结构的理解打下一个坚实的基础,这一部分内容可能要比其他数据结构书本中更多。如果将本书用作第二学期的课程,那么学生应该对C++或者Java已有所了解。在这种情况下,您需要通读第1~10章的内容,然后选读第11~15章中的主题或者基本知识。如果将本书用作第三学期的课程,那么您至少有了一定的基础,您可以浏览第1~5章的内容(或许是为了复习一下相关知识),然后主要关注于第6~15章的内容。
  如果将本书作为第二学期的课程,我想您会对下述事实心存感激,即在讲授数据结构之前我先讲授了类。也就是说,我不是以几页内容来讲授类,然后就转到数据结构上。如果同时将两个主题拼在一块,学生肯定会迷惑的,他们要努力同时掌握类的概念和数据结构的概念。同时,在他们的内心深处,他们可能还会奇怪栈为什么不使用类来生成呢;学生很容易拥有这种思想。然后,学生开始相信类只用于数据结构,这也不是一个好思想。因此,在介绍数据结构之前,本书花5章的内容来介绍类。在这5章中详细展现了类的优点,因此当使用类生成数据结构时,就不会再疑问为什么使用类生成数据结构。而且不管以何种方式讲授数据结构,这是需要讲授的主要内容,因此我们没有在区分这两个主题上浪费太多的时间。
  如果将本书作为第三学期的课程,大家可能希望认真地阅读一下第1~5章的内容,以弥补那些在第二学期课程中还没有太深入的重要内容,因为后面章节的内容以前面章节的内容为基础。大家希望将注意力放在基于客户的设计决策上,因为,正如前面所述,本书自始至终一直以客户为中心。但是在如今,大多数书并不是这样做的。通常情况下,这些知识需要雇主来讲授。您也可以快速地浏览一遍前5章的内容。
  或许您需要在自己的课程中讲授组合、继承和多态概念。有一种方法,就是在面向对象编程的课程中先讲授这方面的内容。第6章包含继承和多态,但是这些内容是可选的。尽管不是全书使用这些主题,但是整本书自始至终都有练习要求使用继承。这些练习用一个星号标示。不管在数据结构中是否使用继承或多态,这种安排应该可以满足您的需求。
内容和组织方式
  首先在第1章中以一种简单的方式介绍类,像介绍结构一样,但是在类中添加了函数。这里没有使用const限定符和构造函数,这就可以使学生能够深刻理解什么是类。然后,从第2章到第6章,以前面的概念为基础,逐步深入地介绍其他概念。
  第2章尽可能早地介绍了类模板的概念(甚至比介绍构造函数还早)。这种表示顺序可能会使一些人感到奇怪,但是如果准备讲授使用类模板的数据结构,可以采用下述方式。
  (1) 首先提供不好的数据结构设计(没有类模板),然后提供较复杂的设计(使用类模板),这是我在前面讲过要尽量避免使用的一种方式。
  (2) 首先讲授一点类,接着是类模板,然后是数据结构。
  (3) 要讲授类就直接讲授类模板,以众多的资料详细讲授它们,然后再讲授数据结构。
  事实证明,方式(2)并不怎么出色,因为在学生准备好学习类模板时,他们已经开始学习数据结构了。但是,本书中的这种安排方式意味着在第2章中要花费一些时间。
  本书中介绍的所有数据结构都是用类模板生成的。较早地介绍类模板可以使学生在事情仍然简单时就调整思路。然后,当在数据结构中使用类模板时,就不会迷惑。第2章中还介绍了重载运算符,因为它们与类模板密切地结合成一个整体。我个人觉得重载运算符和类模板之间的关系经常被忽略。例如,如果在链表中搜索一个元素,可能有一部分代码如下所示:
  template
  bool LinkedList::retrieve( const DataType and element )
  // beginning code here is omitted
  while( ptr != NULL ) {
  if ( element == ptr->info ) {
  element = ptr->
  // other code here is omitted
  如果希望这段代码尽可能通用,那么element就有可能是某些简单的类型,例如整型,或者一些较复杂的类型,例如一个结构的对象,该对象的关键字值是我们希望搜索的值。在后一种情况中,需要为结构重载==运算符,从而可以比较关键字的值。然后再检索其他信息。因此,重载运算符和模板数据结构密切地结合成一个整体。
  我讨论将对象(记录)作为元素存储在数据结构中的内容是不是太多了,情况确实如此。毕竟,数据结构的大多数实际应用都使用对象作为元素。学生应该习惯这一点。现在,要记在心里,本书前6章的内容只是有关类的讨论,因此,一旦我们习惯了将对象作为元素讨论,就会不再困惑,特别是由于在前6章中已经介绍了对象在类中的使用。
  第2章中还介绍了Stack数据结构,但是,在该章中我们实际上并没有详细分析它的内容。我们只是在练习的程序中使用栈,这有助于讲授抽象的重要性。
  第3章介绍const限定符和构造函数,还讨论了一些其他重要的内容:如何修改类。第3章中有两节是关于类的修改的(当然,也是以客户为中心)。
  第4章致力于指针和动态数组的使用,假定学生没有这方面的背景知识。在第4章中,我们还讨论了如何调整动态数组的大小。当然,动态数组的大小实际上并没有改变,而是生成一个不同大小的新动态数组,然后将旧数组中的元素复制过去,并将旧数组释放,最后用作旧数组“名称”的指针被赋予新数组的地址。当我们进行这些操作时,实际上数组永远不会填满(除非耗尽了堆内存)。稍后,在数据结构的数组实现中使用了这一思想,以节省内存,并且保证数组不被填满。例如,在栈中,当向数组中入栈一个元素时,数组中已经没有空间了,因此调用一个函数,在入栈元素之前将数组的大小增加一倍。同理,在元素出栈之后,当栈数组只使用了25%空间时,就将数组的大小收缩一半。通过后面的章节可以证实,在最坏的条件下,采用这种扩展-收缩策略时,每次元素入栈或者出栈在数组之间复制的元素平均不会超过两个。
  第5章介绍的是一个Array类,该类是其他与类相关的重要主题的基础,这些主题包括析构函数、重载赋值运算符和复制构造函数,以及当类的对象包含有动态分配的内存时为什么要使用它们的原因。通过使用这个简单的数组概念,这些重要的函数以及它们的工作方式就变得相当清晰。
  第6章有一节介绍组合,这是必需的,因为后面的章节将使用这一思想。第6章还有两个选读节,包括继承和多态,那些喜欢的或者必须学习的人可以学习这两个主题。再强调一次,书中有一些练习标记有星号,这表明这些练习需要使用继承。
  有了这些类的基础知识后,再介绍数据结构,学生就容易理解了。另外,还有可能提供更加漂亮的实现(不会产生混乱),例如使用可调数组,以及当乘数或除数是2的幂时,为提高速度使用的移位运算符。
  第7章讲授生成数据结构的方法。就是在该章中,我们讨论数据结构中动态数组的扩展和收缩策略。另外,该章还介绍链表。该章还讨论数组和链表在开销中浪费的内存量,从而使学生可以生成一个正式的决策,即在数据结构中使用数组实现还是链表实现。该章还示范了描述如何使用链表的代码段(最初没有使用类)。在这一讨论中,特别强调学生在编写这样的代码时,需要考虑地址,而不仅仅是箭头。实际上,我已尽最大的努力来示范在解决使用链表的问题时应该采用的思考过程。
  第8章讨论栈和队列,分别讨论两者的数组和链表实现。固定长度的数组实现没有讨论,如果数组太小,数组就容易填满;如果数组太大,数组就会浪费大量的内存。因此,本章中基于数组的实现都使用动态数组,动态数组可以随着元素数量的变化调整大小。有时候,当介绍较复杂的数据结构时,会先介绍一个基于固定长度数组的实现。但是,正如前面所述,理解这些实现需要前面章节中(使用普通类)介绍的许多概念。
  第9章整章讨论时间复杂度。听起来该章好像要深入讨论时间复杂度,但是我并没有这样做—— 本章讲授的内容只是确保能够深刻理解时间复杂度即可。这章分析影响数据结构选择的问题。本章采用了一种容易理解的方法来解释对数。并且还详细说明时间复杂度对计算速度的影响。
  第10章分析作为数据结构的链表。在第11章中,在使用链式技术的散列表中使用这个链表。HashTable类可以让客户编写最适合应用程序的散列函数。然后客户可以通过使用函数指针,将散列函数传递给HashTable构造函数,从而使HashTable函数可以调用客户编写的散列函数,而不用考虑客户在什么地方编写散列函数。第11章还讨论双向链表的实现,在双向链表中,任何元素只需花费Θ(1)时间就可以随意访问或者删除。这是通过使用散列表实现完成的。
  第12章讨论优先级队列,并介绍树和堆。与堆函数速度提升相关的实现问题包括:在向上或者向下重堆化(reheaping)时使用“单赋值交换”和移位运算符。这一章中有两节可选内容是关于链堆的(为了保持操作的时间复杂度与基于数组的堆的操作一样,链堆其实嵌入到一个更复杂的数据结构中)。
  递归在第13章中讨论。该章介绍一种理解递归的方式,即通过生成函数的副本来理解递归,通过这种方法可以极大地减少混淆。该章没有提供太多的递归示例,这是因为采用这种方式解释递归之后,就不需要太多的例子了。
  第14章介绍一些重要的排序算法,以及一些最低限度的算法分析,从而可以帮助学生学习算法分析课程。这一章有一节专门讨论链表的排序。
  二叉搜索树和图以一种开放式的方式在第15章中介绍。这里提醒学生,这些主题需要在一门更高级的课程中进一步地深入讨论。
  本书提供了一套完整的资源,可以从/childs上下载,这套资源包括下述内容:
● PowerPoint幻灯片,包括动作幻灯片,演示代码如何影响数据结构。这些幻灯片可以作为学习资源提供给学生。
● 非编程练习的答案。
● 书中全部例子的完整程序代码和类代码。这些代码都是使用Microsoft Visual Studio 2005 C++编译和测试的。这些代码可以从本书合作站点.cn下载。
● 一套测试题。
结构和类 1
类的基本概念 6
类的实现 9
类的测试 17
将函数定义放在类定义中 18
类的注释 20
结构和类之间的区别 21
重载运算符、类模板和抽象 25
重载运算符 25
在Checkbook类中使用Check结构 31
类和抽象 42
类的更多内容 51
const限定符 51
构造函数 53
类的修改 57
修改Checkbook类保存支票历史记录 57
指针和动态数组 69
[ ]运算符 75
动态分配内存 77
动态数组 79
delete操作符 80
对象指针 83
堆内存耗尽 84
可调数组 86
Array类 93
Array类模板 93
使用Array类 103
析构函数 106
复制构造函数 107
重载赋值运算符函数 113
Array类的优缺点 121
标准模板库 122
面向对象编程简介 125
生成数据结构的方法 147
在数据结构中使用数组 147
链式结构简介 151
链表编码 153
链表代码基础 153
在链表中搜索一个肯定存在的值 154
在链表中搜索可能不存在的值 157
在链表的表头插入一个结点 159
在链表中间插入一个结点 161
从链表中删除一个包含链表中某个值的结点 164
使用header结点简化代码 166
删除找到包含某值的结点 166
数组和链表的对比 167
数组和链表在速度上的比较 167
数组和链表在内存浪费上的比较 168
浪费内存分析 171
栈和队列 177
栈的数组实现 178
栈的链表实现 184
队列ADT 184
队列的链表实现 184
队列的其他链表实现 193
队列的数组实现 194
时间复杂度简介 211
时间复杂度基础 213
常量阶时间复杂度 222
大O表示法 223
对数阶时间复杂度 224
折半搜索算法 227
计算机速度:它来源于什么地方 231
数据结构函数的时间复杂度 232
数组扩展和收缩的平摊分析 232
链表作为数据结构 239
列表ADT 239
在信息记录中使用关键码值 240
链表实现 240
链表说明文件 241
链表实现文件 244
其他实现 255
散列表 263
散列表ADT 263
散列函数和散列表设计 263
散列表的实现问题 269
函数指针 271
散列表实现 272
使用散列表实现 277
双向链表的散列表实现 279
实现问题 283
DoublyLinkedList类的说明文件 284
DoublyLinkedList类的实现文件 287
优先级队列、树和堆 299
优先级队列ADT 299
优先级队列设计 299
使用单赋值交换 314
优先级队列的堆实现(基于数组) 316
链(内嵌)堆设计 324
优先级队列的链(内嵌)堆实现 329
递归阶乘函数 339
递归函数编写原则 344
在链式结构上使用递归 345
递归函数的时间复杂度 348
排序算法简介 351
堆排序 351
插入排序 353
快速排序 358
统计排序 361
链表排序 364
其他数据结构 369
二叉搜索树 369
BST和其他数据结构的对比 380
邻接矩阵和邻接表之间的对比 391
如何编译及使用多文件程序 397
Microsoft Visual Studio 2005 C++编译器 397
编译和运行使用类的代码(不是类模板) 397
编译和运行使用类模板的代码 398
使用Microsoft Visual Studio 2005编写代码 399
在Microsoft Visual Studio 2005中打开一个已创建的项目 400
何种情况下事情会变乱 400
UNIX编译器 4014606人阅读
在C++中内嵌汇编代码分析
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
用JAVA或者Python的人常常会告诉你,现在的硬件已经太快了,以至于你可以完全不再考虑性能,快速的开发才是最最重要的。这是个速食者的年代,什么都是,甚至是编程。
但是。。。。。。永远有但是,任何C/C++程序员在工作一段时间后都会发现,不关心性能的C/C++程序员是干不下去的。。。。。C语言的程序员犹甚,C++的程序员也许还可以光靠着MFC等类库混口饭吃。。。
C/C++程序员常常会发现自己只有有限的内存,要占用更小的CPU资源(或者仅仅只有速度非常有限的嵌入式CPU可用),但是却得完成非常复杂的任务,这时候能够利用的工具却非常有限。。。唯一可能有用的就是自己的大脑。。。呵呵,改进算法永远是最重要的,但是一个非常优秀的算法也没有办法满足要求的时候,你再剩下的东西可能就是无限的优化原有的C/C++代码,接着就是汇编了。
这里从内嵌汇编将起,然后讲讲用MASM独立编译,然后链接的方法,都以VS2005为例子,其他编译器请参考。
内嵌汇编:
内嵌汇编的使用是很简单并且方便的,在VS中以的形式就可以简单的使用。对于此语法我没有必要再多说了,各类资料都有介绍,可以参考参考《加密与解密》(第3版)附录2.这里想举几个的函数的调用例子来说明一下,因为在函数调用时需要特别注意调用的约定,各类调用约定有不同的规矩需要留意,因为一旦使用了汇编,出现问题没有东西可以保护你,唯一可以看到的就是程序崩溃。
对于各类调用约定也可以参考《加密与解密》(第3版),其中的分类还比较详细。
我在《&反汇编时的函数识别及各函数调用约定的汇编代码分析》,《&C++中通过指针,引用方式做返回值的汇编代码分析》中也有一些实例的分析,但是不能代替完整的说明,仅供参考。
http://blog.csdn.net/vagrxie/archive//3844768.aspx
http://blog.csdn.net/vagrxie/archive//3844520.aspx
以下形式就是最方便的内嵌汇编用法:
GetArgument(int ai)
int li = 0;
mov eax, ai;
mov li, eax
return li;
调用约定,需要注意的是,mov li, ai的形式是不允许的。。。。与在普通汇编中不允许从内存mov到内存的限制一致。
GetArgument(int ai)
mov eax, ai
就是返回值了。。。这和普通的汇编代码一致。。。。顺面看看生成的代码:
printf("%d/n",GetArgument(100));
0041142E& push&&&&&&& 64h&
& call&&&&&&& GetArgument (41100Ah)
& add&&&&&&&& esp,4
足够人性化的,栈由编译器自动的平衡了,不需要我们额外操心,这是很愉快的事情。
GetArgument(int ai)
mov eax, ai
,那么编译器还会帮我们吗?答案还是肯定的。所以也可以放心用。
0041142E& push&&&&&&& 64h&
& call&&&&&&& GetArgument (4111DBh)
& mov&&&&&&&& esi,esp
//& 仅仅返回参数
int __stdcall GetArgument(int ai)
& push&&&&&&& ebp&
& mov&&&&&&&& ebp,esp
& sub&&&&&&&& esp,0C0h
& push&&&&&&& ebx&
004113CA& push&&&&&&& esi&
004113CB& push&&&&&&& edi&
004113CC& lea&&&&&&&& edi,[ebp-0C0h]
& mov&&&&&&&& ecx,30h
& mov&&&&&&&& eax,0CCCCCCCCh
004113DC& rep stos&&& dword ptr es:[edi]
&&&&&& __asm
&&&&&&&&&&&&& mov eax, ai
004113DE& mov&&&&&&&& eax,dword ptr [ai]
& pop&&&&&&&& edi&
& pop&&&&&&&& esi&
& pop&&&&&&&& ebx&
& add&&&&&&&& esp,0C0h
004113EA& cmp&&&&&&&& ebp,esp
004113EC& call&&&&&&& @ILT+330(__RTC_CheckEsp) (41114Fh)
& mov&&&&&&&& esp,ebp
& pop&&&&&&&& ebp&
& ret&&&&&&&& 4&&&
其实只关心外部调用者没有维持栈平衡,而函数内部最后的ret 4就够了,完全符合__stdcall的定义。
再接下来呢?thiscall?
int GetArgument(int ai)
push&&&&&&& ebp&
mov&&&&&&&& ebp,esp
sub&&&&&&&& esp,0CCh
push&&&&&&& ebx&
push&&&&&&& esi&
push&&&&&&& edi&
push&&&&&&& ecx&
lea&&&&&&&& edi,[ebp-0CCh]
mov&&&&&&&& ecx,33h
mov&&&&&&&& eax,0CCCCCCCCh
rep stos&&& dword ptr es:[edi]
pop&&&&&&&& ecx&
mov&&&&&&&& dword ptr [ebp-8],ecx
mov eax, ai
mov&&&&&&&& eax,dword ptr [ai]
add eax, [ecx]
add&&&&&&&& eax,dword ptr [ecx]
pop&&&&&&&& edi&
pop&&&&&&&& esi&
pop&&&&&&&& ebx&
add&&&&&&&& esp,0CCh
cmp&&&&&&&& ebp,esp
call&&&&&&& @ILT+330(__RTC_CheckEsp) (41114Fh)
mov&&&&&&&& esp,ebp
pop&&&&&&&& ebp&
ret&&&&&&&& 4&
为this指针,并且函数内部维护栈的约定,这个例子也许得将测试程序也贴出来:
(int argc, _TCHAR* argv[])
CTestThisCall lo(20);
printf("%d/n",lo.GetArgument(10));
system("PAUSE");
,结果是正确的。
GetArgument(int ai)
mov eax, ecx
(int argc, _TCHAR* argv[])
printf("%d/n",GetArgument(10));
system("PAUSE");
的规则在VC中是已知的,但是在很多编译器中实现并不一样,所以很多书籍推荐最好不要使用fastcall来内嵌汇编,因为你不知道到底fastcall到底会使用哪些寄存器来传递参数。
In general, you should not assume that a register will have a given value when an __asm block begins. Register values are not guaranteed to be preserved across separate __asm blocks. If you end a block of inline code and begin another, you cannot rely on the registers in the second block to retain their values from the first block. An __asm block inherits whatever register values result from the normal flow of control.
If you use the __fastcall calling convention, the compiler passes function arguments in registers instead of on the stack. This can create problems in functions with __asm blocks because a function has no way to tell which parameter is in which register. If the function happens to receive a parameter in EAX and immediately stores something else in EAX, the original parameter is lost. In addition, you must preserve the ECX register in any function declared with __fastcall.
To avoid such register conflicts, don't use the __fastcall convention for functions that contain an __asm block. If you specify the __fastcall convention globally with the /Gr compiler option, declare every function containing an __asm block with __cdecl or __stdcall. (The __cdecl attribute tells the compiler to use the C calling convention for that function.) If you are not compiling with /Gr, avoid declaring the function with the __fastcall attribute.
,原因主要是不知道哪个寄存器用来传递参数,假如一个函数中你需要使用ecx一定要记得将其还原。。。呵呵,这个道理很简单,因为一个参数的时候fastcall必定是由ecx来传递参数的。两个参数就是ecx,edx,起码在VS2005中是这样。虽然个人没有感觉用起来有多大的风险,但是文中甚至提及可能通过eax传递参数。。(应该是在优化的时候),所以假如真的用了fastcall,请看汇编代码确认一下,以防万一。
GetArgument(int ai1, int ai2, int ai3)
mov eax, ecx
add eax, edx
add eax, [ebp+8]
(int argc, _TCHAR* argv[])
printf("%d/n",GetArgument(10, 20, 30));
system("PAUSE");
&&&&&& printf("%d/n",GetArgument(10, 20, 30));
00411ACE& push&&&&&&& 1Eh&
00411AD0& mov&&&&&&&& edx,14h
00411AD5& mov&&&&&&&& ecx,0Ah
00411ADA& call&&&&&&& GetArgument (4111F4h)
int __fastcall GetArgument(int ai1, int ai2, int ai3)
& push&&&&&&& ebp&
& mov&&&&&&&& ebp,esp
& sub&&&&&&&& esp,0D8h
& push&&&&&&& ebx&
004130BA& push&&&&&&& esi&
004130BB& push&&&&&&& edi&
004130BC& push&&&&&&& ecx&
004130BD& lea&&&&&&&& edi,[ebp-0D8h]
& mov&&&&&&&& ecx,36h
& mov&&&&&&&& eax,0CCCCCCCCh
004130CD& rep stos&&& dword ptr es:[edi]
004130CF& pop&&&&&&&& ecx&
& mov&&&&&&&& dword ptr [ebp-14h],edx
& mov&&&&&&&& dword ptr [ebp-8],ecx
&&&&&& __asm
&&&&&&&&&&&&& mov eax, ecx
& mov&&&&&&&& eax,ecx
&&&&&&&&&&&&& add eax, edx
& add&&&&&&&& eax,edx
&&&&&&&&&&&&& add eax, [ebp+8]
004130DA& add&&&&&&&& eax,dword ptr [ai3]
004130DD& pop&&&&&&&& edi&
004130DE& pop&&&&&&&& esi&
004130DF& pop&&&&&&&& ebx&
& add&&&&&&&& esp,0D8h
& cmp&&&&&&&& ebp,esp
& call&&&&&&& @ILT+330(__RTC_CheckEsp) (41114Fh)
004130ED& mov&&&&&&&& esp,ebp
004130EF& pop&&&&&&&& ebp&
& ret&&&&&&&& 4&&&
说实话,ebp+8的计算我原来是搞错了,我当时直接用了esp+8,但是后来才发现在debug没有优化的时候,此处是会采用先保存esp,然后再用ebp的标准用法的。这里没有任何可健壮和移植性可言,仅仅是为了说明thiscall其实也是有规则并且符合约定的,比如上述程序在VS2005最大速度优化时也能正确运行,反汇编代码如下:
int _tmain(int argc, _TCHAR* argv[])
&&&&&& printf("%d/n",GetArgument(10, 20, 30));
& mov&&&&&&&& edx,14h
& push&&&&&&& 1Eh&
& lea&&&&&&&& ecx,[edx-0Ah] ;注意此处,是编译器相当强程序优化的 成果,用此句替代了mov ecx,0Ah,原因说白了就很简单,因为此句才3个字节,而用mov的需要5个字节。。。。。
0040101A& call&&&&&&& GetArgument (401000h)
0040101F& push&&&&&&& eax&
& push&&&&&&& offset string "%d/n" (4020F4h)
& call&&&&&&& dword ptr [__imp__printf (4020A4h)]
&&&&&& system("PAUSE");
0040102B& push&&&&&&& offset string "PAUSE" (4020F8h)
& call&&&&&&& dword ptr [__imp__system (40209Ch)]
& add&&&&&&&& esp,0Ch
&&&&&& return 0;
& xor&&&&&&&& eax,eax
&&&&&&&&&&&&&& push&&&&&&& ebp&
B EC&&&&&&&&&&& mov&&&&&&&& ebp,esp
&&&&&& __asm
&&&&&&&&&&&&& mov eax, ecx
B C1&&&&&&&&&&& mov&&&&&&&& eax,ecx
&&&&&&&&&&&&& add eax, edx
C2&&&&&&&&&&& add&&&&&&&& eax,edx
&&&&&&&&&&&&& add eax, [ebp+8]
45 08&&&&&&&& add&&&&&&&& eax,dword ptr [ai3]
D&&&&&&&&&&&&&& pop&&&&&&&& ebp&
0040100B C2 04 00&&&&&&&& ret&&&&&&&& 4&&
最后,在VS2005中还有一个专门为内嵌汇编准备的函数调用方式,naked。。。裸露的调用方式?-_-!呵呵,其实应该表示是无保护的。
(naked) __stdcall GetArgument(int ai)
mov eax, [esp+04H]
(int argc, _TCHAR* argv[])
printf("%d/n",GetArgument(10));
system("PAUSE");
反汇编代码:
printf("%d/n",GetArgument(10));
& push&&&&&&& 0Ah&
& call&&&&&&& GetArgument (401000h)
& push&&&&&&& eax&
& push&&&&&&& offset string "%d/n" (4020F4h)
0040101D& call&&&&&&& dword ptr [__imp__printf (4020A4h)]
&&&&&& system("PAUSE");
& push&&&&&&& offset string "PAUSE" (4020F8h)
& call&&&&&&& dword ptr [__imp__system (40209Ch)]
0040102E& add&&&&&&&& esp,0Ch
&&&&&& return 0;
& xor&&&&&&&& eax,eax
mov eax, [esp+04H]
& mov&&&&&&&& eax,dword ptr [esp+4]
&&&&&&&&&&&&& ret 4
& ret&&&&&&&& 4
哈哈,在debug下naked函数都是如此简洁,因为我们对其有完完全全的控制,非常完全!以至于。。。在这里你想用mov eax,ai的形式都是不可以的。。。只能完全遵循普通汇编的规则来走。
但是无论此函数再怎么naked,VS也不是完全不管的,见下例7:
(naked) __cdecl GetArgument(int ai)
mov eax, [esp+04H]
(int argc, _TCHAR* argv[])
printf("%d/n",GetArgument(10));
system("PAUSE");
假如VS完全不管GetArgument函数,因为GetArgument函数内部无法维护栈平衡,那么程序崩溃是必然的,还好VS管理了这个问题:
printf("%d/n",GetArgument(10));
00411ACE& push&&&&&&& 0Ah&
00411AD0& call&&&&&&& GetArgument (4111FEh)
00411AD5& add&&&&&&&& esp,4
这里需要说明的是,当一个函数naked的时候,完全的指挥权都在程序员手中,包括栈的平衡,函数的返回都需要程序员来做,ret是必不可少的,这里想说明的是,一旦你使用了内嵌汇编,你就开始走在了悬崖边上,因为C++强健的编译时期类型检查完全帮助不了你,能够帮助你的仅仅是你的大脑和你对汇编的理解了。呵呵,既然是用汇编写代码,其实都没有反汇编的概念可言,源代码就在哪儿,看仔细了debug比什么都重要,ollydbg,softICE,windbg你爱用什么就用什么吧。
另外,当函数不是naked的时候,从理论上讲你也是可以在汇编中ret的,只不过,你冒的风险也更大了,因为VS好像还不够智能化,它无法预见你的ret,或者从设计上来讲,不是naked的时候你不应该自己处理ret的,所以,即使是你在内嵌汇编中已经有了ret,它还是会原封不动的用于函数返回的代码。。。。
GetArgument(int ai)
mov eax, ai
mov esp,ebp
(int argc, _TCHAR* argv[])
printf("%d/n",GetArgument(10));
system("PAUSE");
上例在VS2005中最大速度优化,内联选项为仅仅在有inline声明才内联时可以运行正确,见汇编代码:
printf("%d/n",GetArgument(10));
& call&&&&&&& GetArgument (401000h)
& push&&&&&&& eax&
& push&&&&&&& offset string "%d/n" (4020F4h)
0040102B& call&&&&&&& dword ptr [__imp__printf (4020A4h)]
&&&&&& system("PAUSE");
& push&&&&&&& offset string "PAUSE" (4020F8h)
& call&&&&&&& dword ptr [__imp__system (40209Ch)]
0040103C& add&&&&&&&& esp,0Ch
&&&&&& return 0;
0040103F& xor&&&&&&&& eax,eax
& push&&&&&&& ebp&
& mov&&&&&&&& ebp,esp
& push&&&&&&& ecx&
& mov&&&&&&&& dword ptr [ai],0Ah
&&&&&& __asm
&&&&&&&&&&&&& mov eax, ai
0040100B& mov&&&&&&&& eax,dword ptr [ai]
&&&&&&&&&&&&& mov esp,ebp
0040100E& mov&&&&&&&& esp,ebp
&&&&&&&&&&&&& pop ecx
& pop&&&&&&&& ecx&
&&&&&&&&&&&&& ret
& ret&&&&&&&&&&&&&
& mov&&&&&&&& esp,ebp
& pop&&&&&&&& ebp&
& ret&&&&&&&&
当你在不应该ret的时候ret,带来的往往是灾难性的后果,所以此处你必须的预见(或者先反汇编看到)原程序会做的所有操作,并且完整的重复它们,不然你在一个非naked的函数中用ret,结果几乎肯定是程序的崩溃。无论如何,除非你有特殊原因,还是按规矩办事比较好。
其实这样的代码对于反汇编来说还挺有意思的:IDA中可以看到
.text:&&&&&&&&&&&&&&&&&&&&& ; ===========================================================================
.text:&&&&&&&&&&&&&&&&&&&&& ; Segment type: Pure code
.text:&&&&&&&&&&&&&&&&&&&&& ; Segment permissions: Read/Execute
.text:&&&&&&&&&&&&&&&&&&&&& _text&&&&&&&&&& segment para public 'CODE' use32
.text:&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&assume cs:_text
.text:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ;org 401000h
.text:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.text:&&&&&&&&&&&&&&&&&&&&& ; =============== S U B R O U T I N E =======================================
.text:&&&&&&&&&&&&&&&&&&&&& ; Attributes: bp-based frame
.text:&&&&&&&&&&&&&&&&&&&&& GetArgument&&&& proc near&&&&&&&&&&&&&& ; CODE XREF: _mainp
.text:&&&&&&&&&&&&&&&&&&&&& var_4&&&&&&&&&& = dword ptr -4
.text:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& push&&& ebp
.text:B EC&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& mov& &&&ebp, esp
.text:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& push&&& ecx
.text: 45 FC 0A 00 00 00&&&&&&&&&&&&&&&& mov&&&& [ebp+var_4], 0Ah
.text:B 45 FC&&&&&&&&&&&&&&&&&&&&&&&&&&&& mov&&&& eax, [ebp+var_4]
.text:B E5&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&mov&&&& esp, ebp
.text:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& pop&&&& ecx
.text:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& retn
.text:&&&&&&&&&&&&&&&&&&&&& GetArgument&&&& endp
.text:&&&&& &&&&&&&&&&&&&&&&; ---------------------------------------------------------------------------
.text:B E5&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& mov&&&& esp, ebp
.text:D&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& pop&&&& ebp
.text:&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&retn
多余的代码IDA人性的分割掉了,这在没有源代码的人那里过去了就过去了,要是真深究的话,可能还会以为因为花指令的影响导致代码反汇编错了。。。呵呵,不然也不会有3行代码无缘无故的被分割在那里,一看也不像数据段。。。。。
另外push ecx,pop ecx的处理也是编译器极端优化的一个例子,这里本来可以用sub,add修改esp的做法的,但是那样的话一条指令就是5个字节。。。这样的话才1个字节!真是极端的优化啊。。。。。
参考资料:
2.《加密与解密》(第3版)附录2
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1828010次
积分:22645
积分:22645
排名:第103名
原创:428篇
转载:22篇
评论:2780条
公司一直在招聘Android应用开发, 有兴趣的见
此博客将为的编程相关内容的镜像站, 除了编程以外的内容这里就不发了, 毕竟是CSDN嘛.
(1)(1)(1)(1)(2)(2)(6)(1)(1)(2)(1)(5)(1)(2)(2)(5)(1)(1)(4)(5)(7)(5)(9)(28)(9)(4)(13)(4)(9)(11)(14)(18)(9)(18)(6)(25)(13)(6)(15)(16)(8)(16)(22)(11)(4)(2)(9)(2)(10)(7)(1)(4)(4)(3)(7)(5)(7)(16)(9)(27)

我要回帖

更多关于 移位实现除法 的文章

 

随机推荐