C标准库和C++的STL是共享元件的例子,可以被我们的程序所链接。这样的好处是:每个对象文件在链接时不需要被陈述,因为开发者可以批量引用库。这简化了应用之间的元素共享和重复利用。
库命名传统: 通常带有
lib
前缀,所有C标准库都是满足这样的规律。当链接时,命令行引用这个库时将不包含前缀或者后缀;例如,gcc src-file.c -lm -lpthread
这个编译、链接命令,在链接过程中引用的库是数学库m
和线程库pthread
,这两个库可以在/usr/lib/libm.a
和/usr/lib/libpthread.a
找到。注意:GNU编译器现在有命令行-pthread
选项,而老版本的编译器需要通过-lpthread
显式指定线程库。因此,你更有可能看到gcc src-file.c -lm -pthread
。
命令:cc -Wall -c ctest1.c ctest2.c
-Wall:包含警告,可以看到警告的帮助页
libctest.a
库:ar -cvq libctest.a ctest1.o ctest2.o
ar -t libctest.a
注意:当创建库以后,通过命令行链接并生成可执行程序以后,会在可执行程序的archive中创建一个符号表,可执行程序就嵌入了ar
命令,对于MS/Windows开发者来说,.a
库和Visual C++的.lib
库一样。
生成一个共享库方法:(动态链接对象库文件)
gcc -Wall -fPIC -c *.cgcc -shared -W1,-soname,libctest.so.1 -o libctest.so.1.0 *.omv libctest.so.1.o /opt/libln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so.1ln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so
这会创建一个libctest.so.1.o
和一个指向它的符号链接。将链接级联也是合法的:
ln -sf /opt/lib/libctest.so.1.0 /opt/lib/libctest.so.1ln -sf /opt/lib/libctest.so.1 /opt/lib/libctest.so
如果查看/lib和/usr/lib下的所有库,以上两种方法都存在,linux开发者并没有统一。重要的是符号链接最终指向的真实库文件。
-Wall: 包含警告;
-fPIC:编译器输出独立代码位置,共享库需要的一个特点;
-shared:产生一个共享库对象,可以被其他对象链接形成一个可执行程序;
-Wl:可选,传递选项给链接器;在上面的例子里,“-soname libctest.so.1被传递; -o:运算输出,共享对象的名字为
libctest.so.1.0`
库的链接:
-lctest
执行;gcc -Wall -I/path/to/include-flies -L/path/to/libraries prog.c -lctest -o prog
gcc -Wall -L/opt/lib prog.c -lctest -o prog
共享库的名字是libctest.so
,这就是为什么你需要创建符号链接,否则你会得到/usr/bin/ld:cannot find -lctest 错误。这些库不会被包含到可执行程序中,在运行过程中动态链接。
可执行程序依赖的共享库可以通过`ldd name-of-executable列举出来,当库被加载时,共享连接库中未解析错误可能会导致程序运行错误。
例如,
[prompt]$ ldd libname-of-lib.so libglut.so.3 => /usr/lib64/libglut.so.3 (0x00007fb582b74000) libGL.so.1 => /usr/lib64/libGL.so.1 (0x00007fb582857000) libX11.so.6 => /usr/lib64/libX11.so.6 (0x00007fb582518000) libIL.so.1 (0x00007fa0f2c0f000) libcudart.so.4 => not found
前三个库表明存在路径,而后两个存在问题。解决后两个库的依赖:
sodu ldconfig
再加载库缓存为了让可执行程序找到需要的库,并在运行时链接,必须对系统进行一些配置,相关的配置方法有以下:
-f file-name
标识引用其他配置文件。ldconfig -n .
,链接使用-L.
即可。...if [ -d /opt/lib ];then LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATHfi...export LD_LIBRARY_PATH
ar:列举在archive库中的所有对象文件ar tf /usr/lib/x86_64-linux-gnu/libjpeg.a
nm:列举符号,包括对象文件、archive库和共享库nm file.o
#列举对象文件中包含的符号nm /usr/lib/x86_64-linux-gnu/libjpeg.a
#列举包含在archive库中的符号。
使用-D列巨额包含在对象文件或共享库中的符号。
在执行时可以动态加载/卸载这些库。
库的包含文件ctest.h如下所示:使用extern "c"
方便该库可以用在c和c++里。这个语句可以防止链接时因为C++的名字隔离而导致的为解析符号。
#ifndef CTEST_H#define CTEST_H#ifdef __cplusplusextern "C" {#endifvoid ctest1(int *);void ctest2(int *);#ifdef __cplusplus}#endif#endif
动态加载或卸载libctest.so库(progdl.c):
#include <stdio.h>#include <dlfcn.h>#include "ctest.h"int main(int argc, char **argv) { void *lib_handle; double (*fn)(int *); int x; char *error; lib_handle = dlopen("/opt/lib/libctest.so", RTLD_LAZY); if (!lib_handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); } fn = dlsym(lib_handle, "ctest1"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(1); } (*fn)(&x); printf("Valx=%d\n",x); dlclose(lib_handle); return 0;}
编译命令:gcc -rdynamic -o progdl progdl.c -ldl
解释:
当使用c++编译上面的c例子,会发现c++函数名字被修饰而导致不可用,除非函数定义有extern "C"{}
保护。
注意,以下两者不等价:
//1:extern "C" { int functionx();}//2:extern "C" int functionx();
以下两者是等价的:
//1:extern "C" { extern int functionx();}//2:extern "C" int functionx();
动态加载C++类:
动态库加载程序可以使得编程者加载C函数。在C++中,我们想要加载类的成员函数,实际上,整个类可以在库中,我们先要加载并访问整个对象以及它的成员函数。通过传递”C“类工厂函数可以实现。
类的头文件如下所示:
class Abc {......};// Class factory "C" functionstypedef Abc* create_t;typedef void destroy_t(Abc*);
对应的cpp文件如下:
Abc::Abc(){ ...}extern "C"{ // These two "C" functions manage the creation and destruction of the class Abc Abc* create() { return new Abc; } void destroy(Abc* p) { delete p; // Can use a base class or derived class pointer here }}
这个cpp文件是库的源文件,C函数在库中动态加载库中实例化、销毁一个类,Abc就是这个类。
包含Main可执行程序调用这个库的方法如下:
// load the symbols create_t* create_abc = (create_t*) dlsym(lib_handle, "create");...... destroy_t* destroy_abc = (destroy_t*) dlsym(lib_handle, "destroy");......
注意: C++类的new/delete应该都由可执行程序或者库来提供,不应该分开。以便如果在一方出现了new/delete的重载不会导致意外情况。
windows下和linux/Unix共享对象(.so)对应的是.dll,通常windows下是.dll,有时也可能是.ocx。 在旧的16位系统上,动态链接库也可能为.exe。不幸的是,windows下Dll的生成和Microsoft IDE紧密结合,没有IDE基本没有办法自己生成。
windows c++对应的函数:
包含头文件(.h/.hpp):
class Abc{public: static Abc* Instance(); // Function declaration. Could also be used as a public class member function.private: static Abc *mInstance; // Singleton. Use this declaration in C++ class member variable declaration. ...}
对应的cpp文件:
/// Singleton instantiationAbc* Abc::mInstance = 0; // Use this declaration for C++ class member variable // (Defined outside of class definition in ".cpp" file)// Return unique pointer to instance of Abc or create it if it does not exist.// (Unique to both exe and dll)static Abc* Abc::Instance() // Singleton{#ifdef WIN32 // If pointer to instance of Abc exists (true) then return instance pointer else look for // instance pointer in memory mapped pointer. If the instance pointer does not exist in // memory mapped pointer, return a newly created pointer to an instance of Abc. return mInstance ? mInstance : (mInstance = (Abc*) MemoryMappedPointers::getPointer("Abc")) ? mInstance : (mInstance = (Abc*) MemoryMappedPointers::createEntry("Abc",(void*)new Abc));#else // If pointer to instance of Abc exists (true) then return instance pointer // else return a newly created pointer to an instance of Abc. return mInstance ? mInstance : (mInstance = new Abc);#endif}
windows链接器将会链接入两个对象实例,一个在exe,一个在可加载模块内。通过内存映射指针可对两者进行明确,以便exe和可加载库指向同一个变量或者对象。而GNU 链接器不存在这个问题。
交叉平台可加载库的编程:
#ifndef USE_PRECOMPILED_HEADERS#ifdef WIN32#include <direct.h>#include <windows.h>#else#include <sys/types.h>#include <dlfcn.h>#endif#include <iostream>#endif using namespace std;#ifdef WIN32 HINSTANCE lib_handle;#else void *lib_handle;#endif // Where retType is the pointer to a return type of the function // This return type can be int, float, double, etc or a struct or class. typedef retType* func_t; // load the library -------------------------------------------------#ifdef WIN32 string nameOfLibToLoad("C:\opt\lib\libctest.dll"); lib_handle = LoadLibrary(TEXT(nameOfLibToLoad.c_str())); if (!lib_handle) { cerr << "Cannot load library: " << TEXT(nameOfDllToLoad.c_str()) << endl; }#else string nameOfLibToLoad("/opt/lib/libctest.so"); lib_handle = dlopen(nameOfLibToLoad.c_str(), RTLD_LAZY); if (!lib_handle) { cerr << "Cannot load library: " << dlerror() << endl; }#endif......... // load the symbols -------------------------------------------------#ifdef WIN32 func_t* fn_handle = (func_t*) GetProcAddress(lib_handle, "superfunctionx"); if (!fn_handle) { cerr << "Cannot load symbol superfunctionx: " << GetLastError() << endl; }#else // reset errors dlerror(); // load the symbols (handle to function "superfunctionx") func_t* fn_handle= (func_t*) dlsym(lib_handle, "superfunctionx"); const char* dlsym_error = dlerror(); if (dlsym_error) { cerr << "Cannot load symbol superfunctionx: " << dlsym_error << endl; }#endif......... // unload the library -----------------------------------------------#ifdef WIN32 FreeLibrary(lib_handle);#else dlclose(lib_handle);#endif
联系客服