C系列编程语言内部数据传递机制研究本科毕业论文_第1页
C系列编程语言内部数据传递机制研究本科毕业论文_第2页
C系列编程语言内部数据传递机制研究本科毕业论文_第3页
C系列编程语言内部数据传递机制研究本科毕业论文_第4页
C系列编程语言内部数据传递机制研究本科毕业论文_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、四川大学锦江学院毕业论文(设计)毕业论文(设计)【摘 要】数据传递是计算机通信中一个十分重要的问题。计算机与外部硬件设备的数据传递一般是通过C语言或汇编语言实现的。因此C系列编程语言内部数据传递机制研究就显得尤为重要。本文主要研究了计算机硬件系统以及单片机之间的数据传递问题。C语言是采用了模块化结构,其本身是一种结构化程序设计语言,模块化结构就是把一个大问题逐步分解成若干独立的小问题,然后再把这些小问题在分解成若干个简单的子问题直到这些子问题或小问题能直接用程序的三种基本结构表达出来。然后,对应每一个小问题或子问题编写出一个功能相对独立的程序块来,这些小程序块就称作模块,而每个模块由功能不一样

2、的函数组成。可以说C语言是由函数组成的。一个C源程序至少包含一个主函数(main函数)也可以包含一个MAIN函数和其他若干个函数,由主函数调用其他函数。其他函数也可以相互调用。每个函数在程序中都用来执行一个特定的功能。对于同一个程序中的函数并不是完全独立的。它们需要通过相互之间调用组成一个有机的整体来实现程序所要完成的任务。其中数据传递是一个极为重要而又特殊的问题,它直接影响程序的功能。显然不同函数间的数据传递就成了一个不可忽视的问题。【关键词】 C语言 数据传递机制 研究 Study on the mechanism of the C series programming language

3、internal data transfer【Abstract】Data transmission is a very important problem in computer communication. Data transfer is through the C language and assembly language to achieve. Therefore it is important to C series programming language internal data transfer mechanism research. This paper mainly s

4、tudies between computer hardware and the MCU data transfer.The C language is adopted modular structure, which itself is a structured programming language, the modular structure is the small problem is a big problem gradually decomposed into several independent, then put these little problems express

5、ed in three kinds of basic structure is divided into several simple problems until the problem or small problems can be directly with the program. Then, each corresponding to a small problems or subproblems write block a function relatively independent, these small block is called a module, and the

6、function of each module is composed of function is not the same as the composition. It can be said that the C language is a function of the composition. A C source code that contains at least one main function (main function) can also contain a MAIN function and a number of other functions, from the

7、 main function call other functions. The other function can call each other. Each function is used to perform a specific function in the program. For the and function the same program are not completely independent. They need through the interaction between calls to form an organic whole to implemen

8、t procedures to complete the task. The data transfer is a very important and special problems, which directly affect the function of the procedure. Obviously, the data transfer between different function has become a problem that can not be ignored. Research on transmission mechanism of internal dat

9、a of C series programming language has become a necessity in C language program design. 【Key words】 C Data transmission mechanism consider目 录引 言11. 通信的基本概念1.1并行与串行的概念21.1.1并行通信31.1.2串行通信51.2几种数据传送(控制)方式的利与弊62.单片机的C语言参数传递机制研究62.1 1个或2个参数传递的寄存器数据传递62.2 通过堆栈操作实现参数的传递83.MOV指令实现数据传递184. 数据结构(C语言)概述194.1C

10、语言背景和数据类型195. C语言函数返回值实现数据传递的利弊205.1Return的使用206. C语言函数参数实现数据传递方法机制226.1无参函数的数据传递机制226.2有参函数的数据传递236.2.1参数为简单变量数据传递研究及其特点236.2.2参数为非简单变量数据传递研究及其特点24 6.3指针参数实现“双向传递”研究257.全局变量实现数据传递机制研究26总结以及展望28参考文献29附录30致谢.30 - III - 引言 数据传递是计算机通信中一个十分重要的问题。PC与外围设备数据传递一般是通过C语言或汇编语言实现的。因此C系列编程语言内部数据传递机制研究就显得尤为重要。本文主

