子程序参数传递的基本原理 (子程序参数传递的方法)

子程序参数传递的基本原理与方法 子程序参数传递的基本原理

一、引言

在计算机编程中,子程序(subroutine)是一种执行特定任务的程序代码段。
参数传递是子程序的重要组成部分,它允许主程序与子程序之间进行数据交换。
本文将详细介绍子程序参数传递的基本原理和方法。

二、子程序参数传递的基本原理

子程序参数传递的基本原理是通过调用子程序时传递参数值,以便子程序能够执行特定的任务或计算。参数传递的过程包括以下几个关键步骤:

1. 定义参数:在编写子程序时,程序员需要定义参数(即形式参数),以指定子程序所需的数据类型和名称。这些参数将在调用子程序时接收实际的值。
2. 传递实际参数:当主程序调用子程序时,需要将实际参数(即实际的值)传递给子程序的对应参数。这些实际参数可以是变量、常量或表达式等。
3. 接收参数值:子程序接收到传递的实际参数值后,将其用于执行特定的任务或计算。在执行过程中,子程序可以修改参数值,但这些修改不会影响到主程序中对应的实际参数值。
4. 返回结果:子程序执行完毕后,可能需要通过返回值或输出参数将结果传递回主程序。

三、子程序参数传递的方法

根据编程语言的不同,子程序参数传递的方法也有所差异。下面介绍几种常见的参数传递方法:

1. 值的传递(Pass by Value)
值的传递是一种常见的参数传递方法。在这种方法中,当实际参数传递给子程序时,会将其值复制给形式参数。子程序在内部使用这些值进行计算或处理,但不会改变实际参数的值。这种方法的优点是简单直观,但在处理大型数据结构(如数组、结构体等)时可能效率较低。
2. 引用传递(Pass by Reference)
引用传递是一种更为高效的参数传递方法。在这种方法中,形式参数接收的是实际参数的内存地址。子程序可以直接操作内存中的数据,从而实现对实际参数的修改。这种方法的优点是可以在子程序中修改主程序的变量值,适用于需要改变数据状态的场景。引用传递需要谨慎处理,以避免意外的数据修改和错误引用。
3. 指针传递(Pointer Passing)
在某些编程语言中,指针传递是一种特殊的参数传递方法。指针传递涉及指针变量的使用,指针变量存储了实际参数的地址。子程序通过指针访问并修改实际参数的值。这种方法在效率和功能上与引用传递相似,但需要更复杂的语法和更多的注意事项。
4. 默认参数(Default Parameters)
默认参数是一种特殊的参数传递方式,允许在调用子程序时省略某些参数的值。这些默认值在定义子程序时已经指定,因此不需要在每次调用时都提供。这种方法简化了函数调用,提高了代码的可重用性。过度使用默认参数可能导致代码的可读性和可维护性下降。

四、结论

子程序参数传递是计算机编程中的关键概念之一。
本文介绍了子程序参数传递的基本原理和方法,包括值的传递、引用传递、指针传递和默认参数等。
在实际编程中,选择合适的参数传递方法需要根据具体需求和编程语言的特性来决定。
在参数传递过程中需要注意数据的安全性、效率和代码的可维护性。
通过深入理解子程序参数传递的原理和方法,程序员可以更好地设计高效、安全的程序代码。


汇编语言中的子程序调用参数的传送方式有哪些

子程序参数传递的方法一般有三种:1)寄存器传递法:适用于参数少时。 2)变量传递法:适用于同一源文件(程序模块)中。 3)堆栈传递法:只适用于传递入口参数。

请问mov EBP,ESP ESP怎么找呀

