源文件需要经过编译才能生成可执行文件,pc下的编译工具链为gcc, ld, objcopy等, 它们编译出来的程序在x86平台下运行,要编译出可以在arm平台运行的程序,就需要使用交叉编译工具 arm-linux-gcc, arm-linux-ld等,主要介绍前者.
一个c/c++文件要经过 预处理(preprocessing), 编译(compilation), 汇编(assembly)和连接(linking)四个步骤才可以变成可执行文件.日常通常用”编译”统称这四个步骤,下面分别介绍这四个步骤:
1) 预处理: c/c++源文件中,以”#”为开头的命令为预处理命令,如”#include”包含命令, “#define”宏定义命令,”#if”,”#ifdef”条件编译命令等. 预处理就是将要包含的文件插入到源文件中,将宏定义展开,根据条件编译命令选择使用需要的代码,最后生成一个”.i”文件进行进一步处理->".i"文件
2)编译:就是把c/c++代码如”.i”文件 “翻译” 成汇编代码 ->“.S”文件
3)汇编;就是将第二步输出的汇编代码”翻译”成一定格式的机器代码,Linux下一般表现为ELF目标文件(OBJ文件),而反汇编则是将机器代码转换成汇编代码,这在调试中经常用到.->”.o”文件按
4)连接:连接就是将上一步生成的OBJ文件和库文件连接起来最终生成在特定平台上运行的可执行文件
下面以一个简单的 helloworld程序为例
在进行编译之前首先介绍一下gcc编译器以及常用选项:
gcc编译器使用方法:
gcc [选项] 文件名 //选项可以有多个
gcc常用选项:
选项 |
功能 |
-v |
查看当前gcc编译器的版本,也可以显示gcc执行时候的详细过程 |
-o |
指定输出文件名为file,这个file名称不可以和源文件名同名 |
-E |
只预处理 |
-S |
只编译 |
-c |
进行编译和汇编,不会进行连接 |
在Ubuntu终端下创建一个hello.c文件,将程序写好后,退出,保存.
详细每一步的命令如下:
如果没有-o选项,则只输出”a.out”可执行文件
这样一步一步来比较麻烦,gcc会对.c文件默认进行预处理操作,于是省略了前俩步,只需要-c来编译,汇编,得到.o文件,再将.o文件进行链接,得到可执行文件.简单步骤如下
从一个.c文件生成一个可执行的文件经过了
hello.c(预处理)->hello.i(编译)->hello.s(汇编)->hello.o(链接)->hello
这四个步骤
当我们详细看其生成文件大小的时候会发现可执行文件比源文件大很多
这是因为链接的时候会将生成的OBJ 文件,系统库的OBJ文件,库文件链接起来,这样最后的可执行文件就会比原来的大很多
gcc -v -nostdlib -o hello hello.o:
会提示因为没有链接系统标准启动文件和标准库文件,而链接失败。
这个-nostdlib 选项常用于裸机 bootloader、 linux 内核等程序,因为它们不
需要启动文件、标准库文件。
一般应用程序才需要系统标准启动文件和标准库文件。 裸机/bootloader、linux 内核等程序不需要启动文件、标准库文件。
而链接又可以分为动态链接以及静态链接
动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需要的动态库才可以运行,上面的都是动态链接,生成的程序体积相对较小,但是需要一栏所需的动态库,否则无法执行.
而静态文件则使用静态库进行连接,生成的程序包含程序所需要的全部库,可以直接运行,但是静态连接生成的程序体积较大:
比较俩种链接方式生成的可执行程序的大小:
可见比动态链接大很多.
再举一个包含三个文件的例子:
终端新建一个文件夹,创建三个文件 main.c, sub.c, sub.h如下:
用helloworld中的简单命令如下:
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -o test sub.o main.o
其中 main.o, sub.o是经过了预处理,编译,汇编后而生成的obj文件,最后一步将他们链接成了可执行文件test