11、要研究了计算机硬件系统以及PC与单片机之间的数据传递问题。C语言是采用了模块化结构,其本身是一种结构化程序设计语言,模块化结构就是把一个大问题逐步分解成若干独立的小问题,然后再把这些小问题在分解成若干个简单的子问题直到这些子问题或小问题能直接用程序的三种基本结构表达出来。然后,对应每一个小问题或子问题编写出一个功能相对独立的程序块来,这些小程序块就称作模块,而每个模块由功能不一样的函数组成。可以说C语言是由函数组成的。一个C源程序至少包含一个主函数(main函数)也可以包含一个MAIN函数和其他若干个函数,由主函数调用其他函数。其他函数也可以相互调用。每个函数在程序中都用来执行一个特定的功能。

12、对于同一个程序中的函数并不是完全独立的。它们需要通过相互之间调用组成一个有机的整体来实现程序所要完成的任务。其中数据传递是一个极为重要而又特殊的问题,它直接影响程序的功能。显然不同函数间的数据传递就成了一个不可忽视的问题。1通信的概念通信,指人与人或人与自然之间通过某种行为或媒介进行的信息交流与传递,从广义上指需要信息的双方或多方在不违背各自意愿的情况下无论采用何种方法,使用何种媒质,将信息从某方准确安全传送到另方。通信在不同的环境下有不同的解释,在出现电波传递通信后通信(Communication)被单一解释为信息的传递, 是指由一地向另一地进行信息的传输与交换,其目的是传输消息。然而,通信

13、是在人类实践过程中随着社会生产力的发展对传递消息的要求不断提升使得人类文明不断进步。1.1并行与串行的概念¢ 在微型计算机中,通信(数据交换)有两种方式:并行通信和串行通信。¢ 并行通信是指计算机与I/O设备之间通过多条传输 线交换数据,数据的各位同时进行传送。 ¢ 串行通信是指计算机与I/O设备之间仅通过一条传 输线交换数据,数据的各位是按顺序依次一位接 一位进行传送。¢ 应该理解所谓的并行和串行,仅是指I/O接口与I/O设备之间数据交换(通信)是并行或串行。无论怎样CPU与I/O接口之间数据交换总是并行。 并行通信串行通信传输速率高传输速率相对较低传

14、输距离短(<30m)通信距离长(几米至几公里)成本高(要采用多条数据线)成本较低应用:如电脑输出数据到打印机;51单片机的P口应用:电脑上的9针座(人称串口);51单片机的全双工异步串行口RXD、TXD 1.1.1并行通信定义:在计算机和终端之间的数据传输通常是靠电缆或信道上的电流或电压变化实现的。如果一组数据的各数据位在多条线上同时被传输,这种传输方式称为并行通信。编辑本段分类终端与其他设备(例如其他终端、计算机和外部设备)通过数据传输进行通信。在数据通信中,按每次传送的数据位数,通信方式可分为:并行通信和串行通信。并行通信时数据的各个位同时传送,可以字或字节为单位并行进行。并行通信速

15、度快,但用的通信线多、成本高,故不宜进行远距离通信。计算机或PLC各种内部总线就是以并行方式传送数据的。工作原理特点1、各数据位同时传输,传输速度快、效率高,多用在实时、快速的场合。2、微机系统中最基本的信息交换方式。3、并行传递的信息不要求固定的格式。4、并行接口的数据传输率比串行接口快8倍,标准并口的数据传输率理论值为1Mbps(兆比特/秒). 5、并行传输的数据宽度可以是1128位,甚至更宽,但是有多少数据位就需要多少根数据线,因此传输的成本较高。6、并行通信抗干扰能力差。7、在集成电路芯片的内部、同一插件板上各部件之间、同一机箱内个插件板之间的数据传输都是并行的。8、以计算机的字长,通

16、常是8位、16位或32位为传输单位,一次传送一个字长的数据。9、适合于外部设备与微机之间进行近距离、大量和快速的信息交换。10、并行数据传输只适用于近距离的通信,通常传输距离小于30米。并行接口一般地并行接口有 3 个方面的功能:实现与系统总线的连接:提供数据的输入输出功能。实现与外设连接:确保与外设间有效进行数据的接收和发送。具有中断请求处理功能:外设输入输出采用中断的方法来实现。接口连接典型的双向并行接口与外设连接1 行接口与CPU的连接数据总线:是CPU与并行接口进行数据交换的通道。读出写入信号线:控制数据流向,确定操作是读还是写。复位线,准备好状态线:并行接口数据准备就绪。中断请求线:

