我们继续用stack.c的例子。为了便于理解,我们把stack.c拆成四个程序文件(虽然实际上没太大必要),把main.c改得简单一些,头文件stack.h不变,本节用到的代码如下所示:

    1. /* push.c */
    2. extern char stack[512];
    3. extern int top;
    4.  
    5. void push(char c)
    6. {
    7. stack[++top] = c;
    8. }
    1. /* pop.c */
    2. extern char stack[512];
    3. extern int top;
    4.  
    5. char pop(void)
    6. {
    7. return stack[top--];
    8. }
    1. extern int top;
    2.  
    3. int is_empty(void)
    4. {
    5. return top == -1;
    6. }
    1. /* main.c */
    2. #include <stdio.h>
    3. #include "stack.h"
    4.  
    5. int main(void)
    6. {
    7. push('a');
    8. return 0;
    9. }

    这些文件的目录结构是:

    1. $ tree
    2. |-- main.c
    3. `-- stack
    4. |-- is_empty.c
    5. |-- pop.c
    6. |-- push.c
    7. |-- stack.c
    8. `-- stack.h
    9.  
    10. 1 directory, 6 files

    我们把stack.cpush.cpop.cis_empty.c编译成目标文件:

    1. $ gcc -c stack/stack.c stack/push.c stack/pop.c stack/is_empty.c

    库文件名都是以lib开头的,静态库以.a作为后缀,表示Archive。ar命令类似于命令,起一个打包的作用,但是把目标文件打包成静态库只能用ar命令而不能用tar命令。选项r表示将后面的文件列表添加到文件包,如果文件包不存在就创建它,如果文件包中已有同名文件就替换成新的。s是专用于生成静态库的,表示为静态库创建索引,这个索引被链接器使用。ranlib命令也可以为静态库创建索引,以上命令等价于:

    1. $ ar r libstack.a stack.o push.o pop.o is_empty.o
    2. $ ranlib libstack.a

    然后我们把libstack.amain.c编译链接在一起:

    1. $ gcc main.c -L. -lstack -Istack -o main

    -L选项告诉编译器去哪里找需要的库文件,-L.表示在当前目录找。-lstack告诉编译器要链接libstack库,-I选项告诉编译器去哪里找头文件。注意,即使库文件就在当前目录,编译器默认也不会去找的,所以-L.选项不能少。编译器默认会找的目录可以用-print-search-dirs选项查看:

    1. $ gcc -print-search-dirs
    2. install: /usr/lib/gcc/i486-linux-gnu/4.3.2/
    3. programs: =/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/:/usr/libexec/gcc/i486-linux-gnu/4.3.2/:/usr/libexec/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../i486-linux-gnu/bin/i486-linux-gnu/4.3.2/:/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../i486-linux-gnu/bin/

    那么链接共享库和链接静态库有什么区别呢?在讲过,在链接libc共享库时只是指定了动态链接器和该程序所需要的库文件,并没有真的做链接,可执行文件main中调用的libc库函数仍然是未定义符号,要在运行时做动态链接。而在链接静态库时,链接器会把静态库中的目标文件取出来和可执行文件真正链接在一起。我们通过反汇编看上一步生成的可执行文件main

    有意思的是,main.c只调用了push这一个函数,所以链接生成的可执行文件中也只有push而没有popis_empty。这是使用静态库的一个好处,链接器可以从静态库中只取出需要的部分来做链接。如果是直接把那些目标文件和main.c编译链接在一起:

    1. $ gcc main.c stack.o push.o pop.o is_empty.o -Istack -o main

    则没有用到的函数也会链接进来。当然另一个好处就是使用静态库只需写一个库文件名,而不需要写一长串目标文件名。