LINUX C(静态库与动态库的制作、环境变量、错误处理)
静态库,动态库的制作和使用,环境变量的使用,错误处理,linux c
用nm命令二进制文件中的符号表
T代表有次函数的定义
U代表无此函数的定义
比如里面half_search这个函数我们在文件内部定义了,它前面会是T,但我们常用的printf为什么是U呢,这就是静态链接和动态链接的区别了
链接分为静态链接和动态链接
静态链接:发生在生成可执行文件的时候
动态链接:发生在可执行文件加载到内存的时候
那么静态链接静态库,动态链接动态库
库文件的命名规则:
静态库:lib库名.a
动态库:lib库名.so
静态库的制作使用步骤
①将要加入静态库的源文件编译为目标文件.o(gcc -c *.c)注:不包含主函数的.c文件
②将第一步生成的目标文件打包到静态库文件(ar -r lib库名.a *.o)
③使用静态库链接生成可执行文件
gcc -c main.c
gcc main.o -L路径 -l库名(L后跟路径,l后跟库名)
使用静态库没有用到的模块不会链接
环境变量
每个进程都维护这一个环境变量列表
进程执行的时候要访问计算机的资源,不同计算机资源放置的位置不同,为了适用于各机器,设置一个变量,让这个变量的值指定计算机资源,即环境变量
进程关系有:父子关系(一个进程下创建的另一个进程)和兄弟关系(一个进程下创建的两个进程之间),可以用pstree来查看进程树
env可以查看当前bash的环境变量
注:环境变量名和值的等号两边不允许有空格
如何提取环境变量呢
两种方法:
①$name(name是环境变量名字)-----这是提取变量的值
echo $name 即可将其输入到显示器,这种方法不仅能输出环境变量也能输出自定义变量
②env|grep name 即可将环境变量输出到显示器,这种方法只能输出环境变量
自定义变量就是我们自己设置的变量
比如我们直接在终端输入caption=beijing,会发现用第一种方法能查找到,但第二种方法查找不到,这说明它并不是环境变量,而是自定义变量
那么我们如何把它设置成环境变量呢,只需要在这个式子前面加上export,再用第二种方式就能够看见了
删除环境变量使用 unset 变量即可
环境变量可以被子进程继承,但是自定义进程不可以
bash进程的配置文件~/.bashrc
bash启动后会自动执行这个脚本文件,写在这里面的环境变量或自定义变量每个bash打开时都会存在,但不建议轻易的改动它,容易导致bash崩掉
PATH环境变量:
显示了一个冒号分割的多个路径,这是查找指令的路径,比如我们用到的ls,它的可执行文件就在这些路径里找,比如我们写的可执行文件a.out,执行时使用./a.out,我们给了他路径在当前目录下,如果我们把“ . ”加到这个路径下,直接用a.out即可
export PATH=$PATH:.即可实现
或者也可以将我们的可执行文件移动到PATH中的这些路径下
比如sudo mv 二进制文件名 /usr/local/bin
动态库的制作和使用步骤
①将要加入动态库的的源文件编译为与位置无关的目标文件(-fPIC 生成与位置无关的目标文件)
gcc -c -fPIC *.c(注:不包含主函数.c文件)
②将第一步生成的与位置无关的目标文件打包到动态库文件
gcc -shared -o lib动态库名.so *.o
③使用动态链接生成可执行文件
gcc main.c -L. -l动态库名
此时执行a.out会出错,找不到动态库,为什么呢?
用ldd 可执行文件查看一下它依赖哪个动态库
会发现 lib库名.so=>not found//找不到动态库
libc.so.6是标c库
因为动态库是动态链接,a.out本身并没有链接库中使用的模块,只有在a.out执行时才会链接动态库中使用的模块,因此我们需要告知a.out去哪里链接动态库
有两种方式可以告知动态库所在的位置
①使用环境变量告知
LD_LIBRARY_PATH这个环境变量为动态库提供路径,只需在后面加上当前动态库路径即可,但是换个终端的话就不可以了,当然也可以写在~/.bashrc下
②通过头文件的方式告知
动态链接器的默认路径是/lib或者usr/lib
把动态库文件移动到这个路径下即可
然后让main.c包含其头文件即可
例如我现在去制作一个动态库:
使用上述制作动态库的步骤生成如下名称的动态库
写一个主函数main.c
这里的头文件t_math.h也可以移动到/usr/local/include中就可以用<t_math.h>中了
把动态库移动到这个路径下,a.out即可正常使用了
动态加载:程序员可以自己选择需要的动态库加载进来
动态加载函数:dlopen dlerror dlclose dlsym
#include<dlfcn.h>//需要包含此头文件
void *dlopen(const char *filename,int flags);
功能:打开一个共享库
参数:
filename:指定要加载的动态库文件的名字
flags(二选一):
RTLD_NOW:立即把所有用到的库加载进来
RTLD_LAZY: 懒绑定,只有用时才加载进来
返回值:成功返回非空地址
失败返回NULL,错误用dlerror获取
int dlclose(void *handle);
功能:关闭一个共享库
参数:
handle:指定共享库加载地址
返回值:成功返回0
失败返回非零,用dlerror获取
char *dlerror(void);
功能:获取动态连接器的错误信息
返回值:描述错误的首地址,返回空代表无错误产生
void*dlsym(void*handle,const char *symbol);
功能:从共享库中获取symbol符号的地址
参数:
handle:dlopen函数的返回值,指定已加载的动态库
symbol:指定具体符号的名字
返回值:失败返回NULL,dlerror获取
成功返回符号地址
用代码来看一下:
编译时要连接dl库才行(Link with -ldl),比如文件名字叫dyn.c
编译:gcc dyn.c -ldl才行,然后执行可执行文件后跟库的地址即可,如果库放在了动态链接指定的路径下可以只写库名即可
程序中的错误处理
系统中维护着一个全局变量errno,就是错误码
显示系统调用,库函数的大部分错误,每个错误码对应一个错误
需要包含#include<errno.h>
那如何获取错误码所对应的错误?strerror
#include<string.h>
char*strerror(int errnum)
功能:获取描述错误码的字符串参数
参数:
errnum:错误码
返回值:返回一个字符串中描述的错误或无此错误码
例如:
测试结果:
用perror函数即可实现更加简单
#include<stdio.h>
void perror(const char *s);
功能:将用户指定的字符串和错误信息输出到显示器
参数:用户指定的字符串
比如
这两行代码其实可以用perror("fopen");即可
为了方便后续的使用可以将错误处理进行封装,因为每次错误的时候都会返回一个值和错误解释,可以使用宏定义的形式进行错误处理
使用时只需要包含这个头文件
判断fopen是否出错时直接使用E_MSG(fopen,-1);
错误返回-1
更多推荐
所有评论(0)