1.2 链接方式与函数库
1.动态链接与静态链接
我们在写代码的过程中,会经常用到库函数,类似printf,scanf,strlen等函数,这些函数在我们的代码中只是调用了它们,并没有实现,那么是谁实现的呢?答案是库函数,是别人预先写好的
同时,程序在预处理、编译和汇编阶段处理的都是我们自己编写的代码,只有在链接的时候,库函数的实现才会和我们的代码关联起来 (符号表的重定位);所以,链接的本质是我们在调用库函数时如何与标准库相关联的问题
程序的链接方式一共有两种:动态链接与静态链接
- 动态链接是指执行代码时,如果遇到库函数调用就跳转到动态库中对应函数的定义处,然后执行该函数,执行完毕后再跳转回原程序并继续往下执行;它的优点是形成的可执行程序小,缺点是受到动态库变动 (删除、升级等) 的影响
- 静态链接则是直接将本程序内部要使用的库函数从对应的静态库中拷贝一份过来;它的优点是不与静态库产生关联,即不受静态库变动 (删除、升级等) 的影响;缺点是形成的可执行程序非常大。
2.动态库与静态库
函数库是一些事先写好的,用于给别人复用的函数的集合,函数库一般分为静态库和动态库两种
静态库是指在编译链接时,把包含的库文件全部拷贝到可执行文件中,然后在运行时就不再需要库文件了,但是由于拷贝了全部内容,所以生成的文件会很大。静态库在Linux下的后缀名是.a,在Windows下后缀名是.lib
动态库:也叫共享库,与静态库相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为.so,在Windows下后缀名是.dll
那么,我们验证一下,我们在Linux下编译是怎么链接的
验证方法:使用file指令可以看到调用的库是动态库还是静态库
我们可以看到,gcc默认的链接方式是动态链接,那么怎么让它使用静态链接呢?只需要在编译指令后面加上-static
这个时候,我们查看一下两个文件的详细信息
可以看到,使用静态链接产生的可执行文件,大小比动态链接产生的文件大得多。
这里补充一点非常重要的事情:一定不要删除系统中的C动态库,因为Linux系统中的基本上所有指令都是使用C语言写的,如果没有C动态库,会导致很多指令都无法使用,最终的解决方案只能是重装系统。
1.3 gcc与g++的使用
gcc 选项
- -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 文件输出到 文件
- -static 此选项对生成的文件采用静态链接
- -g 生成调试信息。GNU 调试器可利用该信息。
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
- -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
- -w 不生成任何警告信息。
- -Wall 生成所有警告信息
我们在使用静态链接的方式编译的时候,可能会发现报错,因为有部分Linux机器没有安装C静态库,所以需要我们手动安装
# 手动安装C静态库 sudo yum install -y glibc-static
同时,部分Linux机器也是没有安装g++的,也需要我们手动安装
# 安装g++ sudo yum install -y gcc-c++
# 安装c++静态库 sudo yum install -y libstdc++-static
2.调试器gdb
2.1debug和release
debug和release是程序编译的类型版本,debug是调试版本,其中包含了程序的调试信息,release是程序的发布版本,其中没有调试信息,并且进行了部分优化(例如对死循环的优化)。
可以看到,debug版本比release版本要大一点,其中多的内容就是调试信息,所以gdb调试必须要在debug模式下调试,如果是release版本下不可执行调试
会显示no debugging symbols found(没有找到调试标志)
Linux下gcc/g++编译出来的程序默认是release版本
到这里我们总结一下之前所学到的关于Linux下的一些默认行为
- gcc/g++的默认行为
- 默认连接方式是动态连接(静态链接需要加-static)
- 默认编译版本是release(编译debug版本需要加-g)
- vim的默认行为
- 打开后的默认模式是命令模式
2.2gdb的安装
sudo yum install -y gdb
2.3gdb的使用
- 第一步:使用-g指令编译源代码,产生debug版本的可执行程序
- 第二步:执行
gdb FileName
进入调试- 第三步:输入调试指令进行调试即可
- 第四步:
ctrl+d
或者q/quit
退出调试