背景知识:
gcc/g++是一个编译器,注意区分编译器和编辑器,vim是是编辑器。简单的说,编辑器是我们敲代码的工具,我们在编辑器上写出我们需要实现的功能;编译器负责实现功能,把我们写的高级语言编译成计算机看得懂的机器语言。
程序的翻译经过了预处理、编译、汇编、链接四个过程。
预处理:头文件展开,条件编译,宏替换,去注释等;
编译:生成汇编,gcc会首先检查代码的规范性、是否有语法错误等,检查无误之后才开始生成汇编代码;
汇编:生成机器可识别的代码,将我们自己的代码进行翻译行程二进制目标文件,不可被执行;
链接:生成可执行文件或库文件,将形成的.obj文件和库文件进行合并,形成可执行程序。
gcc语法:
gcc [选项] 要编译的文件 [选项] 目标文件
编译过程:
预处理:gcc -E 要编译的文件 -o 目标文件,-E的作用是让gcc在预处理结束后就停止编译,-o是指目标文件,.i文件为已经与处理过的原始程序。
编译:gcc -S .i文件 -o .s文件,用户可以使用-S选项来查看,这里只是进行编译,生成汇编代码,而没有进行汇编。
汇编:gcc -c .s文件 -o .o文件,这里已经将汇编语言转换为二进制目标文件了。
反面教材来了,我在这里输错一些东西,第一次汇编,我-c输成-s了,就造成了/usr/bin/ld: no .eh_frame_hdr table will be created.错误,这是依赖关系的问题,之后Makefile的时候会提到。随后又出现了另一个问题,应该生成test.o文件,我误输成test.c了,但是我这个目录下已经有个test.c文件了,就造成原来的test.c直接被覆盖了。然后我就继续用test.s文件进行汇编,一样可以汇编出正确文件,应为我的test.s是经过源码预处理、编译过来的,就算源码被删除也不会影响已经生成test.s文件。这里也从侧面说明了,我们给文件加上后缀的重要性,就算Linux并不依据后缀判定文件类型,但是这个后缀是写给我们自己看的,除了部分文件有特殊颜色标识外,大多数文件是正常的白色。
链接:gcc .o文件 -o 可执行文件或库文件
上面的步骤可以不用一步一步的走,直接gcc -o 生成文件 目标文件,可以直接生成可执行文件。
概念补充:
库函数
在C程序中,有很多东西都是没有定义函数实现的,而是被包含在库函数中实现的。链接的作用就是链接到这些库函数。Linux系统默认携带了语言级别的头文件和语言对应的库。
函数库一般分两种静态库和动态库,库的本质也是文件。静态库一般命名方式为lib xxx .a,在编译链接时静态库的代码会全部被加入到可执行文件中,因此生成的文件比较大,同时程序运行时也就不需要库文件了,就算对应库文件被删除或者修改也不会影响对应的程序;动态库则为lib xxx .so,动态库与静态库相反,并没有把代码加入到执行文件中,而是在程序运行时由链接文件加载库,从而大大节省空间,但是如果对应的库被修改就会影响到对应的程序。
gcc选项:
-E:只激活预处理,但是并不生成文件,如果要生成文件需要重定向到一个输出文件里面
-S(大写S):编译到汇编语言,不进行汇编和链接,只生成汇编代码
-c(小写c):编译到目标代码
-o(小写o):文件输出到文件
-static:对生成的文件采用静态链接
-g:生成调试信息,GNU调试器可利用该信息
-shared:次选项将尽量使用动态库,生成文件的体量相对较小
-O0,-O1,-O2,-O3编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-w(小写w):不生成任何警告信息。
-Wall:生成所有警告信息