17、并行接口向CPU进行中断请求。地址译码电路:进行选择不同的接口电路,选择接口电路内部不同的寄存器。并行接口与外设的连接输入设备:数据输入线,设备数据准备就绪状态线和接口接收数据回答线。 输出设备:数据输出线,接口数据准备就绪状态线和外设接收数据回答线。并行接口控制寄存器:接收CPU发来的控制命令。数据输入缓冲器、数据输出缓冲器:进行数据的输入、输出。状态寄存器:提供接口电路工作状态供CPU查询。传输原理 并行接口输入数据的过程 外设将数据送到“数据输入线”,通过“输入数据准备好” 状态线通知并行接口取走,接口将数据锁存到“输入缓冲器”,通过“数据输入回答”线通知外设,接口数据缓冲器已满,不要再

18、送数据,接口在其内“状态寄存器” 的相应位置 1,便于CPU 查询和接口向CPU 发中断请求之用。 CPU 从接口将数据取走后,接口将“数据输入准备好”、“数据输入回答” 信号清除,以便外设输入下一个数据。 并行接口输出数据的过程 接口“数据输出缓冲器” 空,“数据输出准备好” 状态线送 1,收到CPU 发的数据,将之复位清0,数据通过“数据输出” 线送外设,由“数据输出准备好” 线通知外设取数据。实例微机系统中最基本的信息交换方法例如:微机与并行接口打印机、磁盘驱动器例如:系统板上各部件之间,接口电路板上各部件之间1.1.2串行通信串行通信可以分为:同步通信(Synchronous Comm

19、unication)和异步通信(Asynchronous Communication)两类。在单片机中,主要使用异步通信方式。 (1)异步通信 ¢ 特点:数据是以字符为信息单位传送的。每个字符由起始位、数据位、奇偶校验位和停止位四部分组成,称为一帧。 每帧数据只包括一个字符。字符与字符之间的传送是异步的,而每个字符内部的位传送是同步的。 ¢ 其典型格式为: 1.2几种数据传送(控制)方式的利与弊CPU与I/O设备之间的几种数据传送方式1、程序查询方式2、程序中断3、直接内存访问4、通道方式5、外围处理机方式 数据传送控制方式有程序直接控制方式、中断控制方式、DMA方式和通道

20、方式4种。 程序直接控制方式就是由用户进程来直接控制内存或CPU和外围设备之间的数据传送。它的优点是控制简单,也不需要多少硬件支持。它的缺点是CPU和外围设备只能串行工作;设备之间只能串行工作,无法发现和处理由于设备或其他硬件所产生的错误。 中断控制方式是利用向CPU发送中断的方式控制外围设备和CPU之间的数据传送。它的优点是大大提高了CPU的利用率且能支持多道程序和设备的并行操作。它的缺点是由于数据缓冲寄存器比较小,如果中断次数较多,仍然占用了大量CPU时间;在外围设备较多时,由于中断次数的急剧增加,可能造成CPU无法响应中断而出现中断丢失的现象;如果外围设备速度比较快,可能会出现 CPU来

21、不及从数据缓冲寄存器中取走数据而丢失数据的情况。 DMA方式是在外围设备和内存之间开辟直接的数据交换通路进行数据传送。它的优点是除了在数据块传送开始时需要CPU的启动指令,在整个数据块传送结束时需要发中断通知CPU进行中断 处理之外,不需要CPU的频繁干涉。它的缺点是在外围设备越来越多的情况下,多个DMA控制 器的同时使用,会引起内存地址的冲突并使得控制过程进一步复杂化。 通道方式是使用通道来控制内存或CPU和外围设备之间的数据传送。通道是一个独立与CPU的专管 输入输出控制的机构,它控制设备与内存直接进行数据交换。它有自己的通道指令,这些指令受CPU启动,并在操作结束时向CPU发中断信号。该

22、方式的优点是进一步减轻了CPU的工作负担,增加了计算机系统的并行工作程度。缺点是增加了额外的硬件,造价昂贵 .2. 单片机的C语言参数传递机制研究2.1 1个或2个参数传递的寄存器数据传递寄存器数据传递参数传递到哪里取决于参数的大小和数量。如果只传递很少的字节数,寄存器是用来向过程传递参数的极好位置。如果只向过程传递一个参数,应该使用下列与相应数据类型对应的寄存器:这并不是一个严格的规则。如果发现使用寄存器SI或BX传递16位数值更方便,完全可以这样做。然而,多数程序员习惯于使用上面的寄存器传递参数。如果使用80x86的寄存器向过程传递多个参数,应该按照下面的顺序来使用寄存器:先 后 eax,

