您的位置:首页 > 其它

参数传递与运行栈

2013-06-25 00:00 253 查看

隐式赋值语句——参数传递

参数,也叫做参变量,是一种特殊的变量。为了说明参变量和普通变量之间的异同点,下面我们给出一段具体的代码例子。

这里需要说明的是,本书中给出的相当一部分示例代码,都是用极为简化的伪代码写的,并不能够真正运行。这样做的目的是,最大限度保证代码的简化性、易读性、通用性。

下面,就先让我们看一段包含了参变量和变量定义的伪代码。 

f(x) {
a = 2 * x
if a  10
return 2
else
return 1
}

上面的伪代码结合了Javascript和Python这两种语言的最简化表达方法。由于省略了过程声明、类型声明,写起来极为简单,一目了然。

其中,if和return这两个关键字也是编程语言中常见的,不需要过多的解释;f(x)定义了一个名字叫做f的只有一个参数x的过程;{}这对大括号则包括了过程体内部的代码,这也是C、C++、Java等主流编程语言的语法惯例。

另外需要特别说明的一点是,在命令式语言中,过程(Procedure)通常还有几种其他的叫法,比如,函数(Function),方法(Method)等。后面会讲到函数式编程语言(Functional Programming),为了避免混淆,本书在讲解命令式语言时,将尽量避免使用函数(Function)这个词,而是尽量使用过程(Procedure)和方法(Method)这两个词。

上述代码中,x是一个参变量(参数),a则是过程体内部定义的普通变量(另,在过程体内声明的变量也叫做局部变量)。在函数体内,我们看不到类似于“x = …”的赋值语句。那么,x是什么时候被赋值的呢?答案是,当f(x)这个函数被调用的时候,参数才会被赋值。

运行栈

比如,我们有另一条语句,n = f(4)

这条语句在执行的时候,就会把4这个值赋给x,然后,进入f(x)的过程体代码,执行整个过程。

那么,这一切在内存中是怎么发生的呢?

我们又要回到前面讲过的运行栈的概念了。每个进程运行起来之后,都有自己的运行栈。

前面提到了数组这个数据结构的简单概念和用法。在我们的想象中,数组是一串横着摆放的内存单元格。现在,我们把这一串横着摆放的单元格竖起来,让它竖着摆放,这样,看起来,就是一个栈结构了。实际上,运行栈的基本实现结构,就是数组结构。

这个地方,要说明一下。运行栈也是内存中的一部分。运行栈中的地址,也是一种内存地址。

运行栈(stack)和内存堆(heap)是两种常见的内存分配方式。运行栈的结构很简单,就是一个数组结构加一个存放栈顶地址的内存单元。内存堆(heap)是一种比较复杂的树状数据结构,可以有效地搜寻、增、删、改内存块。一般来说,我们不必关注其具体实现。

分配在运行栈(stack)上的数据,其生命周期由过程调用来决定。分配在内存堆(heap)的数据,其生命周期超出了过程调用的范围。内存堆中的数据,需要程序员写代码显式释放,或者由系统自动回收。

我们回到例子代码。当计算机执行f(4)这个过程调用的时候,实际上是先把4这个数值放到了运行栈里面,然后,转向f(x)过程体定义的工作流程,执行其中的代码。

进入f(x)过程体后,遇到的第一条代码就是关于变量a的赋值语句“a = 2 * x”。计算机首先在运行栈为a这个变量预留一个位置。这个位置是运行栈内的一个内存单元。

我们可以想象一下,在一条竖着摆放的内存单元格中,最上面一个内存单元格上贴上了“a”这个标签。紧挨着“a”单元格下面的那个单元格上就贴着“x”这个标签。“x”单元格内的内容就是4这个数值。 

接下来,计算机执行2 * x 这个表达式。得出结果后,把结果存入到运行栈中之前为a预留的内存单元中。

从上面的描述中,我们可以看出,无论是a这个普通局部变量,还是x这个参变量,具体位置都是在运行栈中分配的。所不同之处在于,a这个普通局部变量的赋值是进入f(x)过程之后发生的,而x这个参变量的赋值是在f(x)过程调用之前就发生了。

所有的过程调用都会产生一个参数值压入运行栈的动作,即,把对应的参数值压入到运行栈上预先为参数分配的位置中。因此,有时候,我们也把过程调用前的参数传递叫做参数压栈。

上面举的例子是参数压栈的最简单的例子,只有一个参数。很多情况下,过程不止需要一个参数,有可能有多个参数。这就涉及到一个参数压栈顺序的问题,是从左向右压,还是从右向左压?这是一个问题。不同的语言实现有不同的做法。不过,在我个人看来,这不是什么重要的知识点,只是一种资料性的知识,不需要费心去理解。用到的时候再查资料就行了。

在不本文结束之前,我们在来问最后一个问题:所有的命令式语言,都是基于栈结构的吗?

不知读者有没有思考过这个问题。在我们学习汇编语言的时候,在我们学习高级命令式语言的时候,都涉及到了进程的运行栈。似乎,运行栈就是天然存在的,不需要多想。

没错,进程的运行栈就是天然存在的,汇编语言需要运行栈,操作系统也需要运行栈。我们可以说,绝大部分命令式语言都是基于栈结构的,但不是全部。比如,Python语言有一个实现,叫做Stackless Python(无栈的Python),这就是一种允许程序员脱离运行栈结构的语言实现。当然,这只是一个特例,无关大局。在绝大多数情况下,当我们考虑命令式语言的时候,脑海里应该自然而然就浮现出一个运行栈的结构。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: