一个c语言程序从源文件到生成可执行文件,编译器需要共经历4个步骤:
1) 预处理:把c文件中预处理命令扫描处理完毕,即对源代码文件中的文件包含(#include)、预编译语句(如宏定义#define等)进行分析,此时生成的文件仍然是可读的。
2) 编译:把预处理后的结果编译成汇编或者目标模块,即生成汇编语言文件,此时生成果汇编成具体CPU上的目标代码模块,也即此时转换成具体的机器语言代码,此时生成的文件是不可读的非文本文件。
4) 连接:把多个目标代码模块连接生成一个大的目标模块,即将多个上面产生的机器代码文件(与其它的机器代码文件和库文件)汇集成一个可执行的二进制代码文件。
gcc作为c语言在linux下很著名的编译软件,分别有如下option来支持4个步骤:
名称 gcc选项 英文名称 gcc调用的程序 示例
预处理 -E Pre-Processing cpp gcc -E test.c -o test.i
编译 -S Compiling ccl gcc -S test.i -o test.s
汇编 -c Assembling as gcc -c test.s -o test.o
连接 无 Linking ld gcc test.o -o test
说明:
gcc在编译c语言文件时,首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(#include)、预编译语句(如宏定义#define等)进行分析;其次调用ccl进行编译工作,将文件编译成汇编语言文件,此时文件依旧是可读的;之后调用as进行汇编工作,将具体的汇编语言文件编译成cpu可执行的目标代码,此时文件不可读了;当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,链接。在链接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的库中链接到合适的地方。下面对上面4个过程做下分别的说明:
1、在预处理阶段,如果不用“-o”指定文件名,那么会默认将预处理结果输出到标准终端设备。
[root@dbbak tmp]# cat a.c
int main()
{
printf("shengtong test!\n");
}
[root@dbbak tmp]# gcc -E a.c
# 1 "a.c"
# 1 "
# 1 "
# 1 "a.c"
int main()
{
printf("shengtong test!\n");
}
[root@dbbak tmp]# gcc -E a.c -o a.i
[root@dbbak tmp]# cat a.i
# 1 "a.c"
# 1 "
# 1 "
# 1 "a.c"
int main()
{
printf("shengtong test!\n");
}
2、在编译阶段,如果不用“-o”指定文件名,那么默认会生成一个“*.s”的汇编语言文件。
[root@dbbak tmp]# gcc -S a.i
[root@dbbak tmp]# ls
a.c a.i a.s
[root@dbbak tmp]# gcc -S a.i -o a1.s
[root@dbbak tmp]# ls
a.c a.i a.s a1.s
[root@dbbak tmp]#
[root@dbbak tmp]# cat a.s
.file "a.c"
.section .rodata
.LC0:
.string "shengtong test!\n"
.text
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
subl $12, %esp
pushl $.LC0
call printf
addl $16, %esp
leave
ret
.Lfe1:
.size main,.Lfe1-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.2.3 20030502 (Red Hat Linux 3.2.3-42)"
[root@dbbak tmp]# cat a1.s
.file "a.c"
.section .rodata
.LC0:
.string "shengtong test!\n"
.text
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
subl $12, %esp
pushl $.LC0
call printf
addl $16, %esp
leave
ret
.Lfe1:
.size main,.Lfe1-main
.section .note.GNU-stack,"",@progbits
.ident "GCC: (GNU) 3.2.3 20030502 (Red Hat Linux 3.2.3-42)"
3、在汇编阶段,如果不用“-o”指定文件名,那么默认会生成一个“*.o”的机器语言代码文件。
[root@dbbak tmp]# gcc -c a.s
[root@dbbak tmp]# ls
a.c a.i a.o a.s a1.s
[root@dbbak tmp]# gcc -c a1.s -o a1.o
[root@dbbak tmp]# ls
a.c a.i a.o a.s a1.o a1.s
[root@dbbak tmp]#
[root@dbbak tmp]# file a.o
a.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
[root@dbbak tmp]# file a1.o
a1.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
4、在连接阶段,不指定前面3个选项就是默认进入这个阶段,如果不用“-o”指定文件名,那么默认会生产一个“a.out”的可执行文件。
[root@dbbak tmp]# gcc a.o
[root@dbbak tmp]# ls
a.c a.i a.o a.out a.s a1.o a1.s -- 可执行文件
[root@dbbak tmp]# gcc a1.o -o a1.out
[root@dbbak tmp]# ls
a.c a.i a.o a.out a.s a1.o a1.out a1.s
[root@dbbak tmp]#
[root@dbbak tmp]# ./a.out
shengtong test!
[root@dbbak tmp]# ./a1.out
shengtong test!
5、编译c文件的时候,可以跳过前面阶段而直接进入后面阶段,如可以不分开执行预处理命令gcc -E test.c,而直接进入编译环节gcc -S test.c -o test.s,gcc会在执行“编译”环节(后面阶段)的时候发现没有“预编译”环节(前面阶段),它会自动补上。
gcc能够编译的语言文件有很多(常见的如c语言和c++语言等),它默认情况下依据文件的后缀名执行相应的编译,下面列出c和c++的:
.c 为后缀的文件,C语言源代码文件;
.i 为后缀的文件,是已经预处理过的C源代码文件;
.s 为后缀的文件,是汇编语言源代码文件;
.o 为后缀的文件,是编译后的目标文件;
.h 为后缀的文件,是程序所包含的头文件;
.a 为后缀的文件,是由目标文件构成的库文件;
.C .cc .cp .cpp .c++ .cxx 为后缀的文件,是C++源代码文件;
.ii 为后缀的文件,是已经预处理过的C++源代码文件;
.m 为后缀的文件,是Objective-C源代码文件;
.mi 是已经预处理过的Objective-C源代码文件;
.S 为后缀的文件,是经过预编译的汇编语言源代码文件。
当然,你可以乱写自己的文件名后缀,如将c文件以.cpp结尾或者以.eng结尾,那么gcc就无法根据后缀名来做正确的编译了,此时怎么办?gcc提供了“-x”选项来指定文件类型,“-x”选项内容有(只列出c和c++的):
c c-header cpp-output
c++ c++-cpp-output
objective-c objc-cpp-output
assembler assembler-with-cpp
当使用了“-x”选项后,那么其后面所有的文件都默认是其指定的文件类型,直到用“-x none”来指定结束。
[root@dbbak tmp]# gcc -x c -E a.sql -o a.i
[root@dbbak tmp]# cat a.i
# 1 "a.sql"
# 1 "
# 1 "
# 1 "a.sql"
int main()
{
printf("shengtong test!\n");
}
[root@dbbak tmp]# mv c.sql c.c
[root@dbbak tmp]#
[root@dbbak tmp]# gcc -x c -S a.sql b.sql -x none -S c.c
[root@dbbak tmp]# ls
a.i a.s a.sql b.s b.sql c.c c.s
下面再介绍下gcc的其他常用选项:
-pass-exit-codes
Normally the gcc program will exit with the code of 1 if any phase of the compiler returns a non-success return code. If you specify -pass-exit-codes, the gcc program will instead return with numeri-cally highest error produced by any phase that returned an error indication.
通常如果编译器遇到错误,返回值1;如果你想返回具体的错误代码,请用它。
-o file
Place output in file file. This applies regardless to whatever sort of output is being produced, whether it be an executable file,an object file, an assembler file or preprocessed C code.
Since only one output file can be specified, it does not make sense to use -o when compiling more than one input file, unless you are producing an executable file as output.
If -o is not specified, the default is to put an executable file in a.out, the object file for source.suffix in source.o, its assembler file in source.s, and all preprocessed C source on standard output. -- 这段话我在上面的1/2/3/4/5中已经介绍了
-v
Print (on standard error output) the commands executed to run the stages of compilation. Also print the version number of the com-piler driver program and of the preprocessor and the compiler proper.
这个选项的作用是把gcc编译c文件的过程给打印出来,如下面这个例子:
[root@dbbak tmp]# gcc -v a.c
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --host=i386-redhat-linux
Thread model: posix
gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-42)
/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/cc1 -lang-c -v -D__GNUC__=3 -D__GNUC_MINOR__=2 -D__GNUC_PATCHLEVEL__=3 -D__GXX_ABI_VERSION=102 -D__ELF__ -Dunix -D__gnu_linux__ -Dlinux -D__ELF__ -D__unix__ -D__gnu_linux__ -D__linux__ -D__unix -D__linux -Asystem=posix -D__NO_INLINE__ -D__STDC_HOSTED__=1 -Acpu=i386 -Amachine=i386 -Di386 -D__i386 -D__i386__ -D__tune_i386__ a.c -quiet -dumpbase a.c -version -o /tmp/cclLW8Ji.s
GNU CPP version 3.2.3 20030502 (Red Hat Linux 3.2.3-42) (cpplib) (i386 Linux/ELF)
GNU C version 3.2.3 20030502 (Red Hat Linux 3.2.3-42) (i386-redhat-linux)
compiled by GNU C version 3.2.3 20030502 (Red Hat Linux 3.2.3-42).
ignoring nonexistent directory "/usr/i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/include
/usr/include
End of search list.
as -V -Qy -o /tmp/ccRCof2i.o /tmp/cclLW8Ji.s
GNU assembler version 2.14.90.0.4 (i386-redhat-linux) using BFD version 2.14.90.0.4 20030523
/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crt1.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crti.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtbegin.o -L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3 -L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../.. /tmp/ccRCof2i.o -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtend.o /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crtn.o
上面的调用堆栈,充分反应了gcc编译过程,首先调用ccl执行预处理和编译,并且在/tmp目录下生成一个临时文件/tmp/cclLW8Ji.s文件;接着调用as执行汇编功能,也生成了临时文件/tmp/ccRCof2i.o;最后执行连接工作。
如果你只想看gcc的调用堆栈过程而不真正执行编译工作,怎么办?用“-###”选项:
-###
Like -v except the commands are not executed and all command argu-ments are quoted. This is useful for shell scripts to capture the driver-generated command lines.
从上面的过程中,我们可以看到,在各个步骤中gcc生成了中间临时文件,而如果你不想生成这些临时文件,而取而代之用管道,该怎么办?用“-pipe”选项:
-pipe
Use pipes rather than temporary files for communication between the various stages of compilation. This fails to work on some systems where the assembler is unable to read from a pipe; but the GNU assembler has no trouble.
举个例子如下,请看红色部分的管道:
[root@dbbak tmp]# gcc -### -pipe a.c
Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --host=i386-redhat-linux
Thread model: posix
gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-42)
"/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/cc1" "-lang-c" "-D__GNUC__=3" "-D__GNUC_MINOR__=2" "-D__GNUC_PATCHLEVEL__=3" "-D__GXX_ABI_VERSION=102" "-D__ELF__" "-Dunix" "-D__gnu_linux__" "-Dlinux" "-D__ELF__" "-D__unix__" "-D__gnu_linux__" "-D__linux__" "-D__unix" "-D__linux" "-Asystem=posix" "-D__NO_INLINE__" "-D__STDC_HOSTED__=1" "-Acpu=i386" "-Amachine=i386" "-Di386" "-D__i386" "-D__i386__" "-D__tune_i386__" "a.c" "-quiet" "-dumpbase" "a.c" "-o" "-" |
"as" "-Qy" "-o" "/tmp/cc8lJo7Z.o" "-"
"/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/collect2" "--eh-frame-hdr" "-m" "elf_i386" "-dynamic-linker" "/lib/ld-linux.so.2" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crt1.o" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crti.o" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtbegin.o" "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3" "-L/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../.." "/tmp/cc8lJo7Z.o" "-lgcc" "-lgcc_eh" "-lc" "-lgcc" "-lgcc_eh" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/crtend.o" "/usr/lib/gcc-lib/i386-redhat-linux/3.2.3/../../../crtn.o"
--help
Print (on the standard output) a description of the command line options understood by gcc. If the -v option is also specified then --help will also be passed on to the various processes invoked by gcc, so that they can display the command line options they accept. If the -W option is also specified then command line options which have no documentation associated with them will also be displayed.
--target-help
Print (on the standard output) a description of target specific command line options for each tool.
--version
Display the version number and copyrights of the invoked GCC.