用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

Logo

更多推荐