main函数是必须的吗

研究实验4

研究过程:

问题引出:C语言编程非得用主函数main吗,不用是否可以?

对此问题进行研究,用tc.exe书写代码如下:

图1  没有main函数的c程序

对其进行编译,链接发现,编译阶段可以完成,但是链接阶段无法完成。即无法通过这种方式生成.exe文件。并显示错误信息:

图2  f()程序提示错误信息

错误信息提示没有定义在c0s中没有定义main。

用link.exe对其生成f.exe文件,查看其汇编代码如下:

图3  f.c对应汇编代码

从图中可以看出来,f()的偏移地址是0,这个与main函数不同,使用g命令

G 1d发现无法返回,且DOS卡死!f.exe的代码共1Dh(29)个字节。

图4  m.c汇编代码

可以发现,m.exe程序比f.exe程序多一条ret语句,即m.exe有30个字节,且程序能够正常返回,且程序执行完076a:0217处的指令就返回。

由此可见,main函数与f.exe首先在偏移地址上就不同f.exe偏移地址是0000h,main函数偏移地址是01fah,其次main函数比f.exe多一条ret指令,使得程序能够正常返回

问,添加了这样一条语句,为什么无法执行,且还会导致dos环境崩溃???!!!!!

问题2:main函数结尾的两个ret分别具有什么含义?

图5  探究ret追踪调用main 函数的地址

易知,call指令占用三个字节,当单步执行ret的时候,我们发现,ip变成了011d,基于之前学习对于cs:ip的理解,我们断定,“call main”的偏移地址为011a,结果如下图:

图6  调用main函数指令地址

由图可以发现,偏移地址确实是地址确实是011a。

下面对c0s进行研究:

将c0s.obj生成.exe文件,并用debug加载,查看汇编代码:如下

图7-8  c0s.exe开头对应汇编代码

图9-10 m.exe开头对应汇编代码

通过观察发现,开头处的汇编语句,出了第一句对DX的赋值不同以外,其余都是相同的。

图11 m.exe调用main处汇编代码

图12  c0s.exe相同偏移处代码

从图7-图12我们可以发现,c0s.exe和我们任意的一个m.exe程序很多内容都是相同的。通过图中第一处红线我们还可以发现,main是c0s.exe的一个实参,即调用main函数的是c0s.exe这个程序!!还可以发现,其余存在很多相同的内容,应该是资源配置的初始化,总之,在这里,我们得到的最重要一条结论就是:main函数被c0s.0bj文件调用!!!随着以后编译环境的改变,或者C语言学习的深入,我们仍然要注意这个本质性问题,虽然对于我们学习语言本身没多大帮助,但对于理解其工作机制却很有帮助!

通过上述,我们知道,main函数只不过是一个入口参数而已,并不具有特殊性。因此,我们想办法看能不能不用main函数编程,首先我们重写一个c0s.asm程序,对其编译,替换原c0s.obj。重新编译连接f.c,发现可以成功对f.c编译连接!!我们直接运行f.exe ,发现程序可以正常运行并返回!如下图

图13 得以正常运行的f.exe

我们通过debug加载f.exe,u命令查看开始处代码:如下

图14-15  debug加载f.exe

通过代码,我们发现,程序将我们自己写的c0s代码也加载进去了,再次印证了以下观点:1.c0s文件和用户obj文件一同连接,生成相应的exe文件;2.所谓的main,f不过是一个入口参数而已,本身并无特地位!

下面重新研究向一块安全的内存中写入”a”到”h”,汇编代码如下:

图16-19 安全空间写入”a”到”h”

我们可以发现一个很奇怪的现象,程序中经常刷新es寄存器的值!!!

图20-21  内存结果显示

可以看出ds:0开始处存放额是a-h,在这里我们需要注意的是,es只管下一条语句的段地址!之后默认的是ds!务必明确这一点!

未解决的问题:

Main函数的结尾处为什么有两个ret?

对于向安全的空间写数据,为什么会经常刷新es寄存器的值?

总结感悟:

对于这次学习我认为学习到最重要的一点是对MIAN函数的认识更加深刻了,即MAIN函数本身并不具有一定的特殊性!仅此而已。期间对于追踪调用MAIN函数指令地址进行了思考,并最终通过实践得到了验证。此次研究我认为对于编程技巧没有太大的帮助,但是对于理解本质却很有帮助,另外,对于最后一个向安全空间写数,我认为还任由许多问题应该探讨!

时间: 2024-10-30 05:47:00

main函数是必须的吗的相关文章

main 函数argc , argv 主命令行参数

