没有main函数,,
我们常用gcc main.c -o main
命令编译一个程序,
其实也可以分三步做,第一步生成汇编代码,第二步生成目标文件,第三步生成可执行文件:
$ gcc -S main.c (注意S是大写的)
$ gcc -c main.s
$ gcc main.o
在main.c中这么写到:
#include<stdio.h>
int m(){
printf("this is a test!");
return 0;
}
很明显,这个程序不能运行,因为没有main函数,试一下
gcc main.c -o main
报错:
[ming@localhost codetest]$ gcc main.c -o main
/usr/lib/gcc/i686-redhat-linux/4.5.1/../../../crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld 返回 1
整个程序的入口点是
crt1.o
中提供的_start
,它首先做一些初始化工作(以下称为启动例程,Startup Routine),然后调用C代码中提供的main
函数。所以,以前我们说main
函数是程序的入口点其实不准确,_start
才是真正的入口点,而main
函数是被_start
调用的。在_start中指名了要掉main,而程序中只有m函数,所以就运行不起来。
C程序的链接过程:
![linux下gcc <wbr>In <wbr>function <wbr>`_start' linux下gcc <wbr>In <wbr>function <wbr>`_start'](http://learn.akae.cn/media/images/asmc.link.png)
可以看到,,在生成main的可执行程序的时候,,其实还加入了其他的一些东西一起链接在一起执行的
main
函数最标准的原型应该是int main(int argc, char *argv[])
,也就是说启动例程会传两个参数给main
函数,这两个参数的含义我们学了指针以后再解释。我们到目前为止都把main
函数的原型写成int main(void)
,这也是C标准允许的,如果你认真分析了上一节的习题,你就应该知道,多传了参数而不用是没有问题的,少传了参数却用了则会出问题。
由于main
函数是被启动例程调用的,所以从main
函数return
时仍返回到启动例程中,main
函数的返回值被启动例程得到,如果将启动例程表示成等价的C代码(实际上启动例程一般是直接用汇编写的),则它调用main
函数的形式是:
exit(main(argc, argv));
也就是说,启动例程得到main
函数的返回值后,会立刻用它做参数调用exit
函数。exit
也是libc
中的函数,它首先做一些清理工作,然后调用上一章讲过的_exit
系统调用终止进程,main
函数的返回值最终被传给_exit
系统调用,成为进程的退出状态。我们也可以在main
函数中直接调用exit
函数终止进程而不返回到启动例程,例如:
#include <stdlib.h> int main(void) { exit(4); }
这样和int main(void) { return 4; }
的效果是一样的。在Shell中运行这个程序并查看它的退出状态:
$ ./a.out
$ echo $?
4
按照惯例,退出状态为0表示程序执行成功,退出状态非0表示出错。注意,退出状态只有8位,而且被Shell解释成无符号数,如果将上面的代码改为exit(-1);
或return -1;
,则运行结果为
$ ./a.out
$ echo $?
255