汇编网首页登录博客注册
mywiil的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
学习动态
好友圈
友情链接

[2009-03-23 16:57] 推荐博文 综合研究试验4--不用main函数编程

在看到这个研究试验之前,从来没与想过“C语言程序为什么必须写main函数”、“没有main函数能性不能行”这些类似的问题。在前面的学习过程中,有一点点体会,就是C语言程序最终生成的可执行文件的运行入口并不是main函数,main函数应该和其他函数一样,是被别的程序代码调用的而已。

现在,跟着书中的思路,学习一下,看看究竟。

首先,编写一个f函数,并在tc下进行编译、连接,并回答问题。
(1)编译和连接那个环节会出问题。
答:编译通过,连接出错。

(2)显示的错误信息是什么?
答:Linker Error:Undefined symbol '_main' in module C0S.(提示某个变量名称没有定义)
(3)这个错误信息可能与那个文件相关。
答:根据提示信息,应该和c0s.obj这个目标文件有关系吧。

接下来,用masm中的link.exe对上一实践中生成的f.obj进行连接。可以直接把f.obj文件拷贝到masm下,或者把link.exe拷贝到我们的minic目录下,然后在命令行下进行连接,生成可执行文件f.exe(这说明,汇编和c编译后的obj文件是通用的)。我们试着运行一下f.exe,然后回答问题。
(1)f.exe的程序代码总共多少字节?
答:debug加载后,CX的值为1DH,即f.exe的代码共有1DH字节。
(2)f.exe的程序能正确返回么?
答:程序无法正确返回。
(3)f函数的偏移地址是多少?
答:0000H。

然后,我们将f函数改成main,然后用tc进行编译连接,生成m.exe,根据debug下的观察回答问题。
(1)m.exe的程序代码总共有多少字节。
答:加载后,CX的值为0EBBH,即m.exe的代码总共有0EBBH个字节。
(2)m.exe能正确返回么?
答:能。
(3)m.exe程序中的main函数和f.exe中的f函数的汇编代码有何不同?
答:完全相同。

根据第(4)题的要求,我们去找调用main函数的指令的偏移地址。我们知道,main函数的偏移地址为01faH,所以,我们就类似 call 01fa 的指令,debug下u命令一次跟踪。发现
1547:011A E8DD00        CALL    01FA
所以,调用main函数的指令的偏移地址为 011AH。
那么,整个程序的返回指令在哪里呢?debug下,返回指令是mov ax,4c00 int 21 ,我们接着上面的U命令继续跟踪。(找了两遍,眼睛都看僵了,没有找到,呜呜~~)看看书,提示用g和p命令,由于返回肯定位于call 01fa这条指令之后,所以,先用g命令到指令call 01fa前面的指令,然后p指令向下,哈哈,终于正常退出了,不过,指令的形式跟我们的有点差别,如下
AX=0000  BX=0691  CX=0000  DX=0740  SP=FFE2  BP=FFE2  SI=002F  DI=067D
DS=159E  ES=B800  SS=159E  CS=1547  IP=0151   NV UP EI PL ZR NA PE NC
1547:0151 B44C          MOV     AH,4C
-p

AX=4C00  BX=0691  CX=0000  DX=0740  SP=FFE2  BP=FFE2  SI=002F  DI=067D
DS=159E  ES=B800  SS=159E  CS=1547  IP=0153   NV UP EI PL ZR NA PE NC
1547:0153 8A4602        MOV     AL,[BP+02]                         SS:FFE4=00
-p

AX=4C00  BX=0691  CX=0000  DX=0740  SP=FFE2  BP=FFE2  SI=002F  DI=067D
DS=159E  ES=B800  SS=159E  CS=1547  IP=0156   NV UP EI PL ZR NA PE NC
1547:0156 CD21          INT     21
-p

Program terminated normally
不过,仔细看看恰面两条指令的结果,等价于mov ax,4c00 ,所以,这里就是退出的汇编指令了。

这个动手的搞定了,开始思考了。思考的提问层层递进。我们随着拷问,先给出自己的答案,然后,提问的最后,给出了我们回答,看看自己想对了多少。呵呵。

