以前我们常用
gcc main.c -o main
命令编译一个程序,其实也可以分三步做,第一步生成汇编代码,第二步生成目标文件,第三步生成可执行文件:
$ gcc -S main.c
$ gcc -c main.s
$ gcc main.o
由此可见,如果我们用
gcc
做链接,
gcc
其实是调用
ld
将目标文件
crt1.o
和我们的
hello.o
链接在一起。
crt1.o
里面已经提供了
_start
入口点,我们的汇编程序中再实现一个
_start
就是多重定义了,链接器不知道该用哪个,只好报错。另外,
crt1.o
提供的
_start
需要调用
main
函数,而我们的汇编程序中没有实现
main
函数,所以报错。
如果目标文件是由C代码编译生成的,用
gcc
做链接就没错了,整个程序的入口点是
crt1.o
中提供的
_start
,它首先做一些初始化工作(以下称为启动例程,Startup Routine
),然后调用C代码中提供的
main
函数。所以,以前我们说
main
函数是程序的入口点其实不准确,
_start
才是真正的入口点,而
main
函数是被
_start
调用的。
main
函数最标准的原型应该是
int main(int argc, char *argv[])
,也就是说启动例程会传两个参数给
main
函数,这两个参数的含义我们学了指针以后再解释。我们到目前为止都把
main
函数的原型写成
int main(void)
,这也是C标准允许的,如果你认真分析了上一节的习题,你就应该知道,多传了参数而不用是没有问题的,少传了参数却用了则会出问题。