【LinuxC】gcc编译流程
A:按照惯例,-lxxx 会被解释为 查找库文件:libhello.so 或者 libhello.a。方式2:我们可以将自己写的那部分源文件对应的.o文件编译成动态库,目的就是为了方便代码。Q:为什么不能将main.o编译成动态库或者静态库呢,为什么一直在讨论hello.o?Q:-lhello给的是hello,怎么会找到libhello.so呢?把多个 .o 文件合并,生成可执行文件 main。所
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.aar 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()
没有其他程序会去调用它,所以 编译成库没有意义
更多推荐
所有评论(0)