总之,我们这下彻底明白了main函数的来历啦。更详细的解释和说明书中已经写的很明白了,就不再这里赘述了。

通过上面的这些经历,我们应该明白,C中不用main函数我们一样能编程。前提是,我们应该修改书中提到的那个实现机制的一些部分,我们只要把初始化程序调用main函数的地方修改成自己的函数名称,那么初始话程序就能跟据我们提供的主函数名称来进行程序的连接了。为了更明确一点,我们自己模拟一下这个机制。

先按照书上的代码,制作一个c0s.obj,并覆盖minic下的同名文件。然后,在tc下重新编译连接f.c。发现,这一次,连接通过,并且,f.exe能够正确运行并返回。debug后我们发现,c0s.obj和我们f.obj两个文件连到了一起,我们的程序能正确的运行,这个机制和流程就应该和C语言的连接操作是一样的,只不过,它的初始化文件要比我们做了更多的前期和后期的操作吧。

最后,我们将一个向安全内存空间中写入8个字符的程序,使用现在的c0s.obj记性连接并在debug下跟踪,进行分析。
首先,先分析宏指令#define Buffer ((char *)*(int far *)0x02000000) ,程序中的符号Buffer将会被全部物理替换为((char *)*(int far *)0x02000000),这个长串是什么意思呢?就是:地址为0x02000000处的内存中的内容,并且这个内容的类型是一个char型的指针。接下来,我们分析程序。
f(){

        Buffer = 0;
;将地址为0x02000000的内存中的内容修改为0.

        Buffer[10] = 0;
;取出地址为0x02000000的内存中的数据,这个数据包含了要操作的内存类型char和数据的值0(上一步赋值的),然后与后面的[10]一起进行寻址定位,注意,此时的段地址不是0x2000000的段地址,因为Buffer只是取得的内存中的数据类型和值,这里只是一个偏移地址。从反汇编的代码我们也可以看出,赋值的时候,地址定位使用的是ds,因为我们是用的自己的c0s.obj,ds指向了开始的数据段,所以,这个0赋值到了ds:[0+10]即ds:[10]的地址处,由于是char型的地址,所以,占用的是一个字节的内存空间。这里明确了,下面的就好推理了。

        while(Buffer[10] != 8){
;如果ds:[10]内存处的值不为8(控制8次循环)

          Buffer[Buffer[10]] = 'a'+Buffer[10];
;将ds:[ds:[10]]的内存中的值赋值成'a'+ds:[10]的值。(有些描述方式不和汇编指令规范,但是表达起来应该比较明确。'a'=61H)

          Buffer[10]++;
;相当于inc ds:[10],将ds:[10]内存中的值自增1。(有些描述方式不和汇编指令规范)
        }
}

上面的代码分析完了,我们应该有个比较明晰的逻辑了,那8个字符到底写到了哪里呢?根据我们的分析,应该是写到了程序加载后的ds:0~ds:7这8个内存字节单元了。而且,ds:[10]内存的值,最后应该是8,这样才能退出while循环.那么我们就在debug下跟踪一下,当程序运行返回之前,我们查看一下内存空间
-d ds:0
194C:0000  61 62 63 64 65 66 67 68-00 00 08 00 00 00 00 00   abcdefgh........

基本上的理解都符合了。不过,这张的内容还是感觉没有吃透,只是跟着书顺下来了。尤其时候面的程序的理解,总感觉不透彻底,不够有力度。所以,还要在看看。(不过从这里的理解,发现前面的那一章的最后程序的分析存在不少问题,赶紧在修改一下去。)

=========================
下一个研究试验明天在学习吧。得把前面的这些都看顺了。

准备中...
评论次数(2)  |  浏览次数(1512)  |  类型(汇编作业) |  收藏此文  | 

[  younggay   发表于  2009-03-25 14:44  ]

博主学习的挺认真啊。
加油!

[  游客   发表于  2009-04-20 16:22  ]

我记得有本书解释过为什么都用main()函数?如何不用main()函数?现在想买这本书又不记得书名了,上次在书店找了很久都找不到.博主你知道请告诉我谢谢!

 
 请输入验证码  (提示:点击验证码输入框,以获取验证码