23、edx,ecx,esi,edi,ebx 通常情况下,应该避免使用寄存器EBP。如果需要传递的数值超过6个双字,应该使用其他的位置传递数值。这种优先选择顺序不是完全任意的。许多高级语言会试图使用寄存器EAX、EDX和ECX传递参数(通常按这个顺序)。而且,Intel的ABI(application binary interface,应用程序二进制接口)允许高级语言的过程在使用寄存器EAX、EDX和ECX时不必保存它们的值。因此,这3个寄存器是传递参数的主要位置,因为许多代码都假定它们的数值在过程调用的时候被修改。例如,考虑下面的过程strfill(s,c);,它将字符c(通过值传递到寄存器AL中

24、)在字符串s(引用方式传递到寄存器EDI中)中的每一个字符位置进行一次复制,直到出现0结束字节:/ strfill- Overwrites the data in a string with a character. / / EDI- Pointer to zero-terminated string (e.g., an HLA string) / AL- Character to store into the string procedure strfill; nodisplay; begin strfill; push( edi ); / Preserve this because it

25、will be modified. while( (type char edi ) <> #0 ) do mov( al, edi ); inc( edi ); endwhile; pop( edi ); end strfill; 在调用过程strfill之前,应首先将字符串数据的地址写入寄存器EDI,同时把字符数据保存在AL中。下面的代码段是对过程strfill的调用示例: mov( s, edi ); / Get ptr to string data into edi (assumes s:string). mov( ' ', al ); strfill();

26、不要忘记,HLA的字符串变量是指针。这个示例假定s是HLA的字符串变量,因此包含一个指向0结束字符串的指针。因此,指令mov(s,edi);将0结束字符串的地址写入寄存器EDI(因此,这句代码将字符串数据的地址传递给strfill,也就是使用引用方式传递字符串)。通过寄存器进行参数传递的一种方法是在调用前写入参数的相应值,而后在过程内部对这些寄存器进行引用。在汇编语言编程中,这是一种使用寄存器传递参数的传统机制。HLA与传统的汇编语言相比相对高级,它提供了一种形参声明语法以告知HLA正在使用通用寄存器传递某个特定参数。这种声明语法为:parmName: parmType in reg 其中pa

27、rmName是参数名称,parmType是参数类型,reg是一个80x86的8位、16位或32位的通用寄存器。参数类型的数据宽度必须与寄存器的容量相同,否则HLA会报出错误。下面是一个具体示例:procedure HasRegParms( count: uns32 in ecx; charVal:char in al ); 这种语法有一个很好的特性,它使您可以像调用其他过程一样使用HLA的高级语法调用一个含有寄存器参数的过程,例如: HasRegParms( ecx, b1 );如果指定与所声明的形参相同的寄存器作为实参,HLA将不再发出其他任何额外代码,它假定参数的数值已经保存在相应的寄存器

28、中。例如,上述调用中的第一个实参就是寄存器ECX中的值,因为过程声明指定了第一个参数在ECX中,故HLA将不再发出任何代码。另一方面,第二个实参在寄存器BL中,但过程却希望这个参数在AL中。因此,HLA会在调用过程之前发出指令mov(bl,al);。这样,在过程的入口点,参数的数值就已经保存在相应的寄存器中了。同样可以通过寄存器使用引用方式传递参数。考虑下面的声明:procedure HasRefRegParm( var myPtr:uns32 in edi );对这个过程的调用需要存储器操作数作为实参。HLA将发出代码把存储器对象的地址写入参数的寄存器(本例中是指EDI)。需要注意的是,由于

29、地址是32位数据宽度的,因此,传递引用参数时必须使用32位的通用寄存器。下面是一个调用HasRefRegParm的示例:HasRefRegParm( x );在调用指令call之前,HLA将发出mov(&x,edi);指令或lea(edi,x);指令,目的是把x的地址写入寄存器EDI中18。如果把一个匿名存储器对象(如edi或ecx)作为参数传递给过程HasRefRegParm,同时,存储器引用所使用的恰好是您为参数声明的那个寄存器(即edi),那么,HLA将不再生成任何代码。如果不使用寄存器EDI,而指定另一个间接寻址方式的寄存器(如ecx),HLA将使用一条简单的mov指令把实际地

30、址复制到寄存器EDI中。如果使用更加复杂的寻址方式如edi+ecx*4+2,HLA将使用lea指令来计算匿名存储器操作数的有效地址。在过程代码内部,HLA会为寄存器参数创建等价的文字名称,并把它们映射到相应的寄存器。在HasRegParms的示例中,无论何时引用参数count,HLA都会用ecx取代count。同样地,在过程体内部,HLA使用al取代charVal。由于这些都是寄存器的别名,因此需要当心,不能再独立于这些名称使用ECX和AL。一个很好的编程习惯是在这些参数的每一次使用后都加上一条注释2.2通过堆栈操作实现参数的传递函数,相信许多人也知道其重要性;一个文件往往由一个或者多个函数构

31、成的。函数调用还有一些深层问题, 一、基本概念:1.堆栈:所谓堆栈,其实也就是程序使用的一种内存元素;它是内存中用来存放一些数据的区域。堆和栈的区别;平常经常说的堆栈,其实也是栈,而不是堆,所以这里也一样。注意这和数据结构说的栈其实还是有区别的,不要混在一起。2.堆栈的工作方式:平常我们所说的数据是怎么存放在内存的?是从低地址开始,然后按照数据占用字节大小往高地址逐个存放的。但堆栈就不一样了。堆栈的工作方式是数据插入堆栈区域然后从堆栈区域删除数据。这是概括的说法。具体是这样的:在UNIX/LINUX 中,堆栈是从高地址向低地址衍生的。这里得说一个重要的东西,那就是堆栈指针ESP。堆栈指针是什么

32、?它永远指向堆栈中的顶部(但如果按照地址值来说却是底部),是不是对顶部这个词的理解感觉有点模糊?就是说,比如说你压栈,就压进一个4字节的数据元素,那么ESP就向下移动了4个字节,注意这里是向下移动,所以ESP应该指向了更低的地址,所以说它是指向了底部。你可以把堆栈想象成一个杯子,倒进水了水平线是不是上升了(这里把杯子最底端假设成高地址,把顶端设为低地址),倒出水了水平线是不是下降了?就和压栈和进栈的道理一样的。3.压栈和进栈指令简介:压栈指令 : pushx source其中, 'x'可以是 'w'(表示字), 或者是'l'(表示长字);sour

33、ce可以是数值或者寄存器值或者内存地址;出栈指令 : popx des同样,'x'可以是 'w'(表示字), 或者是'l'(表示长字);des可以是寄存器值或内存值;关于最最基本的东西已经讲得差不多了。二、函数如何通过堆栈来解决问题:这部分是对函数如何通过堆栈解决函数调用以及参数传递的理论性理解,相当重要,只有了解之后才可以进行实例的分析,这一大部分同样分成几个小部分:1.通过堆栈操作实现参数的传递:前面说过,堆栈的基本操作可以是压栈和出栈,而参数的传递就是通过这种方式来实现的。ESP永远指向了堆栈顶部,如果这时候压进一个int型的数据元素,那么

34、ESP向下移动了4个字节,这时候它还是指向了堆栈的顶部(注意了,顶部的地址比移动前的地址低,不要乱了)。假如把一个int型数据元素出栈,那么ESP向上移动4个字节,这时候它还是指向了堆栈的顶部,只是现在地址是增加了4个字节。所以,如果一个函数需要传递参数过去那么就得在调用函数之前先把参数压进栈,然后再调用。2.函数调用的一般汇编指令:函数调用的一般汇编指令都是那么几条,下面我把他们按一般顺序罗列出来:#Asm Codefunction:pushl %ebpmovl %esp, %ebpsubl $8, %esp#.movl %ebp, %esppopl %ebpret下面先简单分析这几句一般汇

35、编指令的意思和目的。pushl %ebp #这句把寄存器%ebp压栈,目的是什么呢?看下一条指令:movl %esp, %ebp #把寄存器%esp的值给了寄存器%ebp;想想前面说到的%esp寄存器是干什么的?用于指向堆栈的顶部,现在通过这条指令,%ebp都是指向了堆栈的顶部了;所以看看第一条指令,其实就是为了保护原来在%ebp寄存器中的内容#那么这里为什么又要把%esp的值赋给%ebp呢?这里的巧妙就来了。在函数的处理过程中,可能一些数据会被压进栈,那么这时候就会破坏栈里面原有的内容了,如果栈的内容被破坏了,指向栈顶的指针%esp指向的地址不准确了,那么到时候要清栈就会发生更多的意外问题了

36、。清栈下面也会给出解释。所以第二条指令是为了保证有一个寄存器永远指向了栈顶而不必担心会出现刚才所说的问题。现在是寄存器%ebp永远指向栈顶了,而%esp可以移动而不必害怕数据会被破坏了。subl $8, %esp #看这条指令,为什么无故要把%esp的值减去8呢?也就是说%esp向下移动8个字节,而这8个字节的空间到底用来干什么呢?这8个字节空间其实是为临时变量留出来的。注意,它会根据临时变量占用的字节大小而留出不同的空间大小,所以不一定是8个字节,可能是24或者36甚至更大的空间;不过临时变量太多不是好事情,这点注意。movl %ebp, %esp #这条指令把%ebp复制到%esp了,理由

37、是什么?让%esp重新指向栈顶,这样就可以方便函数调用完毕后的清栈了。ret #函数调用完毕的返回指令,这句指令其实同时把函数调用刚刚开始压进的IP地址弹出栈。在下面会有详细分析。关于函数如何通过堆栈来解决问题的基本理论大概就说到这里,下面第3个部分通过实例来分析三、函数调用和参数传递机制的实例分析:这是实战分析部分了,通过例子来加深一下理解。先列出C代码出来,然后列出反汇编的汇编代码,结合C代码来分析汇编代码。1. 函数原型:void function(void);/ C codevoid function(void)return;int main(void)function();retur

38、n 0;反汇编一下看看汇编代码,下面是Linux 下的gcc反汇编后的代码(注意:是在我的机子上的反汇编代码):function:pushl %ebpmovl %esp, %ebppopl %ebpret看看,因为函数function什么也没有做,所以直接就返回了,上面的指令和第2部分的代码基本上一样,甚至更简单,参照一下前面的分析下面看看main函数的反汇编代码了,相对来说复杂一点,看好了:main:pushl %ebpmovl %esp, %ebpsubl $8, %espandl $-16, %espmovl $0, %eaxaddl $15, %eaxaddl $15, %eaxshr

39、l $4, %eaxsall $4, %eaxsubl %eax, %espcall function #函数调用指令movl $0, %eaxleaveret看看函数调用指令 : call function,前面居然还有那么多据指令,那些指令到底干什么用? pushl %ebpmovl %esp, %ebpsubl $8, %esp这三句不分析,和前面第2部分的一样,忘记的回头看一下,其实这也反映了一件事:其实main函数也很普通,它跟其他函数其实差不多,只是地位稍微高一点而已。andl $-16, %espandl 是逻辑与指令,而-16其实补码形式是0xfffffff0。为什么要把%es

40、p的值和16进行逻辑与运算呢?不要小看这条指令,它的作用不容忽视,%esp指向堆栈顶部。这条指令其实是为了强制让%esp的值是16的倍数。为什么要16的倍数?这里必须懂得一个常识:Linux下的编译器GCC默认的堆栈是16字节对齐的,可能有些人要问为什么要对齐,对齐其实为了加快CPU的访问效率,这里你记住这点就可以了。movl $0, %eaxaddl $15, %eaxaddl $15, %eaxshrl $4, %eaxsall $4, %eax看到这几句,干嘛对%eax寄存器进行那么多的操作啊?的确,我也觉得没什么多大的必要,因为仔细看看这几条指令无非就是为了让%eax的值是0而已。看看

41、刚开始 %eax = 0,经过两次addl之后,%eax的值变成30了,30其实就是0x11110,再下面两条指令是为了保证%eax最低5位的值全部为0。注意,这只是在我的机子上的反汇编指令,不同机器对此处理可能不一样,但有一点一样就是保证%eax的值是0。看看下面这条指令:subl %eax, %esp看,%esp值减去%eax值后把结果送到%esp,所以经过这条指令后%esp值仍然是16的倍数,这就是保证%eax值是16的倍数的原因了。 call function movl $0, %eax这个简单了,调用函数function,最后又把%eax寄存器的值清0,结束整个main函数了。这就是

42、最简单的函数调用分析了,没有涉及到参数的传递,所以非常简单,下面就要开始讲到参数的传递了,事实上有了这个例子的分析,下面的简单多了。2.函数原型: int function(int i)现在有了参数了,也有了返回值了,相对来说更比较复杂了。这里就得引入%esp寄存器值的变化了,不然就难以把问题分析清楚了,如果想形象一点地描述那就画图,自己画个图根据我的数据变化一起分析吧。看看一段简单的C代码:/ C Codeint function(int i)return 2 * i;int main(void)int j = function(10);return 0;之所以些这么简单只是为了我们分析问题

43、的方便,懂得个原理就算是复杂的其实稍微再分析一下也就懂了。我们从main开始分析吧:main:pushl %ebpmovl %esp, %ebpsubl $24, %espandl $-16, %espmovl $0, %eaxaddl $15, %eaxaddl $15, %eaxshrl $4, %eaxsall $4, %eaxsubl %eax, %esp #到这里其实和前面的例子基本一样,就不分析了movl $10, (%esp)call functionmovl %eax, -4(%ebp)movl $0, %eaxleaveret看看上面的汇编代码,和前面一样的不分析。但是其中有

44、句不一样:subl $24, %esp ; 因为主函数里有两个临时变量i, j;这里为了有足够的空间留给临时变量所以干脆在堆栈里腾出24个字节空间。在看看下面的代码:movl $10, (%esp) #=> %esp = 800, (800) = 10 ,其中800是我们假设的地址值,(800)表示地址800的内容这里的(%esp)指的是%esp地址里的内容,刚才我们假设这时候%esp的值是800, 那么地址为800的内容就是10了。执行函数调用了,注意在调用函数前其实是先把函数调用指令 call之后的地址压栈,也就是call之后那条指令的IP值压栈,所以这时候 %esp = 796;这

45、里要弄明白为什么要把下条指令地址压栈,假设如果不把IP值压栈,那么当函数调用完毕后怎么能找到函数调用时的地址呢?也就是说如果没把IP压栈,那么函数调用完之后就回不到原来的执行地址了,就会造成程序执行顺序的错误!下面列出函数function的汇编代码:function:pushl %ebpmovl %esp, %ebpmovl 8(%ebp), %eaxaddl %eax, %eaxpopl %ebpretpushl %ebp; 经过这条指令后 %esp值减4,所以这时候%esp值是792。下面这句:movl %esp, %ebp #=> %ebp = 792, %esp = 792, (

46、792) = %ebp ;其中(792)表示地址792的内容movl 8(%ebp), %eax #=> %eax = 108(%ebp)指的是什么?8(%ebp)等于 : (%ebp + 8) ,这里注意,%ebp + 8 是表示一个地址值,加上括号表示存储在该地址上的内容。所以8(%ebp)其实就是地址为800的值,看前面地址800的值刚好是10!所以这句其实是把10复制给%eax寄存器.addl %eax, %eax #=> %eax = 20相当于2 * %eax, %eax这时候等于20了,刚好是实现了C代码中的 (2 * i);popl %ebp #=> 恢复%e

