(一) 研究概述
我们为什么必须使用变量?因为我们在编程时必须存储数据。那么如果可以使用别的方法存储数据,我们就可以不必因此目的而使用变量。
用什么方法来存储数据呢?在学习汇编语言是。我们把数据存储在寄存器和内存空间中。那么,在本次试验中,我们研究的是C语言中如何使用寄存器。
(二) 研究过程
(1) 编写一个程序url.c
编译链接完成,用debug加载。
-u查看如下:
这里的语句比较整齐有逻辑,像是某些功能的实现语句,但是往下-u,几次都看不到所写的语句。
这里,main函数的代码应该是在程序段中的,程序在执行的时候,一条语句要想语句被cpu执行,那么这条语句就必然要在被执行程序的程序段中。在debug中想要找到他的代码只能靠-u,-t等一段一段的查找。yb
(2) 为了找到我们编写的main函数内的语句,再继续编辑如下的程序。
让程序自己现实出main函数的偏移地址。
执行结果如下
可以看到,main的偏移地址是01fa。
但是这个偏移地址是不是正确呢?又是不是通用呢?
这里分析一下,程序在编译链接的过程中,多出来的部分到底是做什么的,是谁添加的。
添加者必然是编译连接程序,而其作用,可能与程序执行前后的现场保护,系统调度等有关系。那,多出来的这一部分,应该是固定的,与我们编写的程序无关的。也就是说,这个偏移地址对于所有的程序来说,都是相同的。那是不是这样呢?我们验证。
用这个偏移地址查看url.exe的代码段
这时,我们看到了我们写在C程序中的代码。
这也证实了我们前面的猜想,添加者必然是编译连接程序,多出来的这一部分,是固定的,与我们编写的程序无关的。同时也说明,main函数的代码,就是在程序段中。
偏移地址之所以能够显示出来,有两种可能:main是一个变量,他存储的就是main在代码段里的偏移变量。或者他就是一个标号。
假如main是一个变量的话,那么他是有自己的存储单元的。那我们通过修改上面的程序,来看看他是不是变量。
编译连接后显示如下
依然显示的是1fa。
假如main是一个变量,这里出现的应该是他的存储单元地址,而不是他的值。所以,main是一个标号。他指向的是我们所写的程序主函数编译链接完之后的第一条语句。
(3) C语言是否讲函数实现为汇编语言中的子程序
在上面的查看中,发现函数后有ret指令。我们设想,C语言有可能将函数实现为汇编语言中的子程序。
在这里我们看到,它确实是把void f(void)这个函数的实现写成了如下的形式:
PUSH BP
MOV BP,SP
MOV AX,BX
ADD AX,CX
POP BP
RET
且与mian函数相邻。
可见C语言确实将函数实现为汇编语言中的子程序。
(三) 附录研究
我们知道,在编写汇编程序的过程中,有些寄存器是不能直接改变数值的。比如,我们想改变cs的值,必须通过‘MOV AX,0001;MOV CS,AX’这种方式间接地改变。那在C语言使用寄存器的时候,应该怎么使用呢?
我们编写如下程序:
编译链接无报错,说明C语言中可以支持直接赋值给cs。
我们debug进入,发现其反编译如下:
我们看到他使用的是MOV AX,0001;MOV CS,AX的形式。也就是说,虽然C语言支持直接赋值,但是在底层,它依然翻译成了间接赋值。只不过中间过程由C语言编译链接时实现。
(四) 研究感悟:
变量不是一定要用的,也不是必须要使用的。存贮变量可以使用寄存器来进行。同时,C语言提供了直接操纵系统底层的语句。