將通用模塊編譯為靜態庫(.a),可實現代碼復用、隱藏實現細節、加快大項目鏈接速度。本文以一個簡單的棧模塊為例,完整演示 **源碼拆分 → 編譯目標文件 → 打包靜態庫 → 鏈接使用** 的全過程(Linux/GCC 環境)。
.
├── main.c
└── stack/
├── stack.c
├── stack.h
├── push.c
├── pop.c
└── is_empty.c
#ifndef STACK_H
#define STACK_H
extern void push(char);
extern char pop(void);
extern int is_empty(void);
#endif
char stack[512];
int top = -1;
/* push.c */
extern char stack[512];
extern int top;
void push(char c) { stack[++top] = c; }
/* pop.c */
extern char stack[512];
extern int top;
char pop(void) { return stack[top--]; }
/* is_empty.c */
extern int top;
int is_empty(void) { return top == -1; }
#include <stdio.h>
#include "stack.h"
int main(void)
{
push('a');
return 0;
}
gcc -c stack/stack.c stack/push.c stack/pop.c stack/is_empty.c
# 產生 stack.o push.o pop.o is_empty.o
ar rs libstack.a stack.o push.o pop.o is_empty.o
# r: 插入/替換 s: 創建索引(供鏈接器快速查找符號)
# 等價於:ar r libstack.a ... && ranlib libstack.a
lib + 名稱 + .a(Archive)。ar 類似 tar,但專用於目標文件打包。gcc main.c -L. -lstack -Istack -o main
-L.:當前目錄搜索庫文件(必須,默認不搜當前目錄)。-lstack:鏈接 libstack.a(去掉 lib 前綴和 .a 後綴)。-Istack:頭文件搜索路徑。./main
# 無輸出即成功(main 僅 push 一個字符)
反彙編可見,main 只調用 push,最終可執行文件**僅包含 push.o**,pop.o 和 is_empty.o 被自動剔除。這是靜態庫相較於直接鏈接一堆 .o 文件的巨大優勢:
# 直接鏈接所有 .o(無按需提取)
gcc main.c stack.o push.o pop.o is_empty.o -Istack -o main
# 所有符號均會鏈接進可執行文件
gcc -lstack 會**優先搜索共享庫 libstack.so**,找不到才用靜態庫。強制靜態鏈接:
gcc main.c -static -L. -lstack -Istack -o main
gcc -print-search-dirs
# libraries 欄即為庫搜索路徑
| 問題 | 原因/解決 |
|---|---|
| ar: creating libstack.a | 正常提示,首次創建庫文件時出現。 |
| undefined reference to `push' | 未加 -lstack 或 -L 路徑錯誤。 |
| 頭文件找不到 | 缺少 -Istack 或路徑拼寫錯誤。 |
| 符號衝突 | 多個庫定義同名符號,調整鏈接順序或使用命名空間前綴。 |
gcc -c 生成 .o,ar rs 打包 .a。gcc -L路徑 -l庫名 -I頭文件路徑 鏈接使用。libxxx.a。掌握靜態庫構建,是邁向 C/C++ 工程化開發的關鍵一步。