47、bp寄存器的值, %esp这时候等于796ret #=> 函数调用完毕返回,这句其实是把刚才压栈的IP值弹出栈,执行这条指令后 %esp = 800# 800!想想我们在调用函数的时候%esp也是800啊!这就是实现了“清栈”了,就是把调用函数所在的栈清除了!好了,函数 function的汇编代码分析完了,现在回头继续看看main函数里的下一条指令了。接下来是这句:movl %eax, -4(%ebp)%eax寄存器存放的是什么?看function函数的代码,可以知道其实就是(2 * i)的值,所以返回值其实是通过%eax来传递的!传递到-4(%ebp)里去了,-4(%ebp) = (%

48、ebp - 4); -4(%ebp)到底是什么呢?看看C代码,返回值传给变量j,那么-4(%ebp)会不会就是j呢?答案是肯定的!我们先看看%ebp的值是什么。看看 main函数的汇编代码,可以得出,%ebp其实指向了main函数的栈底部,但记不记得前面说的subl $24, %esp是为临时变量而留出的空间?没错,-4(%ebp) 就是存储在临时变量区域!也就是变量 j 了。3. 函数原型:int function(int i, int j);现在参数是两个,不是一个了,两个到底该怎么处理呢?同样看C程序和相应的汇编代码:/ C codeint function(int i, int j)r

