在Windows的IDE中编写代码时,我们只需要点击按钮就可以完成程序的编译,从而生成可执行文件。但在Linux中,我们需要使用指令来进行程序的编译和运行,因此也就需要来了解一下从代码到可执行文件会经历什么。我们这里以Linux中的gcc编译器为例。
什么是编译?
把我们敲的代码变成可执行文件。
什么是可执行文件?
就是可以直接运行的程序文件。例如Windows中的.exe文件。
windows中如何编译?
在windows中我们使用IDE进行程序的编译和运行时仅需要鼠标点击几下就可以完成
Linux中如何编译代码?
在Linux中我们使用的gcc来编译C语言代码,使用g++来编译C++代码,这两个是不可以混用的。
我们都知道,在程序写好后,需要先进行编译,只有编译通过了,这个程序才可以尝试运行,如果编译都不能通过,谈何运行呢?
而程序的编译大致分为四部分:
以上这四个阶段就是一个程序在整个编译阶段会经历的四步操作,接下来就让我们详细看一下这四步操作都在干什么。
假设此时我们写了一个名叫mian.c的程序。这是编译的第一阶段,这个阶段会做什么?
什么是宏替换?
宏替换:将定义的宏直接用值替换掉
什么是去注释?
去注释:把文件中所写的注释去掉
什么是引入头文件?
我们在编写代码的过程中,不可避免地要使用库函数,因此会包含对应的头文件,头文件中存储的是库函数的函数声明。这一步就是把头文件中的包含的函数声明等内容复制到我们写的main.c程序中。这里需要注意,头文件中放的都是函数的声明,并不是定义,函数的定义在库中。也就是说,这里是把头文件中的函数声明搬进来了。
这个阶段执行完成后,生成的文件本质还是一个C程序,因为它只是引入了一些函数声明,去掉了注释,进行了宏的替换。所以这一步并不会发现我们程序的错误,我们把这个新生成的文件称为 main.i 文件。
编译阶段编译器会对main.i文件做什么?
首先检查main.i文件中的语法语义有没有错,有错自然就会报错了。如果没有错,就会把我们的mian.i程序解释成汇编指令mian.s文件。
经过这个阶段,mian.s文件就成为了汇编代码,不再是C代码。
汇编阶段会对main.s文件做什么?
这一步会将会汇编指令mian.s文件解释成二进制指令文件,也就是电脑能够识别的指令文件。此时的main.o文件就是二进制文件。
链接阶段会对main.o文件做什么?
生成main.o是不够的,此时的它还无法执行。因为我们在预处理阶段引入的仅仅是函数的声明,但是光有声明,没有定义,这也运行不了啊。
因此在这个阶段,会把main.o文件和存储函数定义的库文件进行链接,让我们的程序运行函数时,可以找到函数的实现。从而生成可执行程序main文件。
链接方式有两种:静态链接和动态链接,Linux中默认是动态链接。
生成可执行程序时,会把函数在库中的实现搬到我们的文件中,这样所有的函数声明和函数定义都在可执行文件中了。
如果我们的程序中调用了库函数A,动态链接会把A函数定义在库文件中的位置记录下来,保存到可执行文件中,并不会把函数的具体的实现复制过来。
当可执行程序运行时,动态库就会被加载到内存中,当可执行文件需要用到哪个函数的话,只需要根据记录在可执行文件中的位置,去内存中的动态库寻找即可。
注意:这里的-o是指定生成的文件名称。
gcc -E main.c -o main.i
gcc -S main.i -o main.s
gcc -c main.s -o main.o
gcc mian.o -o mian
按照上面的分步编译虽然可以达到我们的预期目标,但是比较麻烦,毕竟敲那么多指令也比较费事,因此推荐采用下面这样的合并编译。
上面这些指令可以提高我们的效率,因此通常直接使用第四条指令一步到位。