sub esp,1 在堆栈中留出局部变量的空间看看下面的文章对你的汇编语言一定有帮助!汇编中参数的传递和堆栈修正在 Win32汇编中,我们经常要和 Api 打交道,另外也会常常使用自己编制的类似于 Api 的带参数的子程序,本文要讲述的是在子程序调用的过程中进行参数传递的概念和分析。 一般在程序中,参数的传递是通过堆栈进行的,也就是说,调用者把要传递给子程序(或者被调用者)的参数压入堆栈,子程序在堆栈取出相应的值再使用,比如说,如果你要调用 SubRouting(Var1,Var2,Var3),编译后的最终代码可能是push Var3push Var2push Var1call SubRoutingadd esp,12也就是说,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态。 参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生不正确的结果,这就是我在前面使用“可能”这两个字的原因:各种语言中调用子程序的约定是不同的,它们的不同点见下表:C SysCall StdCall Basic Fortran Pascal参数从左到右 是 是 是参数从右到左 是 是 是调用者清除堆栈 是允许使用:VARARG 是 是 是VARARG 表示参数的个数可以是不确定的,有一个例子就是 C 中的 printf 语句,在上表中,StdCall 的定义有个要说明的地方,就是如果 StdCall 使用 :VARARG 时,是由调用者清除堆栈的,而在没有:VARARG时是由被调用者清除堆栈的。 在 Win32 汇编中,约定使用 StdCall 方式,所以我们要在程序开始的时候使用 stdcall 语句。 也就是说,在 API 或子程序中,最右边的参数先入堆栈,然后子程序在返回的时候负责校正堆栈,举例说明,如果我们要调用 MessageBox 这个 API,因为它的定义是 MessageBox(hWnd,lpText,lpCaption,UType) 所以在程序中要这样使用:push MB_OKpush offset szCaptionpush offset szTextpush hWndcall MessageBox...我们不必在 API 返回的时候加上一句 add sp,4*4 来修正堆栈,因为这已经由 MessageBox 这个子程序做了。 在 Windows API 中,唯一一个特殊的 API 是 wsprintf,这个 API 是 C 约定的,它的定义是 wsprintf(lpOut,lpFormat,Var1,Var2...),所以在使用时就要:push 1111push 2222push 3333push offset szFormatpush offset szOutcall wsprintfadd esp,4*5下面要讲的是子程序如何存取参数,因为缺省对堆栈操作的寄存器有 ESP 和 EBP,而 ESP是堆栈指针,无法暂借使用,所以一般使用 EBP 来存取堆栈,假定在一个调用中有两个参数,而且在 push 第一个参数前的堆栈指针 ESP 为 X,那么压入两个参数后的 ESP 为 X-8,程序开始执行 call 指令,call 指令把返回地址压入堆栈,这时候 ESP 为 X-C,这时已经在子程序中了,我们可以开始使用 EBP 来存取参数了,但为了在返回时恢复 EBP 的值,我们还是再需要一句 push ebp 来先保存 EBP 的值,这时 ESP 为 X-10,再执行一句 mov ebp,esp,根据右图可以看出,实际上这时候 [ebp + 8] 就是参数1,[ebp + c]就是参数2。 另外,局部变量也是定义在堆栈中的,它们的位置一般放在 push ebp 保存的 EBP 数值的后面,局部变量1、2对应的地址分别是 [ebp-4]、[ebp-8],下面是一个典型的子程序,可以完成第一个参数减去第二个参数,它的定义是:MyProc proto Var1,Var2 ;有两个参数local lVar1,lVar2 ;有两个局部变量注意,这里的两个 local 变量实际上没有被用到,只是为了展示用,具体实现的代码是:MyProc procpush ebpmov ebp,espsub esp,8mov eax,dword ptr [ebp + 8]sub eax,dword ptr [ebp + c]add esp,8pop ebpret 8MyProc endp现在对这个子程序分析一下,push ebp/mov ebp,esp 是例行的保存和设置 EBP 的代码,sub esp,8 在堆栈中留出两个局部变量的空间,mov /add 语句完成相加,add esp,8 修正两个局部变量使用的堆栈,ret 8 修正两个参数使用的堆栈,相当于 ret / add esp,8 两句代码的效果。 可以看出,这是一个标准的 Stdcall 约定的子程序,使用时最后一个参数先入堆栈,返回时由子程序进行堆栈修正。 当然,这个子程序为了展示执行过程,使用了手工保存 ebp 并设置局部变量的方法,实际上,386 处理器有两条专用的指令是完成这个功能用的,那就是 Enter 和 Leave,Enter 语句的作用就是 push ebp/mov ebp,esp/sub esp,xxx,这个 xxx 就是 Enter 的,Leave 则完成 add esp,xxx/pop ebp 的功能,所以上面的程序可以改成:MyPorc procenter 8,0mov eax,dword ptr [ebp + 8]sub eax,dword ptr [ebp + c]leaveret 8MyProc endp好了,说到这儿,参数传递的原理也应该将清楚了,还要最后说的是,在使用 Masm32 编 Win32 汇编程序的时候,我们并不需要记住 [ebp + xx] 等麻烦的地址,或自己计算局部变量需要预留的堆栈空间,还有在 ret 时计算要加上的数值,Masm32 的宏指令都已经把这些做好了,如在 Masm32 中,上面的程序只要写成为:MyProc proc Var1,Var2local lVar1,lVar2mov eax,Var1sub eax,Var2retMyProc endp编译器会自动的在 mov eax,Var1 前面插上一句 Enter 语句,它的参数会根据 local 定义的局部变量的多少自动指定,在 ret 前会自动加上一句 Leave,同样,编译器会根据参数的多少把 ret 替换成 ret xxx,把 mov eax,Var1 换成 mov eax,dword ptr [ebp + 8] 等等。 最后是使用 Masm32 的 invoke 宏指令,在前面可以看到,调用带参数的子程序时,我们需要用 push 把参数压入堆栈,如果不小心把参数个数搞错了,就会使堆栈不平衡,从而使程序从堆栈中取出错误的返回地址引起不可预料的后果,所以有必要有一条语句来完成自动检验的任务,invoke 就是这样的语句,实际上,它是自动 push 所有参数,检测参数个数、类型是否正确,并使用 call 来调用的一个宏指令,对于上面的 push/push/call MyProc 的指令,可以用一条指令完成就是:invoke MyProc,Var1,Var2当然,当程序编译好以后你去看机器码会发现它被正确地换成了同样的 push/push/call 指令。 但是,在使用 invoke 之前,为了让它进行正确的参数检验,你需要对函数进行申明,就象在 C 中一样,申明的语句是:MyProc proto :DWORD,:DWORD语句中 proto 是关键字,表示申明,:DWORD 表示参数的类型是 double word 类型的,有几个就表示有几个参数,在 Win32 中参数都是 double word 型的,申明语句要写在 invoke 之前,所以我们一般把它包括在 include 文件中,好了,综合一下,在 Masm32 中使用一个带参数的子程序或者 Api ,我们只需用 proto :dword, dd ?y dd ?dwResult dd ? x,1mov y,2invoke MyProc x,ymov dwResult,eax

