从源代码到可执行程序
四步:预处理 编译 汇编 链接
预处理:
1、宏定义展开
2、预编译命令的处理如#if #ifdef一类的命令;
3、展开#include引用的头文件,如:#include"stdio.h",会把sdio.h中所有代码合并到main.c
中
4、去掉注释
gcc -E main.c -o main.i
编译:
将c++代码翻译成汇编代码,把预处理完的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码
gcc -S main.i -o main.s // main.s也是文本文件
汇编:
将汇编文件翻译成机器码,这一步产生的文件叫做目标文件,是二进制格式
as main.s -o main.o
链接:
将多个目标文件与外部符号链接得到可执行的二进制文件
gcc main.o -o main
查看符号表的几种方式:
nm 目标文件
objdump -t 目标文件
readelf -s 目标文件
strings 目标文件
动态库和静态库
静态链接:
在编译阶段直接把静态库加入到可执行文件中去
gcc -c func.c
ar crsv libfunc.a func.o
使用静态库
gcc -o main main.c -lfunc -L./
动态链接:
是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统中把相应的动态库加载到内存中去
gcc -fPIC -c func.c
gcc -shared -o libfunc.so func.o // func可能会使用到其他库,指定-shared,使得优先链接动态库
使用动态库
例:比如main.c用到了func
gcc -o main main.c -lfunc -L./ // -l指定要链接的库,-L指定链接库所在的路径
export LD_LIBRARY_PATH=./
./main
注意:执行时需要指定搜索连接库的路径,可执行程序运行时会先后搜索环境变量LD_LIBRARY_PATH设置的路径-搜索/etc/ld.so.cache文件列表-搜索/lib和/usr/lib目录,找到需要使用的库文件载入内存。
1、搜索环境变量LD_LIBRARY_PATH设置的路径 – 可以通过export LD_LIBRARY_PATH设置库文件路径
2、搜索/etc/ld.so.cache文件列表 – 将库所在的路径方式添加到/etc/ld.so.conf中,然后执行ldconfig即可更新ld.so.cache
3、搜索/lib和/usr/lib目录 – 将库文件拷贝到该目录下
静态库和动态库的区别优劣:
1、gcc/g++在链接时既有动态库又有静态库,会优先选择动态库,可以通过-static优先链接静态库
2、可执行文件大小不一样 - - - 静态链接的可执行文件要比动态链接的可执行文件要大得多
3、占用磁盘大小不一样 - - - 静态库因为会被拷贝N份到各自的可执行程序中,而动态库各个可执行程序可以共享一份,因此使用静态库占用的磁盘空间相对比动态库要大。
4、依赖不一样 - - - 静态链接在链接时将静态库加入到了可执行程序中,因此静态链接后生成的可执行程序运行时不依赖静态库,删除静态库依然可以执行。
5、扩展性与兼容性不一样 - - - 如果静态库中某个函数的实现变了,那么可执行文件必须重新编译,而对于动态链接生成的可执行文件,只需要更新动态库本身即可,不需要重新编译可执行文件。正因如此,使用动态库的程序方便升级和部署。
6、加载速度不一样 - - - 由于静态库在链接时就和可执行文件在一块了,而动态库在加载或者运行时才链接,因此,对于同样的程序,静态链接的要比动态链接加载更快。所以选择静态库还是动态库是空间和时间的考量。但是通常来说,牺牲这点性能来换取程序在空间上的节省和部署的灵活性时值得的。
参考资料:
https://zhuanlan.zhihu.com/p/71372182
https://www.cnblogs.com/skynet/p/3372855.html