命令格式:

命令格式:
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-Wpedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...] [-Umacro]
[-foption...] [-mmachine-option...]
[-o outfile] [@file] infile...

 

前言:

编译的目的是将代码转换成二进制数据以提供给计算机执行,但是从代码到二进制数据的过程并不是一步就完成的,主要有如下几个阶段:

1)预编译/预处理:

预编译指定源文件,替换其中的宏,将#include语句替换为头文件中的内容。

此阶段依旧是文本到文本的转换,输出结果依旧是文本数据。

命令参数: gcc -E

例:

#预编译 test.c 文件,如果test.c中有include语句,那么取 ./include中查找
#对应的头文件,如果找不到,去系统默认的头文件路径中找

#以下语句会把结果输出到屏幕上
gcc -E -I./include test.c

#转出输出结果到文件中,方便观察
gcc -E -I./include test.c > 1.txt

注:直接使用gcc  xxx  -Ixxx  -o  1 这里的-I其实就是应用在预编译阶段

 

2)汇编:

汇编是将文本内容翻译成汇编语言,这个阶段依旧没有生成二进制数据,还是文本的替换,只不过是高级语言向汇编语言的变更。

命令参数 : gcc -S

例:

#-S选项默认先执行-E,编译的后一个阶段会默认执行前一阶段的动作
gcc -S 1.cpp -o 1

 

3)编译:

编译是将汇编代码转换成二进制数据。

命令参数 : gcc -c

例:

gcc -c 1.cpp -o 1.o

 

4)链接:

这是工程构建中比较重要的一步,编译只是将源码文件(.c .cpp 等等)转换成二进制文件,但是这些文件是互相独立的孤岛,他们互相之间仅持有对方的符号表信息,只是知道自己需要调用什么接口,但是并不知道接口对应代码坐落的具体地址。

链接行为可描述为:“将所有编译获得的二进制文件 和 需要引用的外部库 归纳到一起,然后为各个二进制文件中的内容安排好虚拟内存地址,最后分析这些文件中的外部接口调用,把调用正确地指向刚才分配的虚拟内存地址上,这样各个二进制文件中对于其他二进制文件的函数调用就可以被正确的指向了”。另外这其中也包括了常量数据,静态数据,全局变量初始化等等的内存分配。

命令:gcc -o

总而言之:链接唯一的责任就是向虚拟内存空间的各个段(.data .text .cdata等)中填数据,再顺带解决下函数调用地址定向问题。

 

其他:

  • 选项连续和不连续是不一样的
  • -I后紧跟目录,指定头文件查找目录,可使用多个-I来指定多个目录
  • 预编译-E不生成文件,如果想预览预编译的效果,可重定向gcc -E -I./include test.c > 1.txt
  • -x指定编译语言,此时无视文件后缀,可以有gcc -x c 1.txt,此时1.txt中的内容将被当做c语言对待
  • 预处理和编译-S,生成汇编代码,汇编代码可读
  • -fno-asm,禁止将asm,inline和typeof用作关键字
  • -include,如果在文件中没有#include <xxx.h>或"xxx.h",而又“不想改代码”,那么可以通过此命令在编译时引入头文件
      //func.c
1 void myprintf(void)
2 {
3 printf("hello world\n");
3 }
gcc -c func.c -include /usr/include/stdio.h -o func.o
  • -idirafter dir 在-I的目录里面查找失败,将到这个目录里面查找.
    -iprefix prefix -iwithprefix dir 一般一起使用,当-I的目录查找失败,会到prefix+dir下查找
  • -nostdinc,不再使用默认头文件路径,一般个-I一起使用以精确指定头文件位置
  • -C(区别与-c),预处理时不删除注释,一般和-E一起使用,用以分析程序。和-S一起使用没效果,只能和-E一起,生成一个包含头文件且有注释的完整代码
  • -M,生成文件关联的信息。有点像makefile中的依赖关系
 [root@localhost func]# gcc -M func.c -I../include/1
func.o: func.c /usr/include/stdc-predef.h ../include/1/1.h \
/usr/include/stdio.h /usr/include/features.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-redhat-linux/4.8.2/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/stdlib.h /usr/include/bits/waitflags.h \
/usr/include/bits/waitstatus.h /usr/include/endian.h \
/usr/include/bits/endian.h /usr/include/bits/byteswap.h \
/usr/include/bits/byteswap-16.h /usr/include/sys/types.h \
/usr/include/time.h /usr/include/sys/select.h /usr/include/bits/select.h \
/usr/include/bits/sigset.h /usr/include/bits/time.h \
/usr/include/sys/sysmacros.h /usr/include/bits/pthreadtypes.h \
/usr/include/alloca.h /usr/include/bits/stdlib-float.h
  • -MM,作用和-M一样,但是忽略#include <xxx.h>这种系统默认依赖
  • -MD,和-M一样,但是会把结果输出到.d文件中
  • -MMD,和-MM一样,但是会把结果输出到.d文件中
  • -L,后跟函数库的路径,-L../mylib
  • -l,后跟库的名字,库需要用lib作为开头,使用时省略lib
-L和-l要联合在一起使用,比如我有库yk,全名叫做libyk.a,如果把其放在默认的库搜索目录中,那么只需要使用-lyk即可
如果libyk.a不在默认的库搜索目录中,那么就需要用-L指定一下搜索路径,即-L/home/yk/ -lyk
  • -O0 -O1 -O2 -O3 ,编译器的四个优化级别,-O0表示没有优化,-O1表示缺省优化级别,-O3尽可能多的优化
  • -g,加入调试信息,加入调试信息的输出文件明显要大于未加入调试信息的文件
  • -static,禁止使用动态库
  • -share,使用动态库
  • -w,不生成任何警告
  • -Wall,生成所有警告
  • 查看lib库的搜索路径 : gcc -print-search-dirs
  • 查看头文件的搜索路径:gcc -v -x c -E - (-x c用来指定为c语言,这个命令的最后会输出include<>和include""的搜索路径)
  • 查看动态库的搜索路径:cat /etc/ld.so.conf
    添加搜索路径到默认路径:vim /etc/ld.so.conf
    编译时指定临时动态库搜索路径:gcc -L/home/mylib