49、eturn (i + j);int main(void)function(1, 2);下面列出主要的汇编代码,没有全部列出来,因为一些和前面一样的代码已经分析过,不再罗嗦了。main:# . .movl $2, 4(%esp)movl $1, (%esp)call function先把2送进栈里,再把1压栈,我们看看函数调用的C代码:function(1, 2); 2在右边,而1在左边,所以,当存在多参数的时候参数压栈其实是按从右向左的顺序压栈的。当参数都压栈后,就调用函数了。function:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eaxaddl 8

50、(%ebp), %eaxpopl %ebpret看函数的汇编代码:movl 12(%ebp), %eax ; 知道12是哪里来的吗?画图看看,结合前面的分析!一调用函数先压IP进栈,再压%ebp进栈,那么%esp值就被减去8了,再把%esp值复制到%ebp,就是这样而已!4.函数原型:char *function(char *s);作为字符串函数其实道理也差不多,而且我觉得反而更简单,怎么个简单法,看代码吧:/ C codechar *function(char *s)return s;int main(void)char *p = function("abcd");列出并

51、简单分析一下汇编代码:main:# . .movl $.LC0, (%esp)call function这里你可能会问 $.LC0 是什么。看看下面的定义:.LC0:.string "abcd".LC0只是一个标志,就是字符串"abcd";所以说白了其实非常简单。movl $.LC0, (%esp) ;就是把字符串送到地址为%esp的空间里。下面看调用的函数的汇编代码了:function:#. .movl 8(%ebp), %eax#. .8(%ebp)其实就是字符串了,依然建议你们自己画图看看,只要图一画,自然变得十分清晰。5.函数原型:struct