ARGc和ARGv中的ARG指的是"参数"(ARGuments, argument counter 和 argument vector ) 至少有两个参数至主函数:ARGc和ARGv: 首先是一个至算提供的参数到程序, 第二个是对字符串数组的指针. 基本作用: argc, argv 用命令行编译程序时有用. 主函数main中变量(int argc, char *argv[ ])的含义 有些编译器允许将main()的返回类型声明为void,这已不再是合法的C++; main(int ar

Eclipse4.7使用基础 快捷键 生成main函数 mian+ alt+/

os :windows7 x64    jdk:jdk-8u131-windows-x64    ide:Eclipse Oxygen Release (4.7.0) 先输入 main,然后 按 alt+/ 敲击回车 感想: 刚开始的时候,使用notepad++进行java程序编写,老是手动输入 main函数.现在好了,有了先进的IDE,效率提高了许多.但是话说回来,零基础的新手的话,敲几天main函数有好处! Java优秀,值得学习.Eclipse是一款免费.强大的IDE,值得学习使用.

如何理解Python的Main函数?

作者:蟒蛇帝国(ID:Pythondg) 难度:初级 演示环境:OS:ubuntu 16.04Python:3.6 编写 Python 代码的时候我们经常看到下面这条语句.貌似是 Python 的 Main 函数.那它具体是什么意思呢. if __name__ == '__main__':     print('hello world') 首先 Python 里有两个概念, 源码文件: ~/code_house/py_dev$ tree . ├── file1.py ├── file2.py └

【转】IntelliJ 创建main函数快捷

http://blog.csdn.net/tiantiandjava/article/details/42269173 今天偶然发现了IntelliJ中 创建main函数的快捷键,依次还有for循环,System.out.println(); 在编写代码的时候直接输入psv就会看到一个psvm的提示,此时点击tab键一个main方法就写好了. psvm 也就是public static void main的首字母. 依次还有在方法体内键入for会有一个fori的提示,选中然后tab键,就会自动创

IntelliJ中的main函数和System.out.println()快捷键

1.在IntelJ中和Eclipse中稍有不同,在Eclipse中,输入main再按Alt+/即可自动补全main函数,但是在IntellJ中则是输入psvm,选中即可 2.在方法体内部有for循环,在IntellJ中是输入fori,然后会有一个提示,选中需要的for循环即可 3.System.out.println();在IntellJ中是输入sout

C语言学习011:带参数的main函数

直接上代码 1 #include <stdio.h> 2 3 int main(int argc,char *argv[]){ 4 printf("%i \n",argc); 5 int i; 6 for(i = 0; i < argc; i++){ 7 printf("%s\n",argv[i]); 8 } 9 10 return 0; 11 } 在上面的main方法中,第一个参数argc表示数组中的元素个数,第二个参数*argv表示参数数组,即

在C++工程中main函数之前跑代码的廉价方法(使用全局变量和全局函数)

[cpp] view plain copy // test.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <windows.h> #include <crtdbg.h> /// 在C++工程中main函数之前跑代码的廉价方法 /// 利用全局变量可以赋可变初值的事实 /// mainCRTStartup() => _cin

main函数参数

方法1. C/C++语言中的main函数,经常带有参数argc,argv,如下: int main(int argc, char** argv) int main(int argc, char* argv[]) 这两个参数的作用是什么呢?argc 是指命令行输入参数的个数,argv存储了所有的命令行参数.假如你的程序是hello.exe,如果在命令行运行该程序,(首先应该在命令行下用 cd 命令进入到 hello.exe 文件所在目录) 运行命令为: hello.exe Shiqi Yu 那么,

菜鸟nginx源码剖析 框架篇(一) 从main函数看nginx启动流程(转)

俗话说的好,牵牛要牵牛鼻子 驾车顶牛,处理复杂的东西,只要抓住重点,才能理清脉络,不至于深陷其中,不能自拔.对复杂的nginx而言,main函数就是“牛之鼻”,只要能理清main函数,就一定能理解其中的奥秘,下面我们就一起来研究一下nginx的main函数. 1.nginx的main函数解读 nginx启动显然是由main函数驱动的,main函数在在core/nginx.c文件中,其源代码解析如下,涉及到的数据结构在本节仅指出其作用,将在第二节中详细解释. nginx main函数的流程图如下:

带参数的main函数的使用

以前接触的main函数都是不带参数的.因此main 后的括号都是空括号.  main() {   ...  } 实际上,main函数可以带参数,这个参数可以认为是main函数的形式参数. C语言规定main函数的参数只能有两个,习惯上这两个参数写为argc和argv.因此,main函数的函数头可写为: main(argc,argv) C语言还规定argc(第一个形参)必须是整型变量,argv(第二个形参)必须是指向字符串的指针数组.加上形参说明后,main函数的函数头应写为: main (int