I. Windows平台下C调用动态库的方法:
Case 1:如果提供了.lib和.dll,则不需要动态加载,因为动态库的.lib中含有dll的所有导出符号
(参考:俞甲子等. 《程序员的自我修养》)
1、假设动态库的源文件Math.c包含一个Add函数,被声明为导出函数(可被库外部调用):
extern "C" __declspec(dllexport) double Add(double a, double b)
{return a+b;}
/*
*
* extern 表示这是个全局函数,可以供各个其他的函数调用,其实extern没有意义,因为全局函数本来就可以被外部访问。
* “C” 表示编译时按照 C编译器的方式进行编译,而不是C++。 C++的编译方式考虑了函数重载,所以对函数名进行了新的修饰,产生了所谓的破坏性命名。
/*
编译成动态库:cl /LDd(或者/LD,d代表debug版) Math.c
编译生成了Math.dll, Math.obj, Math.exp, Math.lib四个文件
2、使用DLL
/* Test..c*/
#include<stdio.h>
__declspec(dllimprot) double Add(double, double); //声明这个要用的导入函数
int main(int argc, char **argv)
{
double result = Add(3,2);
printf("Result = %f\n",result);
return 0;
}
编译项目代码:cl /c Test.c
链接动态库: link Test.obj Math.lib
完成生成Test.exe
Case 1:如果仅有.dll,需要显示运行时加载(即LoadLibrary(), GetProcAddress()那一套)
假设还是刚才那个Math.dll,依旧要使用那个定义为导出函数(即__declspec(dllexport))的Add函数
#include<windows.h>
#include<stdio.h>
typedef double (*Func)(double, double);//声明出指向DLL目标函数的函数指针
int main(int argc, char **argv)
{
Func function;
//load DLL
HINSTANCE hinstLib = LoadLibrary("Math.dll");
if (hinstLib == NULL) {printf("Error loading"); return 1;}
// get func address
function = (Func)GetProcAddress(hinstLib, "Add");
if(function == NULL){printf("Error addressing func");FreeLibrary(hinstLib);return 1;}
// call it
int res = function(3.0,3.0);
FreeLibrary(hinstLib);
return 0;
}
II. Linux平台下C调用共享库(.so)的方法:
Case1. 有同时提供.h文件和.so文件
1. 编写foo库
/* foo.h*/
#ifndef foo_h__
#define foo_h__
extern void foo(void);
#endif // foo_h__
/* foo.c*/
#include <stdio.h>
void foo(void)
{
printf("Hello, I'm a shared library");
}
2.制作foo.so库
$gcc -c -Wall -Werror -fpic foo.c -o foo.o
$gcc -shared -o libfoo.so foo.o
3. 代码main.c:
#include <stdio.h>
#include "foo.h" //有.h可用
int main(void)
{
printf("This is a shared library test...");
foo();
return 0;
}
4. 在编译时链接动态库:
$gcc -L/home/username/foo -Wall -o test main.c -lfoo
5. 执行
$export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH #添加动态库搜寻路径,因为test的执行需要依赖foo库
$./test
Case2. 仅有.so文件,可以运行时动态加载
#include<stdio.h>
#include<dlfcn.h>
int main(int argc, char* argv[])
{
void *handle;
double (*func)(double); //声明出要调用的库中函数
char *error;
// open .so
handle = dlopen("libfoo.so",RTLD_NOW);
if(handle == NULL){printf("Error loading .so");return -1;}
// address the func foo
func = dlsym(handle, "foo");
if( (error=dlerror()) !=NULL){printf("Error addressing func"); dlclose(handle); return -1;}
//call it
foo();
return 0;
}
编译:$gcc -o test test.c -ldl #-ldl代表链接用于支持动态加载库的动态链接库(DL库)
执行:./test
#可以看出运行时加载的方式 不需要 在编译、执行时指明要用的库(共享对象),只需链接DL库并包含dlfcn.h,用于支持dlopen, dlsym这一套机制。
只有.so,也可以在链接阶段链接.so,本文件中通过声明外部函数来访问
代码文件:
代码main.c:
#include <stdio.h>
//无.h可用
//声明外部函数
extern void foo(void);
int main(void)
{
printf("This is a shared library test...");
foo();
return 0;
}
$gcc test.c libfoo.so -o main Xlinker -rpath ./ #后面几个参数为链接器指明.so的查找路径