对如图程序进行编译连接,再用debug加载。
我们在偏移地址1fa处查看main函数的内容:
执行到1fd处,发现n的偏移地址为01a6,段地址存储在ds寄存器里,为07c4.
再查看函数f2:
参数a、b的值是用栈来传递的,它们的段地址都存放在ss寄存器中:
局部变量c的值在这里是用si寄存器存储的,因为c正好是int型,那么子函数里定义的局部变量是用寄存器存储吗?我们在这里加一条赋值语句看看会如何:
可见,局部变量d是放在栈里的,而c是放在寄存器si里的,只是函数要将c返回,就将c的值赋给了ax。那么如果返回值不是int型怎么办?这个问题我们之前已经研究过:如果是1字节的数据,用al存放,如果是4字节的数据,高16位用dx传递,低16位用ax传递。
也就是说全局变量n的段地址在ds寄存器里,局部变量a、b、d的段地址在ss寄存器里,局部变量c的值存储在寄存器si里而不是内存里,没有段地址。所以全局变量n存储在程序开始的数据段里,而局部变量c存储在栈段里。参数a、b存储在栈段里。函数的返回值按值的大小存储在寄存器ax和dx中。全局变量的存储空间在程序开始就分配了,在整个程序执行完才释放,分配和释放的工作应该是由c0s.obj里的函数完成的。局部变量的存储空间在什么时候分配呢?我们将增加局部变量d的函数f2与之前的函数f2对比,发现多了一条语句“sub sp,2”,之后对d的赋值语句为“mov word ptr [bp-2],4”,这说明“sub sp,2”就是为局部变量d分配栈段空间的指令,局部变量是在子函数开始执行时分配的,那么是在函数入口处将局部变量全部分配,还是在函数中局部变量定义处分配呢?因为TC2.0所使用的c标准要求在函数开头将要使用的变量全部定义,所以在这里这两种方式是一样的。而函数结束时“mov sp,bp”指令将sp的值还原,也就是释放局部变量d的空间,所以局部变量的存储空间是在函数结束时释放的。从程序中可以看到,函数参数的存储空间是在主函数里对函数进行调用时就分配的,也就是将参数的值入栈,而在函数返回后,用pop cx将参数从栈段中释放。
主函数里调用f3函数使用的语句是“call 076a:0239”,也就是直接call函数的段地址+偏移地址,我们来看f3函数的内容:
发现f3返回时是用retf返回的,也就是将ip和cs都出栈。所以对于far型的函数,调用时要用call 段地址+偏移地址,返回时要用retf将段地址和偏移地址都出栈。
再来看程序2:
观察函数f的内容:
发现n的存储空间为si寄存器,a的存储空间为以ds:0194为地址的两个字节。它们的存储空间是什么时候分配的呢?我们知道局部变量n的存储空间是在函数开始时分配的,而a的存储空间是固定的内存空间,不是栈段,在函数结尾处n的空间被释放了而a的空间并没有被释放。在网上查阅资料得知,静态局部变量和全局变量分配存储空间的方式是相同的,而且具有相同的生命周期,只是静态局部变量只能在定义的函数中使用。
观察主函数,也没有释放静态局部变量的语句,可见静态局部变量的存储空间也是由c0s.obj里的函数进行分配和释放的。
我们观察程序的执行结果也可以发现:
不管执行多少次f函数,每次输出n的值都为1,因为它是局部变量,f函数结束后就要释放,而a是静态局部变量,相当于全局变量,它的值是可以不断累加的。
再来看程序3:
main函数的内容为:
这里的a、b、c、a1、a2都是全局变量,只是它们的类型不同而已。他们的存储空间是否相邻呢?看看偏移地址194处的数据段的内容:
可以看到数据段里存储了5个值为1的数,它们的存储空间是紧邻的。
整型的存储空间为2个字节,字符型为1个字节,长整型为4个字节。