Compiling C
C 编程中相关文件后缀
.a 静态库 (archive)
.c C源代码(需要编译预处理)
.h C源代码头文件
.i C源代码(不需编译预处理)
.o 对象文件
.s 汇编语言代码
.so 动态库
单个源文件生成可执行程序
下面是一个简单的“hello, world”程序的源代码:T
/* helloworld.c */ #include <stdio.h> int main(int argc,char *argv[]) { printf(“hello, world\n”); return(0); }
最简单直接的编译该代码为可执行程序的方法是,将该代码保存为文件 helloworld.c,并执行以下命令:
$ gcc helloworld.c
编译器通过检查命令行中指定的文件的後缀名可识别其为 C 源代码文件。GCC 默认的动作:编译源代码文件生成对象文件(object file),链接对象文件得到可执行程序,删除对象文件。由于命令行中未指定可执行程序的文件名,编译器采用默认的 a.out。在命令行中输入程序名可使其执行并显示结果:
$ ./a.out hello, world
选项 -o 用来指定所生成的可执行程序的文件名。下面的命令生成名为 howdy 的可执行程序:
$ gcc helloworld.c -o howdy
在命令行中输入程序名将使其执行,如下:
$ ./howdy hello, world
源文件生成对象文件
选项 -c 指示 GCC 编译源代码文件,但将对象文件保留在磁盘中并跳过链接对象文件生成可执行文件这一步。在这种情况下,默认的输出文件的文件名同源代码文件名一致,只不过后缀换为 .o 。例如:下面的命令将生成名为 helloworld.o 的对象文件:T
$ gcc -c helloworld.c
选项 -o 可用来指定生成的对象文件的文件名。以下命令将产生名为harumph.o的对象文件:
$ gcc -c helloworld.c -o harumph.o
当构建对象库或者生成一系列对象文件以备稍后链接用时,一条命令即可从多个源码文件生成对应的对象文件。下面的命令将生成对象文件arglist.o, ponder.o 与 listsort.o:
$ gcc -c arglist.c ponder.c listsort.c
多个源文件生成可执行程序
即使多个源码文件被编译,GCC编译器也会自动进行链接操作。例如:下面的代码保存在名为 hellomain.c 的文件中并调用一个名为 sayhello()的函数:
/* hellomain.c */ void sayhello(void); int main(int argc,char *argv[]) { sayhello(); return(0); }
以下代码保存在名为 sayhello.c 的文件中并定义了 sayhello() 函数:
/* sayhello.c */ #include <stdio.h> void sayhello() { printf(“hello, world\n”); }
下面的命令将两个文件分别编译为对象文件且将其链接为可执行程序 hello,并删除对象文件:
$ gcc hellomain.c sayhello.c -o hello
编译预处理
选项 -E 指示编译器只进行编译预处理。下面的命令将预处理源码文件 helloworld.c 并将结果在标准输出中列出:
$ gcc -E helloworld.c
选项 -o 用来将预处理过的代码定向到一个文件。像本文一开始给出的后缀列表所给出的,不需经过预处理的C源码文件保存为后缀为 .i的文件中,这种文件可以这样来获得:
$ gcc -E helloworld.c -o helloworld.i
生成汇编代码
选项 -S 指示编译器生成汇编语言代码然后结束。下面的命令将由 C 源码文件 helloworld.c 生成汇编语言文件 helloworld.s:
$ gcc -S helloworld.c
汇编语言的形式依赖于编译器的目标平台。如果多个源码文件被编译,每个文件将分别产生对应的汇编代码模块。
创建静态库
静态库是编译器生成的普通的 .o 文件的集合。链接一个程序时用库中的对象文件还是目录中的对象文件都是一样的。静态库的另一个名字叫归档文件(archive),管理这种归档文件的工具叫 ar 。
要构建一个库,首先要编译出库中需要的对象模块。例如,下面的两个源码文件为 hellofirst.c 和 hellosecond.c:
/* hellofirst.c */ #include <stdio.h> void hellofirst() { printf(“The first hello\n”); } /* hellosecond.c */ #include <stdio.h> void hellosecond() { printf(“The second hello\n”); }
这两个源码文件可以用以下命令编译成对象文件:
$ gcc -c hellofirst.c hellosecond.c
程序 ar 配合参数 -r 可以创建一个新库并将对象文件插入。如果库不存在的话,参数 -r 将创建一个新的,并将对象模块添加(如有必要,通过替换)到归档文件中。下面的命令将创建一个包含本例中两个对象模块的名为 libhello.a 的静态库:
$ ar -r libhello.a hellofirst.o hellosecond.o
现在库已经构建完成可以使用了。下面的程序 twohellos.c 将调用该库中的这两个函数:
/* twohellos.c */ void hellofirst(void); void hellosecond(void); int main(int argc,char *argv[]) { hellofirst(); hellosecond(); return(0); }
程序 twohellos 可以通过在命令行中指定库用一条命令来编译和链接,命令如下:
$ gcc twohellos.c libhello.a -o twohellos
静态库的命名惯例是名字以三个字母 lib 开头并以后缀 .a 结束。所有的系统库都采用这种命名惯例,并且它允许通过 -l(ell) 选项来简写命令行中的库名。下面的命令与先前命令的区别仅在于 gcc 期望的找寻该库的位置不同:
$ gcc twohellos.c -lhello -o twohellos
指定完整的路径名可使编译器在给定的目录中寻找库。库名可以指定为绝对路径(比如 /usr/worklibs/libhello.a)或者相对与当前目录的路径(比如 ./lib/libhello.a)。选项 -l 不能具有指定路径的能力,但是它要求编译器在系统库目录下找寻该库。