G++,静态库,动态库

G++(Linux)

-I(大写i),到指定目录搜索头文件
-L,到指定目录搜索库文件
-l(小写L),到前面指定的目录下添加库文件进行链接
-o,为生成文件重命名
   g++ -E xxx.cpp
   不加-o 生成xxx.i
   加-o xxx123 生成xxx123
-E,将源代码 .c/.cpp文件进行预编译
   将所有#define删除
   并且展开所有的宏定义处理所有的条件预编译指令,如#if #ifdef #undef #ifndef #endif #elif处理#include
   将包含的文件插入到此处,这是一个递归的过程
   删除所有注释 // /* */添加行号和文件名标识,以便于编译时产生的错误警告能显示行号
   保留#pragma编译器指令
   g++ -E xxx.cpp 生成xxx.i
-S,将预处理完的.i文件进行一系列的词法分析、语法分析、语义分析及优化后生成响应的汇编代码文件
   g++ -S xxx.cpp/xxx.i 生成xxx.s
-c,生成目标文件 以xxx.o结尾,可以用于生成库文件
   g++ -c xxx.cpp/xxx.s 生成xxx.o

静态库

在g++ -c命令创建完.o结尾的目标文件后,可以通过ar命令打包成静态库
实际上ar命令是用于创建备存文件的,将一些文件打包成一个文件,类似于压缩文件,但不完全等于压缩文件
实际上在调用g++ -l(小写L)指令的时候将备存文件中的.o文件提取出来进行链接操作
但是在链接中需要用到-L来指示库文件所在目录,用-l(小写L)来指定库文件
而-l(小写L)时会补充xxx前后的lib与.a,所以在使用ar命令来打包静态库的时候需要直接命名为libxxx.a
在实际上以下两种链接方法完全等效

$ g++ main.cpp -L./lib -lmath

$ g++ main.cpp math.o

一般对.o结尾的目标文件生成静态库的时候,使用以下参数
ar rcs [备存文件] [成员文件]
(静态库文件名) (目标文件名)
例 ar rcs libmath.a math.o

更多ar命令,请看以下
Linux ar命令

动态库

动态库编译方式如同静态库一样,先要编译出目标文件.o,然后组成.so库—— g++ -c math.cpp -o math.o
但是这次编译需要用到-fPIC指令来编译,在g++ -fPIC -c math.cpp -o math.o
获得了目标文件.o后,需要用指令-shared声明将.o文件链接成.so库,g++ -shared -o libmath.so math.o
实际上生成.so库可以合成一个指令,g++ -fPIC -shared math.cpp -o libmath.so
编译main.cpp时,需要和链接静态库一样,要用到-L与-l(小写L),但是要添加-ldl,g++ math.cpp -L ./so/ -lmath -ldl
关于载入动态库的规则:
在Linux中,系统会按照一定方式搜索动态库文件

  • (仅限ELF)如果调用程序的可执行文件包含 DT_RPATH 标记,并且不包含 DT_RUNPATH 标记,则会搜索 DT_RPATH 标记中列出的目录。
  • 如果在程序启动时,环境变量 LD_LIBRARY_PATH 被定义为包含以冒号分隔的目录列表,则会搜索这些目录。 (作为安全措施,set-user-ID 和 set-group-ID程序将忽略此变量。)
  • (仅限ELF)如果调用程序的可执行文件包含 DT_RUNPATH 标记,则搜索该标记中列出的目录。
  • 检查缓存文件/etc/ld.so.cache(由ldconfig(8)维护)以查看它是否包含filename的条目。
  • 搜索目录 /lib和 /usr/lib(按此顺序)。
    例如创建环境变量 LD_LIBRARY_PATH ,在中间加入动态库的目录,export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./so

在执行调用动态库时,需要对以上规则内的目录进行添加对应动态库,也可以用ldd命令查看可执行文件中调用的动态库是否存在对应的文件,ldd a.out

如何执行动态库中的程序?
一、隐式执行
二、显示执行

一、隐式执行

库文件中的可执行函数都声明为静态(static),int main()中直接调用该静态函数,道理懂得都懂。

二、显示执行

使用函数库中各种库函数,加载动态库,执行动态库中程序etc.
介绍几个常用动态库函数
1.dlopen
  void* dlopen(const char* filename,int mode)
  filename 为动态库名,mode为载入动态库的方式模式。

  mode:
  1、解析方式
  RTLD_LAZY:在dlopen返回前,对于动态库中的未定义的符号不执行解析(只对函数引用有效,对于变量引用总是立即解析)。
  RTLD_NOW: 需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL,错误为:: undefined symbol: xxxx.......
  2、作用范围,可与解析方式通过“|”组合使用。
  RTLD_GLOBAL:动态库中定义的符号可被其后打开的其它库解析。
  RTLD_LOCAL: 与RTLD_GLOBAL作用相反,动态库中定义的符号不能被其后打开的其它库重定位。如果没有指明是RTLD_GLOBAL还是RTLD_LOCAL,则缺省为RTLD_LOCAL。
  3、作用方式
  RTLD_NODELETE: 在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量。这个flag不是POSIX-2001标准。
  RTLD_NOLOAD: 不加载库。可用于测试库是否已加载(dlopen()返回NULL说明未加载,否则说明已加载),也可用于改变已加载库的flag,如:先前加载库的flag为RTLD_LOCAL,用dlopen(RTLD_NOLOAD|RTLD_GLOBAL)后flag将变成RTLD_GLOBAL。这个flag不是POSIX-2001标准。
  RTLD_DEEPBIND:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突。这个flag不是POSIX-2001标准。

  dlopen执行后返回一个handle

2.dlsym
  void* dlsym(void* handle,const char* symbol)
  执行完后返回handle中的对应symbol符号的地址
  其中handle就是dlopen执行完后的handle,symbol就是要求获取的函数或全局变量的名称
  关于symbol,其中有说法的是符号名
  因为在c++中为了保证多态性,函数都能进行重载,所以在c++中的符号名不是完全按照函数名来命名,会按照不同编译器的方式进行改编
  而c中不需要保证多态性,所以符号名都为函数名。
  所以在需要进行c方式编译的代码段,用 extern "C" 来声明这一段代码用C语言来编译,使用dlsym时,便可以通过函数名来调用库中的函数

3.dlclose
  int dlclose (void *handle)
  传入handle,卸载handle所指的动态库内存区域

4.dlerror
  char *dlerror(void)
  返回错误信息

你可能感兴趣的:(G++,静态库,动态库)