一、问题描述
可能你在看makefile教程的时候会碰到一个问题,就像我就碰到了,我看的是陈皓 (CSDN)前辈的教程
https://seisman.github.io/how-to-write-makefile/overview.html
我把问题贴出来,里面有一段makefile是这么写的:
edit : main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
这个看着有点长,为方便阐述我把这句简化一下,记作makefile(Ⅰ),主要是想要表达这个语法
edit : main.o
gcc -o edit main.o
以前我写makefile是这么写的,记作makefile(Ⅱ):
main.o : main.c
gcc -o main.o main.c
这个时候就犯迷糊了,啥意思啊,这咋整啊,难道以前写的makefile(Ⅱ)生成的main.o还能再改个名,改成edit不成,然后我就在shell中手动执行了一下makefile(Ⅱ)中的gcc命令,首先生成main.o:
gcc -o main.o main.c
然后再执行makefile(Ⅰ)里的写法:
gcc -o edit main.o
结果报错了:
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
二、问题原因
于是搜索报错原因,说是找不到main函数的问题,莫非我把main写错了?于是去看自己的源代码,结果没有错误。那又是为啥?
翻了很多博客,最后在这篇博客里找到了原因,讲了C源文件到可执行文件的过程:
参考:https://www.cnblogs.com/qytan36/archive/2010/05/25/1743955.html
C源文件到可执行文件共经历了4个过程。在使用GCC编译程序时,编译过程可以被细分为四个阶段,包括预处理、编译、汇编、链接。
1、预处理
在预处理阶段,编译器主要作加载头文件、宏替换、条件编译的作用。一般处理带“#”的语句。
我们可以通过gcc 的 -E 选项进行查看,如下所示:
gcc -E main.c > main.i
编译器将main.c预处理结果输出 main.i 文件。
2、编译
在编译过程中,编译器主要作语法检查和词法分析。在确认所有指令都符合语法规则之后,将其翻译成等价的中间代码或者是汇编代码。
gcc -S main.i -o main.s
编译器将预处理结果文件main.i翻译成汇编代码main.s
3、汇编
汇编阶段是把编译阶段生成的”.s”文件转成二进制目标代码。
gcc -c main.s -o main.o
编译器将main.s文件转化为main.o 文件。
4、链接
在成功编译之后,就进入了链接阶段。链接就是将目标文件、启动代码、库文件链接成可执行文件的过程,这个文件可被加载或拷贝到存储器执行。
gcc main.o -o main.exe
编译器将main.o链接成最终可执行文件main.exe
三、问题解决
看了上面的博客忽然意识到,我之前在第一部分里写的命令:
gcc -o edit main.o
里面的main.o是我用命令gcc -o main.o main.c生成的可执行文件,这个main.o是我习惯性的取名为.o后缀,但我之前并不知道它是什么类型的文件。其实这个并不是通过上面第二节中第二步编译生成的.obj文件!而是最终生成的可执行的.out文件!终于知道弄错在什么地方了!
四、gcc -c与gcc -o以及不加参数的区别
以下摘自gcc –help的解释(gcc version 7.3.0):
-c Compile and assemble, but do not link.
-o <file> Place the output into <file>.
'none' means revert to the default behavior of guessing the language based on the file's extension.
中文翻译一下:
-c 编译和汇编,但不要链接。
-o <file> 将输出放入<文件>。
'无参数' 表示恢复为基于文件扩展名猜测语言的默认行为。
1、通过gcc 不加参数可以一步直接编译生成可执行文件
gcc main.c
这里生成的是可执行文件a.out,当然可以通过-o选项更改生成文件的名字,比如将生成的可执行文件命名为hello.exe
gcc main.c -o main.exe
2、gcc -c 编译生成main.o
gcc -c main.c #生成main.o
gcc main.o #不加参数,gcc自动链接上一步生成的main.o来生成最终可执行文件a.out
当然也可以通过-o选项更改生成的执行文件的名字
gcc main.o -o main.exe
好了,还有啥不懂得可以一起探讨,我也是这两天碰到修改makefile的需求才接触makefile.^^