GCC、glibc和GNU C的关系

名称 比喻 功能描述
GCC 厨师/翻译机器 把食谱(C 代码)翻译成可执行的菜(机器码程序)
glibc 厨房工具/原料箱 C 语言的标准库提供刀、锅、调料(库函数)供厨师使用,不需要自己去造锅
GNU C 食谱语言版本 厨师用的食谱语言(C 语言的 GNU 版本),可以写标准菜,也可以用特色调料(GNU 扩展)

C语言的编译过程

以hello.c和main.c两个源文件做示范
在这里插入图片描述

预处理

将两个源文件预处理成.i文件(理解成intermediate或者initial)。
展开头文件,展开宏,删除注释
gcc -E hello.c -o hello.i
gcc -E main.c -o main.i

编译

将.i文件编译成.s文件(soure)。
C语言 --> 汇编语言
gcc -S hello.c -o hello.s
gcc -S main.c -o main.s

汇编

将.s文件汇编成.o文件(object)。
汇编语言 --> 机器码
gcc -c hello.s -o hello.o
gcc -c main.s -o main.o

链接

把多个 .o 文件合并,生成可执行文件 main。
把机器码组合成完整程序,可直接运行
gcc main.o hello.o -o main

静态链接

编译时就把所有依赖的库函数的机器码 直接拷贝进可执行文件。
生成的可执行文件 不依赖外部库文件,可以独立运行。

gcc -static hello.o main.o -o main

动态链接

方式1:gcc默认就是动态链接

方式2:我们可以将自己写的那部分源文件对应的.o文件编译成动态库,目的就是为了方便代码复用
将hello.o编译成动态库libhello.so:
gcc -fPIC -shared -o libhello.so hello.o
然后生成可执行文件main:
gcc main.o -L ./ -lhello -o main

Q:-lhello给的是hello,怎么会找到libhello.so呢?
A:按照惯例,-lxxx 会被解释为 查找库文件:libhello.so 或者 libhello.a

小问题:但是直接执行main文件会报错:cannot open shared object file: No such file or directory。是运行时找不到动态库 (.so) 引起的
方法一:设置环境变量 LD_LIBRARY_PATH

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

. 表示当前目录
运行程序时告诉系统去当前目录找动态库

注意:直接LD_LIBRARY_PATH=/home/dante/develop/也是可以的,但是这种写法只对当前命令有效,不像 export 那样修改了环境变量供后续命令使用。这就意味着需要这样写才能执行main文件:LD_LIBRARY_PATH=/home/dante/develop/ ./main
在这里插入图片描述

方法二:在编译时指定 rpath

gcc main.o -L. -lhello -Wl,-rpath=. -o main

-Wl,-rpath=. 告诉可执行文件 运行时去当前目录找动态库

这样就不需要每次设置环境变量

-L和-Wl,-rpath=.的区别
一个是编译时找库和另一个是运行时找库
在这里插入图片描述

方法三:把动态库复制到系统库路径

sudo cp libhello.so /usr/local/lib/
sudo ldconfig

让操作系统默认能找到库
ldconfig 刷新动态库缓存

混合链接

某些库静态链接,而其他库动态链接。这种方式结合了静态链接和动态链接的优点。

将hello.o编译成静态库libhello.a
ar crv libhello.a hello.o

如果相同目录下同时存在hello的静态库和动态库文件,链接时会默认选择动态链接

利用静态库生成可执行的main文件
gcc main.o -L ./ -lhello -o mian
-lhello表示链接libhello.a文件。注意这里要去掉开头的lib前缀和结尾的.a后缀

问题思考

Q:为什么不能将main.o编译成动态库或者静态库呢,为什么一直在讨论hello.o?

A:main.o 和 hello.o 的角色不同

  • hello.o
    是你自己写的库代码
    可以被多个程序复用(比如 main.c、test.c 都想用 print_hello)
    所以它可以编译成 静态库 .a 或 动态库 .so
    这样做的意义是 代码复用、减少重复编译、灵活升级

  • main.o
    是具体的应用程序入口,包含 main() 函数
    每个可执行程序都有唯一的 main()
    没有其他程序会去调用它,所以 编译成库没有意义

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