GCC编译器(GNU C Compiler)是GNU组织的一款开源 编译器,它是Linux环境下的默认C语言编译器。它处理能够高效的编译C语言以外,还可以编译其他语言。并且,现在的GCC已经不光包括编译器本身,还包含了编译过程中的工具链。
1 GCC编译流程
在学习使用GCC编译程序之前,首先要知道编译C程序的基本流程,一般情况下分为下面四步:
(1) 对C语言进行预处理,生成*.i文件。
(2) 将上一步生成的*.i文件编译生成汇编语言文件,后缀名为*.s
(3) 将汇编语言文件*.s经过汇编,生成目标文件,后缀名为*.o
(4) 将各个模块的*.o文件链接起来,生成最终的可执行文件
2 GCC常用选项
GCC的编译选项非常多,现在有上千个,但是我们常用的并不多,下面我们只介绍其中非常实用的几个。
在这之前,我们先编写下面的几个源文件,以备测试只用。
1 //main.c 2 #include <stdio.h> 3 4 extern int add(int a, int b); 5 extern int mul(int a, int b); 6 7 int main(void) 8 { 9 int a = 10, b = 5; 10 int result; 11 12 result = add(a, mul(a, b)); 13 printf("result = %d ", result); 14 return 0; 15 }
1 //test1.c 2 int add(int a, int b) 3 { 4 return a+b; 5 }
1 //test2.c 2 int mul(int a, int b) 3 { 4 return a*b; 5 }
2.1 -E选项
该选项对C语言源文件进行预处理,但是并不编译该程序。对于一般的预处理问题(比如,宏的展开问题、文件的包含问题等),可以使用这个选项进行查看。另外,如果直接使用此选项,程序会将预处理结果直接输出到终端,不便于查看,因此,一般可以使用重定向将程序输出结果保存到一个文本文件中,格式如下:
gcc -E source.c > output
如果我们对main.c执行预编译,得到的output文件内容如下:
1 # 1 "main.c" 2 # 1 "<command-line>" 3 # 1 "/usr/include/stdc-predef.h" 1 3 4 4 # 1 "<command-line>" 2 5 # 1 "main.c" 6 7 # 1 "/usr/include/stdio.h" 1 3 4 8 # 27 "/usr/include/stdio.h" 3 4 9 # 1 "/usr/include/features.h" 1 3 4 10 # 374 "/usr/include/features.h" 3 4 11 # 1 "/usr/include/i386-linux-gnu/sys/cdefs.h" 1 3 4 12 # 385 "/usr/include/i386-linux-gnu/sys/cdefs.h" 3 4 13 # 1 "/usr/include/i386-linux-gnu/bits/wordsize.h" 1 3 4 14 # 386 "/usr/include/i386-linux-gnu/sys/cdefs.h" 2 3 4 15 # 375 "/usr/include/features.h" 2 3 4 16 # 398 "/usr/include/features.h" 3 4 17 # 1 "/usr/include/i386-linux-gnu/gnu/stubs.h" 1 3 4 18 19 20 21 22 23 ............................... 826 827 extern int pclose (FILE *__stream); 828 829 830 831 832 833 extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__)); 834 # 913 "/usr/include/stdio.h" 3 4 835 extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); 836 837 838 839 extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; 840 841 842 extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); 843 # 943 "/usr/include/stdio.h" 3 4 844 845 # 3 "main.c" 2 846 847 extern int add(int a, int b); 848 extern int mul(int a, int b); 849 850 int main(void) 851 { 852 int a = 10, b = 5; 853 int result; 854 855 result = add(a, mul(a, b)); 856 printf("result = %d ", result); 857 return 0; 858 }
当然,我们也可以使用接下来要介绍的-o命令来指定输出文件名称,格式如下:
gcc -E source.c -o source.i
使用该命令对main.c进行预处理,得到的main.i文件内容与之前重定向输出得到的output文件的内容完全相同。
2.2 -S选项
该选项(大写S)将C语言源文件编译生成汇编语言文件,但是并不汇编该程序。注意:汇编过程的作用是将汇编语言文件编译成目标文件*.o,而-S选项的作用是得到汇编语言文件*.s。该选项的使用方法为:
gcc -S source.c
使用该选项,最终生成与源文件名称相同,但是后缀为*.s结尾的汇编语言文件。
xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc -S test1.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls main.c test1.c test1.s test2.c
当然,输入的源文件也不止一个,你可以编译当前目录下的所有C语言源文件:
xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc -S *.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls main.c main.s test1.c test1.s test2.c test2.s
我们也可以查看生成的汇编语言代码:
1 .file "test1.c" 2 .text 3 .globl add 4 .type add, @function 5 add: 6 .LFB0: 7 .cfi_startproc 8 pushl %ebp 9 .cfi_def_cfa_offset 8 10 .cfi_offset 5, -8 11 movl %esp, %ebp 12 .cfi_def_cfa_register 5 13 movl 12(%ebp), %eax 14 movl 8(%ebp), %edx 15 addl %edx, %eax 16 popl %ebp 17 .cfi_restore 5 18 .cfi_def_cfa 4, 4 19 ret 20 .cfi_endproc 21 .LFE0: 22 .size add, .-add 23 .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" 24 .section .note.GNU-stack,"",@progbits
2.3 -c选项
该选项(小写c)表示编译、汇编指定的源文件,但是不进行链接。该选项的使用方法如下:
gcc -c source.c
也就是在-c选项后面紧跟要编译、汇编的C源文件,最终生成与源文件名称相同,但是后缀为*.o结尾的目标文件。
xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls main.c test1.c test2.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc -c test1.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls main.c test1.c test1.o test2.c
可以看到,使用-c选项编译之后生成了对应的*.o目标文件。当然,你也可以一次性指定多个C源文件,使用-c选项后,会针对每一个C源文件生成一个相应的*.o目标文件。
xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc -c test2.c main.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls main.c main.o test1.c test1.o test2.c test2.o
2.4 -o选项
该选项(小写O)用于将输入文件编译后输出指定名称的文件。该选项有两种使用方法,第一种是紧跟gcc命令之后:
gcc -o app source1.c source2.c source3.c
那么,编译我们的测试程序可以使用下面的方式:
xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc -o app *.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls app main.c test1.c test2.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ./app result = 60
还有另外一种方式,即-o选项放置在最后:
gcc source1.c source2.c source3.c -o app
这种方式的逻辑性更强,语义可以理解为编译C语言源文件得到最终的可执行程序app,使用这种方式编译我们的测试程序过程如下:
xiaomanon@xiaomanon-machine:~/Documents/c_code$ rm app xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc test1.c test2.c main.c -o app xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls app main.c test1.c test2.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ./app result = 60
此外,此选项很多时候用作链接多个目标文件的时候,我们可能需要先对不同的源文件进行相应的操作来得到目标文件*.o,然后在最后将这些目标文件链接成一个可执行文件。
xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc -c *.c xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls main.c main.o test1.c test1.o test2.c test2.o xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc main.o test1.o test2.o -o app xiaomanon@xiaomanon-machine:~/Documents/c_code$ ls app main.c main.o test1.c test1.o test2.c test2.o xiaomanon@xiaomanon-machine:~/Documents/c_code$ ./app result = 60
2.5 -I选项
该选项用于指定包含的头文件的目录,这一点对于大型的代码组织来说是很有用的。
2.6 -g选项
该选项用来生成可以被gdb调试器使用的调试信息。只有使用了该选项后生成的可执行文件才带有程序中引用的符号表,这是gdb调试程序才能对可执行程序进行调试。
xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc *.c -o app xiaomanon@xiaomanon-machine:~/Documents/c_code$ ll app -rwxrwxr-x 1 xiaomanon xiaomanon 7381 12月 29 15:23 app* xiaomanon@xiaomanon-machine:~/Documents/c_code$ gcc -g *.c -o app xiaomanon@xiaomanon-machine:~/Documents/c_code$ ll app -rwxrwxr-x 1 xiaomanon xiaomanon 8825 12月 29 15:23 app*
以上命令分别生成了不带调试信息的可执行文件和带有调试信息的可执行文件,并对比了两者的文件大小,可以看出使用的-g选项生成的可执行文件明显要比没有使用-g选项的可执行文件大。