之前一直都是在Windows下享受IDE帶來的各種便利苗沧,但對(duì)于程序到底是怎么從源代碼變成可執(zhí)行文件的刊棕,一直不甚明了。聽了舒大師的Linux入門課后待逞,研究一下在Linux下如何進(jìn)行C語言程序開發(fā)甥角。
主要從三個(gè)方面來展開:
- GCC的基本使用
- 靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的編譯和鏈接
- Makefile的編寫
1. 任務(wù)概述
為了涵蓋前面提出的三個(gè)主題,設(shè)置了如下的任務(wù):
在4個(gè)c文件中分別實(shí)現(xiàn)4個(gè)函數(shù)识樱,將其中兩個(gè)文件編譯為靜態(tài)鏈接庫(kù)嗤无,另兩個(gè)文件編譯為動(dòng)態(tài)鏈接庫(kù),最后在主程序中分別調(diào)用這4個(gè)函數(shù)進(jìn)行輸出怜庸。
文件目錄定義:
./bin //生成的可執(zhí)行文件
./inc //頭文件
func.h
./lib //生成的庫(kù)文件
./obj //中間目標(biāo)文件
./src //源文件
func_d1.c
func_d2.c
func_s1.c
func_s2.c
main.c
Makefile
2. GCC基本使用
不考慮庫(kù)文件和makefile的使用当犯,直接用GCC進(jìn)行編譯。
生成中間目標(biāo)文件:
$ gcc -c src/func_d1.c
$ gcc -c src/func_d2.c
$ gcc -c src/func_s1.c
$ gcc -c src/func_s2.c
$ gcc -c src/main.c -I inc
鏈接文件:
$ gcc -o main func_d1.o func_d2.o func_s1.o func_s2.o main.o
結(jié)果測(cè)試:
$ ./main
結(jié)果輸出:
hello world!
Using the static library.
Input number is: 1.
Using the dynamic library.
Input string is: hello.
當(dāng)然割疾,這里還并沒有static library和dynamic library什么事嚎卫,只是單純地輸出一些字符串而已。
3. 靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
先刪除之前生成的可執(zhí)行文件杈曲,重新采用庫(kù)文件的方式來實(shí)現(xiàn)驰凛。
$ rm main
3.1 靜態(tài)鏈接庫(kù)
將func_s1.c
和func_s2.c
這兩個(gè)文件編譯為靜態(tài)鏈接庫(kù),生成libfunc_s.a
担扑。
$ ar rc libfunc_s.a func_s1.o func_s2.o
3.2 動(dòng)態(tài)鏈接庫(kù)
將func_d1.c
和func_d2.c
這兩個(gè)文件編譯為動(dòng)態(tài)鏈接庫(kù)恰响,生成libfunc_d.so
。
其中涌献,需要以-fpic
方式胚宦,重新編譯func_d1.o
和func_d2.o
,使其適應(yīng)共享庫(kù)的調(diào)用方式燕垃。
$ gcc -c -fpic src/func_d1.c src/func_d2.c
$ gcc -shared func_d1.o func_d2.o -o libfunc_d.so
3.3 程序調(diào)用
同時(shí)調(diào)用這兩個(gè)庫(kù)文件枢劝,生成最終的可執(zhí)行文件。
gcc src/main.c -I inc -L . -lfunc_s -Wl,-rpath lib -lfunc_d -o main
執(zhí)行生成的main
文件卜壕,結(jié)果與剛才相同您旁,但此時(shí)才是真正地分別通過靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的方式來實(shí)現(xiàn)函數(shù)調(diào)用的。
4. Makefile編寫
通過編寫Makefile文件轴捎,可以實(shí)現(xiàn)程序的自動(dòng)化編譯和鏈接鹤盒,并且對(duì)于大型的程序蚕脏,能夠?qū)⑽募M織得更加合理和有序。
Makefile文件如下所示:
SHELL = /bin/bash
CC = gcc
AR = ar rc
RM = rm -rf
MV = mv
MK = mkdir -p
INC_DIR = ./inc/
SRC_DIR = ./src/
OBJ_DIR = ./obj/
LIB_DIR = ./lib/
BIN_DIR = ./bin/
BIN_FILE = $(BIN_DIR)main
INC = $(INC_DIR)func.h
SRC_M = $(SRC_DIR)main.c
SRC_S = $(SRC_DIR)func_s1.c $(SRC_DIR)func_s2.c
SRC_D = $(SRC_DIR)func_d1.c $(SRC_DIR)func_d2.c
OBJ_M = main.o
OBJ_S = func_s1.o func_s2.o
OBJ_D = func_d1.o func_d2.o
LIB_S = $(LIB_DIR)libfunc_s.a
LIB_D = $(LIB_DIR)libfunc_d.so
all: init $(LIB_S) $(LIB_D) $(BIN_FILE)
init:
$(MK) $(OBJ_DIR)
$(MK) $(LIB_DIR)
$(MK) $(BIN_DIR)
$(LIB_S): $(OBJ_S)
$(AR) $(LIB_S) $(OBJ_S)
$(MV) $(OBJ_S) $(OBJ_DIR)
$(OBJ_S): $(SRC_S)
$(CC) -c $(SRC_S)
$(LIB_D): $(OBJ_D)
$(CC) -shared $(OBJ_D) -o $(LIB_D)
$(MV) $(OBJ_D) $(OBJ_DIR)
$(OBJ_D): $(SRC_D)
$(CC) -c -fpic $(SRC_D)
$(BIN_FILE): $(OBJ_M)
$(CC) $(OBJ_M) $(LIB_S) $(LIB_D) -o $(BIN_FILE)
$(MV) $(OBJ_M) $(OBJ_DIR)
$(OBJ_M): $(SRC_M)
$(CC) -c $(SRC_M) -I $(INC_DIR) -o $(OBJ_M)
clean:
$(RM) $(OBJ_DIR) $(LIB_DIR) $(BIN_DIR)
$(RM) *.o
其中:
- 執(zhí)行
make clean
命令清除之前的編譯結(jié)果侦锯。 - 執(zhí)行
make
命令進(jìn)行整個(gè)程序的編譯鏈接驼鞭。
結(jié)果測(cè)試:
$ bin/main
最終輸出結(jié)果也和預(yù)想的一致。
至此尺碰,已經(jīng)打通了在Linux下進(jìn)行C語言程序開發(fā)的必要路徑挣棕。
5. 附程序源文件
main.c
#include <stdio.h>
#include "func.h"
void main(void)
{
printf("hello world!\n");
func_s1();
func_s2(1);
func_d1();
func_d2("hello");
return;
}
func.h
void func_s1(void);
void func_s2(int a);
void func_d1(void);
void func_d2(char *s);
func_s1.c
#include <stdio.h>
void func_s1(void)
{
printf("Using the static library.\n");
return;
}
func_s2.c
#include <stdio.h>
void func_s2(int a)
{
printf("Input number is: %d.\n", a);
return;
}
func_d1.c
#include <stdio.h>
void func_d1(void)
{
printf("Using the dynamic library.\n");
return;
}
func_d2.c
#include <stdio.h>
void func_d2(char *s)
{
printf("Input string is: %s.\n", s);
return;
}