zoukankan      html  css  js  c++  java
  • 王爽-汇编语言-综合研究四-不使用main函数编程

    (一) 研究目的

    使用C语言编程,我们一定要使用main函数么?

    (二) 研究过程

    1) 最初的程序

    首先,我们编写一个不写main函数的C语言程序。

    程序如下:

    clip_image001

    在编译的过程中,没有发现错误。在链接的过程中发现出现的错误如下:

    clip_image003

    链接时出现Undefined symbol ‘_main’ in module c0s

    这样的错误信息,可能main函数与c0s.obj这个文件有关系。

    这时我们想,C语言编译之后的文件后缀名是什么?是.obj。那汇编语言编译后的文件名是什么?也是.obj。这两个文件有关联么?

    理论上说,他们的内容应该是相似的。而且都应该可以被link.exe编译。

    我们尝试link:

    clip_image005

    我们发现没有错误。

    2) 带main函数的程序

    我们编写带main的函数的程序

    clip_image006

    我们编译链接。得到m.exe

    3) 找不同

    我们带main和不带main的C语言程序都编译连接成了.exe,那我们就来找找他们的不同。代码是相同的,不同的只是main的有无,那不同点就只集中在main上面。这样,我们就可以集中的针对main的功能进行研究。

    首先我们看一下文件详情。

    clip_image008

    我们看到,F.exe和M.exe大小还是差很多的。结合我们以前debug时看到的,在编译链接后程序第一条语句之前有很多未知的程序。我们分析,可能是M中有main函数,这使得在编译连接过程中,给程序多增加了很多语句。

    我们分别运行这两个程序。

    程序的运行结果如下:

    clip_image010

    我们看到两者的运行情景均如上所示,区别是M.exe运行后返回dos中,而F.exe运行后直接卡死。

    原因是什么呢?我们想到在研究二中查看函数实现的时候,在main函数中有ret指令。当时我们分析,这是因为C语言将main函数也实现为了子程序。是不是程序没有返回呢?

    我们继续分析不同。我们用debug加载。

    首先查看两者的-r和-g的情况:

    clip_image012

    clip_image014

    这里我们直观的看出,程序M的代码段长度为0EB8,程序F的代码段长度为001D。且程序M执行后可以正确的返回,而程序M执行后则不能返回。

    在查看其反汇编后的代码:

    F.exe如下:

    clip_image016

    M.exe如下:

    clip_image018

    中间部分不再赘述

    clip_image020

    我们可以看到,在函数内的实现有一处不同,在F.exe中,缺少三条指令,RET;PUSH BP;MOV BP,SP;(分号只是为了区分语句)。这是main函数返回,恢复寄存器BP的语句。

    还有一处不同,是01fa前所增加的部分,在F.exe中是没有的。

    那我们来分析一下增加的这部分代码。

    基于我们前面的认识,首先,main被作为了一个子程序;第二,编译时为main添加了很多代码。那是不是添加的代码调用了main并且实现了程序正确返回的功能呢?

    我们查看前面增加的代码,我们知道main的偏移地址是01FA,那我们就找有没有类似call 01FA的语句。我们最后在这里发现了:

    clip_image022

    这说明,我们的猜想是正确的,main函数前的这些程序,调用了main函数。而且,我们发现了如下的代码:

    clip_image024

    我们执行到这里:

    clip_image026

    这说明什么?main前添加的程序有这么两个功能,第一,调用main函数;第二,使得程序正确返回。当然,还有其他的功能,但是这两个功能是最基本的。

    4) C0S的作用

    我们刚才看到,没有main函数的程序在链接的时候会出现COS文件的错误,而C0S文件是一个.obj文件。那么,他是不是能够被链接成为一个.exe文件呢?如果可以,那我们就可以看到其汇编代码。

    clip_image028

    虽然有错误,但是生成了C0S.exe。我们debug加载查看。

    clip_image030

    我们看到,C0S中的代码与main前所加的代码基本相同。也就是说,我们可以认定,main前面的程序与C0S有关。

    5) 程序生成exe的过程

    援引书中的话:

    tc.exe将c0s.obj和用户.obj一同连接,生成.exe。照这个方法生成的exe程序运行过程如下:

    ①c0s.obj里的程序先运行,进行相关的初始化。如申请资源,设置ds,ss等相关寄存器。

    ②c0s.obj里的程序调用main函数,从此用户程序开始运行。

    ③用户程序运行结束从main程序返回到c0s.obj的程序中。

    ④c0s.obj的程序接着运行,进行相关的资源释放,环境恢复的工作。

    ⑤c0s.obj的程序调用DOS的int 21h例程的4ch号中断功能,程序返回。

    6) 自己编写C0S.obj

    基于以上的认识,我们在汇编中编写这样的程序:

    clip_image032

    编译,并将其复制到minic文件夹下,替换原来的C0S.obj。

    我们这时在编译我们原来写的F.C,发现链接成功。

    clip_image034

    我们debug加载反编译后,其代码如下:

    clip_image036

    我们看到,这里的call的偏移地址是0012,是我们F函数的第一条语句。运行后发现:

    clip_image038

    程序正常返回。这也就是说,我们编写的C0S.obj已经实现了调用返回的功能。

    7) 研究一个程序

    我们编写一个程序如下:

    clip_image040

    我们看到,这个程序与上次程序不同点在于,Buffer没有申请内存,而是直接赋值为零。我们猜测,所写入的位置为ds:[0]。我们验证:

    clip_image042

    我们看到,a-f这个个字母就是写到了DS:[0]处。

    (三) 附录研究

    我们在TC2.0的环境下发现了C0.asm这样一个文件。我们想,这与C0S.oobj会不会有关联。

    我们打开,发现其语句与我们C0S链接之后的语句如此一致:

    clip_image044

    我们尝试编译链接C0.asm,发现其出现了RULES.ASI,EMUVARS.ASI没有找到的情况。我们拷入,编译成功。发现其反汇编代码如下:

    clip_image046

    与C0S.obj链接之后的代码相同。这是我们就可以知道,c0.asm就是C0S的源代码。

    而我们用记事本打开其他两个文件:

    clip_image048

    这里面大部分定义的是一些常量。

    (四) 研究感悟

    main函数还是f函数?这不重要。重要的是程序连接的过程中对程序所添加的修改。为了保证程序可以正常的调用和返回,以及其他的一些功能实现,C语言添加了如此长的代码。透过main看到这个实质,会让我们的学习更加深入一层。

  • 相关阅读:
    LOJ 6089 小Y的背包计数问题 —— 前缀和优化DP
    洛谷 P1969 积木大赛 —— 水题
    洛谷 P1965 转圈游戏 —— 快速幂
    洛谷 P1970 花匠 —— DP
    洛谷 P1966 火柴排队 —— 思路
    51Nod 1450 闯关游戏 —— 期望DP
    洛谷 P2312 & bzoj 3751 解方程 —— 取模
    洛谷 P1351 联合权值 —— 树形DP
    NOIP2007 树网的核
    平面最近点对(加强版)
  • 原文地址:https://www.cnblogs.com/shandianlongxiao/p/4025466.html
Copyright © 2011-2022 走看看