堆栈区和SP

LS说的是x86汇编的看lz问得明显是 51单片机的SP默认是07H但是程序总要用ram的..07很多时候都是要被用掉得所以,一般来说 个开始运行的时候.就会确定好了 堆栈的起始地址比如你说的30H那么开始的时候会把SP = 30H当然如果你原本吧用的数据都定义在 很后面,直接不修改sp也是没有关系的///////////////////////////补充.先说一下堆栈的作用.比如你用call指令的时候回吧 你的返回地址压入堆栈.比如这个时候sp = 07,返回地址=1122H那么call后 07H = 11H08H = 22HSP =09H.因为压入2个直接所以变成09//sp = sp + 2你用ret或者ret指令后,,sp = sp - 2还有pop push指令.也是相关的作用,没有堆栈或者堆栈设置错误.你调用指令根本就不能用,程序肯定会跳飞.....这就是堆栈的作用至于SP为什么默认等于7 ..因为前面8个字节都是 特殊功能寄存器,,别人做cpu只好sp默认等于7了..其实sp默认等于多少都没有关系,,因为这东西.初始化的时候..必定要修改的.至于你说的堆栈区是30H-7FH...根本就没有这种说法,,只要你的单片机有ram 要你设置 80h 90h ..也没有关系.只是51单片机只有128个字节,所以最大只能设置成7Fh至于为什么要30H开始,,因为20H开始时位寻址区.一般给某些特殊数据用,但是你要用也没有关系,,0-20H这些地址.是4主.特殊功能寄存器,,自然也最好保留,,但是你要高兴 从07H开始...也是可以用的,,////////////////////////////////////////讲了那么多..楼主才给5分.太不够意思了..o(∩_∩)o

本文原创来源:电气TV网,欢迎收藏本网址,收藏不迷路哦!

相关阅读

添加新评论