编译链接和库
GCC
- gcc options
- -ON 优化等级为N 范围0~3
- -IDIR 头文件目录
- -LDIR 链接文件的目录
- -lFILE 链接文件
- -w 禁止所有警告
- -Wall 显示所有警告
- -g 添加调试信息
简单C程序
c code
#include <stdio.h>
int main(int argc, char **argv)
{
printf("hello, gcc & make!\n");
return 0;
}
过程
预处理
gcc -E -o main.i main.c
1. 展开所有宏定义
2. 处理条件预编译指令
3. 插入#include
执行的头文件代码
4. 添加行号和文件标识, 以便编译错误和警告的提示.
5. 删除所有注释
6. 添加其他信息
编译
gcc -S -o main.S main.i
对预处理完的文件test.i
进行词法分析, 语法分析, 语义分析以及优化后生成相应的汇编代码.
汇编
gcc -c -o main.o main.S
将汇编代码转变成二进制机器指令(根据汇编指令和机器指令对照表逐条翻译), 最后产生目标文件main.o
.
链接
gcc -o main main.o
使用链接器将程序的目标文件与所需的所有附加的目标文件连接起来, 最终生成可执行文件, 附加的目标文件包括静态链接库和动态链接库.
GDB
编译时使用gcc -g选项才能使用gdb调试
- gdb BINARY_FILE 以下为交互式命令
- l 显示源代码
- r 运行
- c 继续
- b LINE 在指定行LINE设置断点
- bt 显示调用栈
- s 单步执行(进入函数)
- n 单步执行(不进入函数)
- p VAR 打印变量VAR的值
Makefile
自动化编译链接文件
- make [LABEL] 在当前目录下寻找 makefile/Makefile/GNUmakefile 文件, 执行文件中自动化编译链接命令.
- -f FILE 指定makefile文件名
makefile语句
目标文件: 依赖文件…
命令…
makefile变量
声明: VAR_NAME = VAR…
使用: $(VAR_NAME)
makefile特殊变量
$@
: 目标文件
$^
: 被依赖的所有文件
$<
: 被依赖的第一个文件
约定俗成的目标命令
all: 编译所有目标
clean: 删除make创建的文件
install: 安装已编译好的程序
print: 列出改变过的源文件
tar: 打包备份
dist: 创建一个压缩文件
TAGS: 更新所有目标
防止和文件名冲突可以使用伪命令.PHONY
简单makefile编写
makefile code
CC = gcc
SRC = main.c
BIN = main
$(BIN): $(SRC)
$(CC) -o $@ $^
.PHONY: clean
clean:
rm $(BIN)
库
静态链接
优点: 代码装载速度快, 执行速度比动态链接库略快, 不需要外部函数库支持
缺点: 文件体积大, 代码冗余造成内存浪费, 静态函数库发生改变时程序必须重新编译.
动态链接
优点: 文件体积小, 节省内存空间, 升级方便, 提高软件可维护性和可扩展性.
缺点: 程序变成多个部分, 更容易出错.
要制作成库的函数编写
fun.c
c code
int fun(int a, int b)
{
return a*a + b*b;
}
main.c
c code
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int a = atoi(argv[1]);
int b = atoi(argv[2]);
int ans = fun(a, b);
printf("%d\n", ans);
return 0;
}
静态库
制作静态库
ar -crs libfun.a fun.o
makefile code
CC = gcc
AR = ar
SRC_FUN = fun.c
OBJ_FUN = fun.o
LIB_FUN = libfun.a
SRC = main.c
BIN = main
all: $(OBJ_FUN) $(LIB_FUN) $(BIN)
$(OBJ_FUN): $(SRC_FUN)
$(CC) -c -o $@ $^
$(LIB_FUN): $(OBJ_FUN)
$(AR) -crs $@ $^
$(BIN): $(SRC)
$(CC) -o $@ $^ -L. -lfun
.PHONY: clean
clean:
rm $(BIN)
rm $(OBJ_FUN)
rm $(LIB_FUN)
动态库
制作动态库
gcc -c -fpic -o fun.o fun.c
gcc -shared -o libfun.so fun.o
makefile code
CC = gcc
SRC_FUN = fun.c
OBJ_FUN = fun.o
LIB_FUN = libfun.so
SRC = main.c
BIN = main
all: $(OBJ_FUN) $(LIB_FUN) $(BIN)
$(OBJ_FUN): $(SRC_FUN)
$(CC) -c -fpic -o $@ $^
$(LIB_FUN): $(OBJ_FUN)
$(CC) -shared -o $@ $^
$(BIN): $(SRC)
$(CC) -o $@ $^ -L. -lfun -Wl,-rpath=.
.PHONY: clean
clean:
rm $(BIN)
rm $(OBJ_FUN)
rm $(LIB_FUN)
动态库的链接方式还可以使用库环境变量, 或者直接把库放在系统的库目录中.
c语言运行时动态加载动态库
需要链接dl库
gcc -o main main.c -ldl
执行main程序
./main 3 4 ./libfun.so
c code
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv)
{
void *lib_handle;
int (*fn)();
char *err;
lib_handle = dlopen(argv[3], RTLD_LAZY);
if (!lib_handle) {
fprintf(stderr, "%s\n", dlerror());
exit(-1);
}
fn = dlsym(lib_handle, "fun");
err = dlerror();
if (err) {
fprintf(stderr, "%s\n", err);
exit(-2);
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
int ans = fn(a, b);
printf("%d\n", ans);
dlclose(lib_handle);
return 0;
}
使用ldd
命令查看二进制程序所依赖的动态链接库