52、text function(int n);现在函数返回值类型换成是结构体了,差别就来了,不过其实道理还是那个样,本质的东西还是一样的。到结构体这里我就简单说几句而已。/C codestruct text int a;struct text function(int n) struct text s;s.a = n;return s;int main(void)struct text t = function(10);return 0;是为了分析不同函数调用和参数传递方式是怎样进行的。看汇编代码:main:# . .leal -20(%ebp), %eaxmovl $10, 4(%esp)mov

53、l %eax, (%esp)call function到这个例子就只分析最重要的指令了,其他的大家可以尝试分析看看。在main函数开头,我的机子上GCC居然留出了40个字节给临时结构体变量!开头有这句:subl $40, %esp ;不知道为什么留这么大的空间。但这些数据我们先不管了,毕竟不是最重要的。leal -20(%ebp), %eax看看上面这句,这条指令非常重要。把(%ebp - 20)的地址复制给%eax,%eax 的值其实是结构体中成员的地址。自己分析后面的代码证实一下。下面是function函数的汇编代码:function:pushl %ebpmovl %esp, %ebpsu

54、bl $16, %espmovl 8(%ebp), %eaxmovl 12(%ebp), %edxmovl %edx, -4(%ebp)movl -4(%ebp), %edxmovl %edx, (%eax)subl $16, %esp #为函数中临时结构体变量保留空间.在上面和条指令后面那几条指令都是按变量所在的地址赋给结构体变量成员了, 3.MOV指令实现数据传递MOV指令,能实现以下操作: CPU内部寄存器之间数据的任意传送(除了码段寄存器CS和指令指针IP以外)。 立即数传送至CPU内部的通用寄存器组(即AX、BX、CX、DX、BP、SP、SI、DI),给这些寄存器赋初值。 CPU内部

55、寄存器(除了CS和IP以外)与存储器(所有寻址方式)之间的数据传送,可以实现一个字节或一个字的传送。 能实现用立即数给存储单元赋初值。 立即数传送 MOV CL,4 ;CL4,字节传送 MOV DX,0FFH ;DX00FFH,字传送 MOV SI,200H ;SI0200H,字传送 MOV BVAR,0AH ;字节传送 ;假设BVAR是一个字节变量,定义如下:BVAR DB 0 MOV WVAR,0BH ;字传送 ;假设wvar是一个字变量,定义如下:wvar dw 0 寄存器传送 mov ah,al ;ahal,字节传送 mov bvar,ch ;bvarch ,字节传送 mov ax,bx ;axbx,字传送 mov ds,ax ;dsax,字传送 mov bx,al ;bxal,字节传送 存储器传送 mov al,bx ;alds:bx mov dx,bp ;dxss:bp+0 mov dx,bp

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论