-
2019-09-28 21:21:41
C语言动态链接库
1. 概述
在实际编程中,我们可以把完成某项功能的函数放在一个动态链接库里,然后提供给其他程序调用。
1.1 静态库和动态库
- 静态库:这类库的名字一般是libxxx.a,在使用静态库的情况下,在编译链接可执行文件时,链接器从静态库中复制这些函数和数据,并把它们和应用程序的其他模块组合起来创建最终的可执行文件(.exe)。当发布产品时,只需要发布这个可执行文件,并不需要发布被使用的静态库。
- 动态库:是一种不可执行的二进制程序文件,它允许程序共享执行特殊任务所必需的代码和其他资源。Windows平台上动态链接库的后缀名是”.dll”,Linux平台上的后缀名是“.so”。Linux上动态库一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。
1.2 动态链接库的优点
- 复用性:DLL的编制与具体的编程语言以及编译器无关,不同语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数
- 可扩展性:DLL文件与EXE文件独立,只要接口不变,升级程序只需更新DLL文件不需要重新编译应用程序
- 节省内存:如果多个应用程序使用同一个dll,该dll的页面只需要存入内存一次,所有的应用程序都可以共享它的页面,从而节省内存
2. 生成动态链接库
2.1 windows版本
下面以codeblocks编译器为例,其他编译器也就是建立dll工程不一样:
File->New->Projects->Dynamic Link library->Go
项目的命名就是最后dll的名字,新建main.c和main.h
main.c#include "main.h" #include <stdio.h> /* 输入年月日计算一年中第几天 */ int Day_of_year(int year, int month, int day) { int sum,leap; switch(month) // 先计算某月以前月份的总天数 { case 1:sum=0;break; case 2:sum=31;break; case 3:sum=59;break; case 4:sum=90;break; case 5:sum=120;break; case 6:sum=151;break; case 7:sum=181;break; case 8:sum=212;break; case 9:sum=243;break; case 10:sum=273;break; case 11:sum=304;break; case 12:sum=334;break; default:printf("data error");break; } sum=sum+day; // 再加上某天的天数 if(year%400==0||(year%4==0&&year%100!=0)) {// 判断是不是闰年 leap=1; } else { leap=0; } if(leap==1&&month>2) { // *如果是闰年且月份大于2,总天数应该加一天 sum++; } return sum; }
main.h
#ifndef __MAIN_H__ #define __MAIN_H__ #include <windows.h> #ifdef __cplusplus #define EXPORT extern "C" __declspec (dllexport) #else #define EXPORT __declspec (dllexport) #endif // __cplusplus EXPORT int Day_of_year(int year, int month, int day); #endif // __MAIN_H__
编译成功后在bin\Debug目录下生成3个文件:dll.dll,libdll.a,libdll.def
2.2 Linux版本
main.c
#include "main.h" #include <stdio.h> /* 输入年月日计算一年中第几天 */ int Day_of_year(int year, int month, int day) { int sum,leap; switch(month) // 先计算某月以前月份的总天数 { case 1:sum=0;break; case 2:sum=31;break; case 3:sum=59;break; case 4:sum=90;break; case 5:sum=120;break; case 6:sum=151;break; case 7:sum=181;break; case 8:sum=212;break; case 9:sum=243;break; case 10:sum=273;break; case 11:sum=304;break; case 12:sum=334;break; default:printf("data error");break; } sum=sum+day; // 再加上某天的天数 if(year%400==0||(year%4==0&&year%100!=0)) {// 判断是不是闰年 leap=1; } else { leap=0; } if(leap==1&&month>2) { // *如果是闰年且月份大于2,总天数应该加一天 sum++; } return sum; }
main.h
#ifndef __MAIN_H__ #define __MAIN_H__ int Day_of_year(int year, int month, int day); #endif
在命令行下输入 gcc -shared -fPIC main.c -o libday.so
即可生成一个名为libday.so的动态链接库3. 调用动态链接库
3.1 windows版本
3.1.1 隐式调用
新建工程,把上面生成的dll.dll和libdll.a(不可缺)拷贝到新工程的bin\Debug目录下
main.c#include <stdio.h> #include "main.h" int main() { printf("day = %d\n", Day_of_year(2015,10,1) ); system("pause"); return 0; }
main.h 保持一样
#ifndef __MAIN_H__ #define __MAIN_H__ #include <windows.h> #ifdef __cplusplus #define EXPORT extern "C" __declspec (dllexport) #else #define EXPORT __declspec (dllexport) #endif // __cplusplus EXPORT int Day_of_year(int year, int month, int day); #endif // __MAIN_H__
Project - Build options - Linker settings - Add 选择 bin\Debug\libdll.a - 确定,然后再编译即可
3.1.2 显示调用(推荐)
新建工程,把dll.dll拷贝到新工程的bin\Debug目录下
main.c#include <stdio.h> #include <windows.h> typedef int(*Getday)(int, int, int); //定义函数类型 HINSTANCE hDll; //DLL句柄 Getday getday; int main() { hDll = LoadLibrary("dll.dll"); //加载 dll getday = (Getday)GetProcAddress(hDll, "Day_of_year");//通过指针获取函数方法 printf("day = %d\n", getday(2015, 10, 1) );//调用函数 FreeLibrary(hDll);//释放Dll句柄 system("pause"); return 0; }
编译就可以使用,当dll程序升级时,只需要替换dll,而不用重新编译exe
3.2 Linux版本
linux版本动态链接库没有windows版本那么多文件,只有一个so文件
3.2.1 隐式调用
test.c
#include <stdio.h> #include "main.h" int main() { printf("day = %d\n", Day_of_year(2015, 10, 1)); }
在命令行输入 gcc -o test test.c -L./ libday.so
然后执行编译生成的可执行文件 ./test
注意加上导出函数头文件main.h,-L指定动态链接库的搜索路径3.2.2 显式调用(推荐)
test.c
#include <stdio.h> #include <dlfcn.h> // 显式加载需要用到的头文件 int main() { void *pdlHandle = dlopen("./libday.so", RTLD_LAZY); // RTLD_LAZY 延迟加载 char *pszErr = dlerror(); if( !pdlHandle || pszErr ) { printf("Load lib.so failed!\n"); return 1; } int (*Day_num)() = dlsym(pdlHandle, "Day_of_year"); // 定位动态链接库中的函数 if( !Day_num ) { pszErr = dlerror(); printf("Find symbol failed!%s\n", pszErr); dlclose(pdlHandle); return 1; } printf("day = %d\n", Day_num(2015, 10, 1)); // 调用动态链接库中的函数 dlclose(pdlHandle); // 系统动态链接库引用数减1 return 0; }
在命令行输入 gcc -o test test.c -ldl
然后执行编译生成的可执行文件 ./test
优点:不必在编译时就确定要加载哪个动态链接库,可以在运行时再确定。3.2.3 调试案例
1)错误一
/tmp/ccMpgzNu.o: In function `main': test.c:(.text+0x13): undefined reference to `dlopen' test.c:(.text+0x1c): undefined reference to `dlerror' test.c:(.text+0x53): undefined reference to `dlsym' test.c:(.text+0x63): undefined reference to `dlerror' test.c:(.text+0x89): undefined reference to `dlclose' test.c:(.text+0xc7): undefined reference to `dlclose' collect2: error: ld returned 1 exit status
解决方案:
- 头文件添加:#include <dlfcn.h>
- 编译选项加上- ldl,即 gcc -o test test.c -ldl ,网上有gcc -ldl -o test test.c,这种方式也是会报这个错误的
2)错误二
error while loading shared libraries: libtiger.so: cannot open shared object file: No such file or direct
我的这段代码里则会打印Load lib.so failed!
解决方案:
- 在程序代码里配置路径void *pdlHandle = dlopen(“libday.so”, RTLD_LAZY);
将动态链接库配上路径,如 ./libday.so表示可执行文件与链接库同一路径 - 将动态链接库的目录放到程序搜索路径中,可以将库的路径加到环境变量
export LD_LIBRARY_PATH=pwd
:$LD_LIBRARY_PATH(pwd带反撇号的哈) - 拷贝libday.so到绝对目录 /lib 下(但是要超级用户才可以,因此要使用sudo哦)
更多相关内容 -
C++ 动态链接库
2021-06-01 09:01:192>静态加载方式(.h .lib .dll三件套加载) 1>认识DLL(动态链接库) 动态链接库DLL(Dynamic-Link Library)通常包含程序员自定义的变量和函数, 可以在运行时动态链接到可执行文件(我们的exe程序)中。 2 >格式后缀 ...原创文章,转载请注明出处。
如果Windows系统有缺失的DLL文件,可以去这个链接下载。点击去这里寻找
C++ DLL简介以及使用
1>认识DLL(动态链接库)
动态链接库DLL(Dynamic-Link Library)通常包含程序员自定义的变量和函数, 可以在运行时动态链接到可执行文件(我们的exe程序)中。
2 >格式后缀
Windows上:(.dll)
Linux上:(.so)
Android上:(.so)
IOS上:(.dylib)3 >DLL优点
1)模块化,耦合小:大规模软件开发中,开发过程独立,耦合度小,比如UE4里面的模块(每一个.build.cs)都是一个DLL。
2)扩展性:DLL文件与EXE文件是独立的,只要接口不变,升级程序只需更新DLL文件不需要重新编译应用程序。并且我们的EXE文件较小。
3)复用性:DLL的编制与具体的编程语言以及编译器无关,不同语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数。
4)节省内存:当应用程序使用动态链接时,多个应用程序可以共享磁盘上单个DLL副本。
5)隐私性:可以当做黑盒使用,可以将我们的具体实现代码隐藏起来,比如我们想将算法的具体实现隐藏起来,不让别人看到我是怎么做的。一般SDK接入时候,里面的具体算法实现都是不会公开cpp的。
4 >DLL缺点
如果是动态loaddll的话,会牺牲部分性能吧。微乎其微的。
5 >加载DLL
1>动态加载(运行时加载)通过LoadLibary加载, GetProcAddress调用。
如果加载失败,通过GetLastError()获取失败原因。
下面是LoadLibary的示例代码
第一步,在.h中声明了static HMODULE hDLL;#include<Windows.h> //加载的头文件 class QIDCardReader : public QMainWindow { Q_OBJECT public: QIDCardReader(QWidget* parent = 0); private: static HMODULE hDLL; };
第二步,定义要调用的dll中的方法。并通过LoadLibrary给hDLL赋值
#include "QIDCardReader.h" //CPP中预声明一下我要调用dll里面的方法 typedef int(*MyRouton_RepeatRead)(bool); typedef int(*MyInitComm)(int); typedef int(*MyAuthenticate)(); typedef int(*MyReadBaseInfosPhoto)(char * , char * , char * , char *, char * , char * , char *, char * , char* , char * ); typedef int(*MyCloseComm)(); HMODULE QIDCardReader::hDLL; QIDCardReader::QIDCardReader(QWidget* parent) : QMainWindow(parent) ,ui_(new Ui::QIDCardReaderClass) { hDLL = LoadLibrary(L"D:\\bimvr-vrlauncher\\test\\Sdtapi.dll"); //加载dll文件 //如果加载失败的话, 通过GetLastError()进行获取,看失败原因。 auto code5 = GetLastError(); }
第三步,调用dll中的方法 GetProcAddress(hDLL, “实际的dll端函数导出名称”)
//调用1 if (hDLL) { MyRouton_RepeatRead MyRouton_RepeatReadFunc = (MyRouton_RepeatRead)GetProcAddress(hDLL, ("Routon_RepeatRead"));//直接使用原工程函数名 if (MyRouton_RepeatReadFunc) { MyRouton_RepeatReadFunc(true); } } //调用2 int iPort = 1001; //int ret = InitComm(iPort); int ret = -1000; if (hDLL) { MyInitComm MyInitCommFunc = (MyInitComm)GetProcAddress(hDLL, ("InitComm"));//直接使用原工程函数名 if (MyInitCommFunc) { ret = MyInitCommFunc(iPort); } } if (ret) { //ret = Authenticate(); if (hDLL) { MyAuthenticate MyAuthenticateFunc = (MyAuthenticate)GetProcAddress(hDLL, ("Authenticate"));//直接使用原工程函数名 if (MyAuthenticateFunc) { ret = MyAuthenticateFunc(); } } if (ret) { char user_name[31] = { 0 }; char user_gender[3] = { 0 }; char user_folk[11] = { 0 }; char user_birthday[9] = { 0 }; char user_code[19] = { 0 }; char user_address[71] = { 0 }; char user_agency[31] = { 0 }; char expire_start[9] = { 0 }; char expire_end[9] = { 0 }; QByteArray data_path = QString::fromStdWString(AppUtils::GetDataPath()).toLatin1(); ret = ReadBaseInfosPhoto(user_name, user_gender, user_folk, user_birthday, user_code, user_address, user_agency, expire_start, expire_end, str/*data_path.data()*/); if (hDLL) { MyReadBaseInfosPhoto MyReadBaseInfosPhotoFunc = (MyReadBaseInfosPhoto)GetProcAddress(hDLL, ("ReadBaseInfosPhoto"));//直接使用原工程函数名 if (MyReadBaseInfosPhotoFunc) { ret = MyReadBaseInfosPhotoFunc(user_name, user_gender, user_folk, user_birthday, user_code, user_address, user_agency, expire_start, expire_end, str/*data_path.data()*/); } } } }
第四步,可以选择性调用FreeLibrary(hDLL)卸载Dll,这个是下面的静态三件套加载方式做不到的。
也就是说我们可以动态控制Dll的加载与卸载。2>静态加载方式(.h .lib .dll三件套加载)
1>项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件Sdtapi.h所在的目录
2>项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件Sdtapi.lib所在的目录
3>项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“Sdtapi.lib”(若有多个 lib 则以空格隔开)
也可以使用#pragma comment(lib, “Sdtapi.lib”)代替上面的1、2、3步骤。
QIDCardReader.cpp 包含.h,将.h添加到项目中#include "sdtapi.h" void QIDCardReader::onNotify() { Routon_RepeatRead(true); int iPort = 1001; int ret = InitComm(iPort); if (ret) { ret = Authenticate(); if (ret) { char user_name[31] = {0}; char user_gender[3] = {0}; char user_folk[11] = {0}; char user_birthday[9] = {0}; char user_code[19] = {0}; char user_address[71] = {0}; char user_agency[31] = {0}; char expire_start[9] = {0}; char expire_end[9] = {0}; QByteArray data_path = QString::fromStdWString(AppUtils::GetDataPath()).toLatin1(); ret = ReadBaseInfosPhoto(user_name, user_gender, user_folk, user_birthday, user_code, user_address, user_agency, expire_start, expire_end, data_path.data()); } } }
现在是可以通过编译了,但是运行会报错 提示LinkError
因为程序分为编译和链接两步骤,这就是为什么报错误的原因。
我们解决这个报错就是将.dll放到和.exe同级目录就好了如果最终exe找不到dll的话,会提示下面的错误。解决方法就是将dll放到和exe同级目录就好了。
谢谢,创作不易,大侠请留步… 动起可爱的双手,来个赞再走呗 <( ̄︶ ̄)> -
C++ 动态链接库和静态链接库
2019-09-23 15:59:58动态静态的区别静态链接库静态链接库的创建方法lib的调用动态链接库动态链接库的创建方法dll的使用隐式链接显式链接 源程序编译链接生成文件格式 首先贴出从源程序生成可执行文件的过程。 源程序(sour...今天对C++生成动态链接路和静态链接库非常感兴趣,必须搞定,否则都没有心情干其他事了。Let’s go~
文章目录
源程序编译链接生成文件格式
首先贴出从源程序生成可执行文件的过程。
源程序(source code)->预处理(preprocessor) ->编译和优化 (compiler) - >生成目标文件(object code) ->链接(Linker)->可执行文件
生成文件个是对应表源程序 预处理 编译和优化 生成目标文件 链接 可执行文件 文件后缀 .h、.cpp、.c .i .s .o .out .exe 语言 c\c++ c\c++ 汇编 二进制 二进制 二进制 下面是实际举例说明:
1.h#pragma once void print(); //我是1.h的注释
1.cpp
#include "1.h" #include<iostream> //我是1.cpp的注释 void print() { std::cout << "print函数运行成功" << std::endl; }
main.cpp
#include <iostream> #include "1.h" #define AA "this is a macro!" int main() { #ifdef AA //我是main.cpp的注释 print(); std::cout << AA << std::endl; #endif int a = 1; int b = 5; int c = a + b; std::cout << c << std::endl; system("pause"); return 0; }
预编译
预编译阶段先介绍编译器。先介绍几个概念:GNU、GCC、gcc、g++
按照概念范围大小排序如下:
GNU:GNU是一个操作系统。1971年贝尔实验室汤姆森和里奇完成了unix的的基本工作,1973年写成了C语言版本,但是unix并不是贝尔实验室的正式项目,所以也没有商业应用,这两个人就用写论文发表,在学术圈技术交流,高校教授觉得很好,学生也学习,汤姆森和里奇都是免费拷贝分享,一起发展修改进步,结果AT&T公司觉得很有前景,于是选择申请专利商业闭源,这样学生就得花钱买,1985年,Richard Stallman 愤怒的认为unix大家都有过添砖加瓦的建设,应该共享。随即,发起GNU(GNU is not unix)自由操作系统,软件共享运动。本来GNU就是要做一个完整的操作系统来取代unix,但是当编译器、编辑器等其他的外围都写完后,操作系统的内核Hurd一直没有搞定,1991年一个叫Linus Torvalds的学生写了自己的linux,也就是操作系统内核,于是Linux和GNU组合起来成了现在的GNU/Linux操作系统,也就是常简说的Linux操作系统。
GCC:GNU Compiler Collection(GUN 编译器集合),它可以编译C、C++、JAV、Fortran、Pascal、Object-C、Ada等语言。
g++:GCC中的GUN C++ Compiler(C++编译器)
gcc:GCC中的GUN C Compiler(C 编译器)
由于编译器是可以更换的,所以gcc不仅仅可以编译C文件,更准确的说法是:gcc调用了C compiler,而g++调用了C++ compiler。
gcc and g++分别是gnu的c & c++编译器,gcc/g++在执行编译工作的时候,总共需要4步:1.预处理,生成.i的文件[预处理器cpp]; 2.将预处理后的文件转换成汇编语言,生成文件.s[编译器egcs]; 3.由汇编变为目标代码(机器代码)生成.o的文件[汇编器as]; 4.连接目标代码,生成可执行程序[链接器ld]。
gcc和g++的主要区别:
-
对于 .c和.cpp文件,gcc分别当做c和cpp文件编译(c和cpp的语法强度是不一样的)
-
对于 .c和.cpp文件,g++则统一当做cpp文件编译
-
使用g++编译文件时,g++会自动链接标准库STL,而gcc不会自动链接STL
-
gcc在编译C文件时,可使用的预定义宏是比较少的
-
gcc在编译cpp文件时/g++在编译c文件和cpp文件时(这时候gcc和g++调用的都是cpp文件的编译器),会加入一些额外的宏,这些宏如下:
#define GXX_WEAK 1
#define __cplusplus 1
#define __DEPRECATED 1
#define GNUG 4
#define __EXCEPTIONS 1
#define private_extern extern -
在用gcc编译c++文件时,为了能够使用STL,需要加参数 –lstdc++ ,但这并不代表 gcc –lstdc++ 和 g++等价,它们的区别不仅仅是这个。
主要参数:
-g - turn on debugging (so GDB gives morefriendly output)
-Wall - turns on most warnings
-O or -O2 - turn on optimizations
-o - name of the output file
-c - output an object file (.o)
-I - specify an includedirectory
-L - specify a libdirectory
-l - link with librarylib.a
使用示例:g++ -ohelloworld -I/homes/me/randomplace/include helloworld.C
以上参考来源于下面链接。感谢原作者。https://www.cnblogs.com/oxspirt/p/6847438.html
预处理器主要负责以下的几处:
1.宏的替换 2.删除注释 3.处理预处理指令,如#include,#ifdef
下面进行实例验证。
现找到:
点击进入你的工程目录对应的cpp位置。
输入g++ –E main.cpp>main.i进行预编译生成main.i文件。
打开main.i发现里面内容实在很多,前面一大堆不知道啥,大概就是在干#include 和#include "1.h"这两件事。到文件最后:
看看行号,就简简单单的main函数,就那么多行了。重点是:main函数中的头文件被替换了(不管替换成什么了),注释不见了,宏被替换了。
同理生成1.i文件
也是相同的处理。
当相同地编译运行1.h文件时,有了如下警告:
单词“pragma”就是编译指示的意思,警告你1.h只在main文件中编译一次,此操作不可行。
编译和优化
这个步骤作用如下:
词法分析 – 识别单词,确认词类;比如int i;知道int是一个类型,i是一个关键字以及判断i的名字是否合法。 语法分析 – 识别短语和句型的语法属性; 语义分析 – 确认单词、短语和句型的语义特征; 代码优化 – 修辞、文本编辑; 代码生成 – 生成译文。 内联函数的替换就发生在这一阶段
预编译和编译都是GCC来完成的。
编译
按照g++ –E main.cpp>main.i文件的语句写g++ –E main.i>main.s,然后报错了。
然后用g++ –E main.cpp>main.s也是相同的报错。
后来搜到有人使用g++ -S main.cpp,生成成功。.s文件表示是汇编文件,用编辑器打开就都是汇编指令。.file "main.cpp" .lcomm __ZStL8__ioinit,1,1 .def ___main; .scl 2; .type 32; .endef .section .rdata,"dr" LC0: .ascii "this is a macro!\0" LC1: .ascii "pause\0" .text .globl _main .def _main; .scl 2; .type 32; .endef _main: leal 4(%esp), %ecx andl $-16, %esp pushl -4(%ecx) pushl %ebp movl %esp, %ebp pushl %ecx subl $36, %esp call ___main call __Z5printv movl $LC0, 4(%esp) movl $__ZSt4cout, (%esp) call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp) movl %eax, %ecx call __ZNSolsEPFRSoS_E subl $4, %esp movl $1, -12(%ebp) movl $5, -16(%ebp) movl -12(%ebp), %edx movl -16(%ebp), %eax addl %edx, %eax movl %eax, -20(%ebp) movl -20(%ebp), %eax movl %eax, (%esp) movl $__ZSt4cout, %ecx call __ZNSolsEi subl $4, %esp movl $__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, (%esp) movl %eax, %ecx call __ZNSolsEPFRSoS_E subl $4, %esp movl $LC1, (%esp) call _system movl $0, %eax movl -4(%ebp), %ecx leave leal -4(%ecx), %esp ret .def ___tcf_0; .scl 3; .type 32; .endef ___tcf_0: pushl %ebp movl %esp, %ebp subl $8, %esp movl $__ZStL8__ioinit, %ecx call __ZNSt8ios_base4InitD1Ev leave ret .def __Z41__static_initialization_and_destruction_0ii; .scl 3; .type 32; .endef __Z41__static_initialization_and_destruction_0ii: pushl %ebp movl %esp, %ebp subl $24, %esp cmpl $1, 8(%ebp) jne L4 cmpl $65535, 12(%ebp) jne L4 movl $__ZStL8__ioinit, %ecx call __ZNSt8ios_base4InitC1Ev movl $___tcf_0, (%esp) call _atexit L4: leave ret .def __GLOBAL__sub_I_main; .scl 3; .type 32; .endef __GLOBAL__sub_I_main: pushl %ebp movl %esp, %ebp subl $24, %esp movl $65535, 4(%esp) movl $1, (%esp) call __Z41__static_initialization_and_destruction_0ii leave ret .section .ctors,"w" .align 4 .long __GLOBAL__sub_I_main .ident "GCC: (i686-posix-sjlj, built by strawberryperl.com project) 4.9.2" .def __Z5printv; .scl 2; .type 32; .endef .def __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc; .scl 2; .type 32; .endef .def __ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_; .scl 2; .type 32; .endef .def __ZNSolsEPFRSoS_E; .scl 2; .type 32; .endef .def __ZNSolsEi; .scl 2; .type 32; .endef .def _system; .scl 2; .type 32; .endef .def __ZNSt8ios_base4InitD1Ev; .scl 2; .type 32; .endef .def __ZNSt8ios_base4InitC1Ev; .scl 2; .type 32; .endef .def _atexit; .scl 2; .type 32; .endef
汇编咱也看不懂,咱也不敢说,只是看明白了1+5,add 了存在1和5的地址。
同理生成1.s文件。也是看不懂。优化
优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。上图中,我们将优化阶段放在编译程序的后面,这是一种比较笼统的表示。
对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。
后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。
经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。
详细可见下面链接,感谢原作者。
https://blog.csdn.net/qq_30647245/article/details/80558生成目标文件
这一步是从汇编程序生成目标代码(机器代码)。
GCC语句是g++ -c main.cpp
功能:.o是GCC生成的目标文件,除非你是做编译器和连接器调试开发的,否则打开这种.o没有任何意义。二进制机器码一般人也读不了,就是下图这样。
链接
连接各个.o目标代码文件,生成可执行程序。必须已经生成1.o和main.o,这样才能链接需要的.o文件生成.exe。
g++语句为 g++ 1.o main.o -o Test.exe
双击生成的Test.exe
这样,完整的从源程序到exe就结束了。其中的g++命令语句参考链接为https://www.jianshu.com/p/e5e9925a6158,感谢原作者。
下面终于要进入今天的正题链接库是怎么回事?
什么是库?
库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。所谓静态、动态是指链接。
动态静态的区别
上面刚刚提到了静态链接库和动态链接库,如果采用静态链接库,则无论你愿不愿意,在链接程序时,lib中的指令都被直接(通过拷贝的方式)包含在最终生成的EXE文件中了,这是在第5步链接程序中可以知道的。动态链接库在链接程序时,只是记录了少量必要信息,在实际程序执行时才动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。所以,当单独执行debug文件或者release文件中的exe的时候,静态链接库不需要放在exe的目录下,但是动态链接库必须放在exe的目录下。
静态链接库
之所以成为静态库,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
试想一下,静态库与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。静态库特点总结:1 静态库对函数库的链接是放在编译时期完成的。 2 程序在运行时与函数库再无瓜葛,移植方便。 3 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
静态链接库的优点和缺点:
1.代码装载速度快,执行速度略比动态链接库快;
2.只需保证在开发者的计算机中有正确的.LIB文件,在以二进制形式发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题,可避免DLL地狱等问题。
3.使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费;lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。
静态链接库的创建方法
首先创建静态库,要封装的函数只是一系列子函数:
//main_test.cpp #include <iostream> #include "1.h" #define AA "this is a macro!" void main_test() { #ifdef AA //我是main.cpp的注释 print(); std::cout << AA << std::endl; #endif int a = 1; int b = 5; int c = a + b; std::cout << c << std::endl; } void my_func() { std::cout << "我是第二个函数" << std::endl; } //main_test.h #pragma once void main_test(); void my_func();
在工程属性里更改如下:不生成exe,就生成静态库lib
按F7或者点击生成解决方案之后就可以在Debug里看到生成的lib文件了。lib的调用
将这里的.lib文件和.h文件拷贝,到要引用的目录下,#include “*.h”和#pragma comment (lib,".lib"),在当前工程中就可以直接用里面的函数了。lib文件还有其他方法添加,这里就不赘述了。
动态链接库
代码复用是提高软件开发 效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架, 如ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点比较多,总结起来有4点:
1 暴露了源代码; 2 容易与程序员的“普通”代码发生命名冲突; 3 多份拷贝,造成存储浪费; 4 更新功能模块比较困难。
实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。
另一个问题是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。
动态链接库的创建方法
创建动态库的方法略有不同。先是将属性里的配置类型改为动态库(dll)。
在头文件中需要加入宏命令。#pragma once #ifdef __DLLEXPORT #define __DLL_EXP _declspec(dllexport) // 导出函数 - 生成dll文件时使用 #else #define __DLL_EXP _declspec(dllimport) // 导入函数 -使用dll是使用 #endif // __DLLEXPORT void main_test(); void my_func();
如果是生成dll文件,需要在属性->C\C+±>预处理器->预处理器定义中加上宏。
生成解决方案后在Debug中就生成了 .dll和.lib文件。我开始的时候怎么都只有dll,没有lib,后来参考别人的建议,在在工程上右键 -> 添加 -> 新建项 -> 选"模块定义文件(.def)" -> 随便输入个名字 -> 添加,添加的名字任意,可以默认,里面只有LIBRARY这一句,不用管,这时候再生成解决方案,Debug里就有lib和dll了。
def文件中内容要加上函数名//Source.def LIBRARY Project2 EXPORTS main_test @1 my_func @2
注意:.def文件中的第一条 LIBRARY 语句不是必须的,但LIBRARY 语句后面的 DLL 的名称必须正确,即与生成的动态链接库的名称必须匹配。
EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。
main_test.cpp文件为:#include <iostream> #include "main_test.h" #pragma comment(lib, "Project2.lib") void main() { main_test(); my_func(); system("pause"); }
F7生成解决方案就会生成dll、lib文件。
dll的使用
调用分为2种。
隐式链接
需要用到.dll、.lib和.h文件。
这里的.lib文件不再是静态链接库的意思,而是导入库的意思。
将三个文件复制到需要使用的工程目录中,
将.dll和.lib放在一个文件夹内。
测试工程main.cpp#include <iostream> #include "main_test.h" #pragma comment(lib, "ku\\Project2.lib") void main() { main_test(); my_func(); system("pause"); }
运行结果
显式链接
需要用到.dll和.h文件。
我暂时不需要用显式调用,所以以后用到再说。
That’s all. Let’s enjoy it ~
-
-
动态链接库的建立与调用
2020-05-25 10:33:03(1)理解动态链接库的实现原理。 (2)掌握Windows系统动态链接库的建立方法。 (3)掌握Windows环境下动态链接库的调用方法。 二:实验准备知识:动态链接库介绍 动态链接库(Dynamic Link Library DLL)是一个可...一:实验目的
(1)理解动态链接库的实现原理。
(2)掌握Windows系统动态链接库的建立方法。
(3)掌握Windows环境下动态链接库的调用方法。
二:实验准备知识:动态链接库介绍
动态链接库(Dynamic Link Library DLL)是一个可执行模块,它包含的函数可以由Windows应用程序调用以提供所需功能,为应用程序提供服务。
1.动态链接库基础知识
大型的应用程序都是由多个模块组成的,这些模块彼此协作,已完成整个软件系统的工作。其中可能有些模块的功能是通用的,被多个软件系统使用。在设计软件系统时,如果将所有模块的源代码都静态编译到整个应用程序的.exe文件中,会产生两个问题,一是应用程序过大,运行时消耗较大的内存空间,造成系统资源的浪费;二是在修改程序时,每次程序的调整都必须编译所有的源代码,增加了编译过程的复杂度,也不利于阶段性的模块测试。
Windows系统提供了非常有效的编译和运行环境,可以将独立的模块编译成较小的动态链接库文件,并可对这些动态链接库单独进行编译和测试。运行时,只有在主程序需要时才将动态链接库装入内存并运行。这样不仅减少了应用程序的大小及对内存的大量需求,而且使得动态链接库可以被多个应用程序使用,从而充分利用了资源。Windows系统中的一些主要系统功能都是以动态链接库的形式出现的,如设备驱动器等。
动态链接库文件在Windows系统中的扩展名为.dll,它由全局数据结构、若干函数组成,运行时被系统加载到进程的虚拟地址空间中,成为调用进程的一部分。如果与其他的动态链接库没有冲突,该文件通常映射到进程虚拟地址空间地址上。
2.动态链接库入口函数
DllMain()函数是动态链接库的入口函数,当Windows系统加载动态链接库时调用该函数,DllMain()函数不仅在将动态链接库加载到进程地址空间时被调用,在动态链接库进程分离时也被调用。
每个动态链接库必须有一个入口点,像用C语言编写其他应用程序时必须有一WinMain()函数一样,在Windows系统的动态链接库中,DllMain()是默认的入口函数。函数原型如下:
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
其中参数hModule为动态链接库的句柄,其值与动态链接库的地址相对应。参数ul_reason_for_call指明系统调用该函数的原因。lpReserved说明动态链接库是否需要动态加载或卸载,lpReserved为NULL表示需要动态加载或使用FreeLibrary()卸载,即运行时用到该动态库链接库时才将其装入内存,当进程不用该动态链接库时,可以使用FreeLibrary()将动态链接库卸载。lpReserved为非NULL表示静态加载,进程终止时才卸载,即进程装入内存时同时将其动态链接库装入,进程终止时动态链接库与进程同时被卸载。
使用入口函数还能使动态链接库在被调用时自动做一些初始化工作,如分配额外的内存或其他资源;在撤销时做一些清除工作,如回收占用的内存或其他资源。需要做初始化或清除工作时,DllMain()函数格式如下:
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
Switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
初始化或清除工作分以下几种情况。
DLL_PROCESS_ATTACH
当动态链接库被初次映射到进程的地址空间时,系统将调用该动态链接库的DllMain()函数,给它传递参数ul_reason_for_call,的值DLL_PROCESS_ATTACH。当处理DLL_PROCESS_ATTACH时,动态链接库应执行动态链接库函数要求的任何与进程相关的初始化工作,如动态链接库堆栈的建立等。当初始化成功时,DllMain()返回TRUE,否则返回FALSE,并终止整个程序的执行。
DLL_PROCESS_DETACH
当动态链接库从进程的地址空间被卸载时,系统将调用该动态链接库的DllMain()函数,给它传递参数ul_reason_for_call的值DLL_PROCESS_DETACH。当处理DLL_PROCESS_DETACH时,动态链接库执行与进程相关的清除操作,如堆栈的撤销等。
DLL_THREAD_ATTACH
当在一个进程中创建进程时,系统查看当前映射进程的地址空间中的所有动态链接库文件映像,并调用每个带有DLL_THREAD_ATTACH 值的DllMain()函数文件映像。这样,动态链接库就可以执行每个线程的初始化操作。新创建的线程负责自行动态链接库的所有DllMain()函数中的代码。
当一个新动态链接库被映射到进程地址空间时,如果该进程内已经有若干个线程正在执行,那么系统将不为现有的线程调用带DLL_THREAD_ATTACH值的DllMain()函数。只有当新线程创建,动态链接库被映射到进程地址空间时,它才可以调用带有DLL_THREAD_ATTACH值的DllMain()函数。另外,系统并不为主线程调用带DLL_THREAD_ATTACH值的DllMain()函数。进程初次启动时映射到进程的地址空间中的任何动态链接库均接收DLL_PROCESS_ATTACH通知,而不是DLL_THREAD_ATTACH通知。
DLL_THREAD_DETACH
终止线程的方法是系统调用ExitThread()函数撤销该线程,但如果ExitThread()函数要终止动态链接库所在的线程,系统不会立即将该线程撤销,而是取出这个即将被撤销的线程,并让它调用已映射的动态链接库中所有带有DLL_THREAD_DETAC值的DllMain()函数。通知所有的动态链接库执行每个线程的清除操作,只有当每个动态链接库都完成了对DLL_THREAD_DETACH通知的处理时,操作系统才会终止线程的运行。
如果当动态链接库被撤销时任然有线程在运行,那么带有DLL_THREAD_DETACH值的DllMain()函数就不会被任何线程调用。所以在处理DLL_THREAD_DETACH时,要根据具体情况进行。
3.动态链接库导入/导出函数
动态链接库文件中包含一个导出函数表,这些导出函数有他们的符号名和标识名被唯一地确认,导出函数表中还包含了动态链接库中函数的地址。当应用程序加载动态链接库时,通过导出函数表中各个函数的符号名和标识名找到该函数的地址。如果重新编译动态链接库文件,并不需要修改调用动态链接库的应用程序,除非改变了导出函数的符号名和其它参数。
在动态链接库源程序文件中声明导出函数的代码如下:
_declspec(dllexport) MyDllFunction(int x,int y);
其中关键字_declspec(dllexport)表示要导出其后的函数MyDllFunction()。如果一个动态链接库文件中的函数还需要调用其他动态链接库,此时,动态链接库文件除了导出函数外,还需要一个导入函数,声明导入函数的代码如下:
_declspec(dllimport) DllAdd(int x,int y);
其中关键字_declspec(dllimport) 表示要导入其后的函数DllAdd(),在生成动态链接库时,链接程序会自动生成一个与动态链接库相对应的导入/出库文件(.lib文件),下面的例子中建立一个名为SimpleDll的动态链接库工程文件,在SimpleDll工程的Debug目录下,可以看到 SimpleDll.dll和 SimpleDll.lib 两个文件,其中SimpleDll.dll是编译生成的动态链接库可执行文件,SimpleDll.lib 就是导入/导出库文件,该文件中包含SimpleDll.dll文件名和SimpleDll.dll中的函数名Add()和 Sub(),SimpleDll.lib 是文件SimpleDll.dll的映像文件,在进行隐式链接时要用到它。
下面是一个动态链接库程序的例子。
#include ···
extern “C”_declspec(dllexport) int Add (int x,int y);
extern “C”_declspec(dllexport) int Sub (int x,int y);
BOOL APIRNTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
return TRUE;
}
int Add(int x,int y)
{
int z;
z=x+y;
return z;
}
int Sub(int x,int y)
{
int z;
z=x-y;
return z;
}
应用程序要链接引用动态链接库的任何可执行模块,其.lib文件是必不可少的。除了创建.lib文件外,链接程序还要将一个输出符号表嵌入到产生的动态链接库文件。该输出符号表包含输出变量、函数和类的符号列表基函数的虚拟地址。
4.动态链接库的两种链接方式
当应用程序调用动态链接库时,需要将动态链接库文件映射到调用进程的地址空间中。映射方法有两种:一种是应用程序的源代码只引用动态链接库中包含的符号,当应用程序运行时,加载程序隐式地将动态链接库装入到进程的地址空间中,这种方法也称隐式链接;另一种方法是应用程序运行时使用LoadLibary()显式地加载所需要的动态链接库,并显式地链接需要的输出符号表。
当进程加载动态链接库是,Windows系统按以下搜索顺序查找并加载动态链接库。
应用程序的当前目录(将动态链接库文件复制到应用程序的.exe文件所在目录下)。
Windows目录下。
Windows\System32目录下。
PATH环境变量中设置的目录。
列入映射网络目录表中的目录。
(1)隐式链接
如果程序员建立了一个动态链接库文件(.dll),链接程序会自动生成一个与动态链接库相对应的导入/导出库文件(.lib文件)。该文件作为动态链接库的替代文件被编译到应用程序地工程项目中。当编译生成应用程序时,应用程序中的调用函数与.lib文件中导出符号名相匹配,若匹配成功,这些符号名进入生成的应用程序的可执行文件。.lib文件中也包含对应动态链接库文件名,但不含路径,它同样也被放入生成的应用程序的可执行文件。Windows系统根据这些信息发现并加载动态链接库,然后通过符号名实现对动态链接库函数的动态链接。
在调用动态链接库的应用程序中,声明要调用的动态链接库中的函数,需要写明extern”C”,它可以使其他编程语言访问所写的动态链接库中的函数。下面是通过隐式链接方法调用Simp[leDll.dll中的函数Add()和Sub()的方法,先建立一个控制台工程文件CallDll,在CallDll.cpp文件中输入代码:
extern ”C”_declspec(dllimport) int Add(int x,int y);
extern “C”_declspec(dllimport) int Sub(int x,int y);
int main(int argc,char* argv[])
{
int x=7;
int y=6;
int t add=0;
int sub=0;
printf(“Call Dll Now!\n”);
//调用动态链接库
add=Add(x,y);
sub=Sub(x,y);
printf(“7+6=%d,7-6=%d\n”,add,sub);
return 0;
}
为了能够使用调用程序CallDll.cpp正确地调用到动态链接库SimpDll.dll,在生成工程文件CallDll.cpp的执行文件之前,先将SimpleDll.dll复制到工程文件CallDll的Debug目录下,将SimpleDll.lib复制到CallDll.cpp所在目录下。然后在Microsoft Visual Studio的环境下的【Project Setting】对话框中的【Link】选项卡中输入动态链接库的导入/导出库文件SimpleDll.lib。
(2)显示连接
显示连接方式更适合于集成化的开发工具,如Visual Basic等,使用显示连接,程序员不必再使用导入/导出文件,而直接调用WIN32提供的LoadLibary()函数加载动态链接库文件,调用GetProcAddress()函数得到动态链接库中函数的内部地址,在应用程序退出之前,还应使用FreeLibrary()释放动态链接库。
下面是通过显示连接调用动态链接库的例子。
#include …
Int_tmain(int argc,TCHARargv[],TCHARenvp[])
{
int s;
int nRetCode=0;
typedef int (*pAdd)(int x,int y);
typedf int (*pSub)(int x,int y);
HMODULE hDll;
pAdd add;
pSub sub;
hDll=LoadLibrary(“SimpleDll.dll”);
add=(pAdd)GetProcAddress(hDll,”Add”);
s=add(6,2);
sub=(pSub)GetprocAddress(hDll,”Sub”);
s=sub(6,2);
FreeLibrary(hDll);
Return nRetCode;
}
在上面的例子中,使用类型定义关键字typedef定义了指向动态链接库中相同函数原型的指针,然后通过LoadLibrary(“SimpleDll.dll”)将到头了即可文件SimpleDll.dll加载到应用程序中,并返回当前动态链接库文件的句柄。在通过GetProcAddress(hDll,“Add”)和GetProcAddress(hDll,“Sub”)获得导入到应用程序中动态链接库的函数Add()和Sub()的指针。函数调用完毕后使用Freelibrary(hDll)卸载动态链接库文件。需要注意的是,在编译应用程序之前,要吧动态链接库文件复制到应用程序所在的目录下。使用显示连接方式,不需要使用相应的.dll文件
5.函数调用参数传递约定
动态链接库函数调用参数传递约定决定着函数参数传送时入栈和出栈的顺序,以及编译器用来识别函数名字的修饰约定。为了使不同的编译语言方便地共享动态链接库,函数输出时必须正确的调用约定。
Microsoft Visual C++6.0 支持的常用函数约定主要有_stdcall调用约定、C调用约定和_fastcall调用约定。
_stdcall调用约定相当于16位动态链接库库中经常使用的PASCAL调用约定。在Microsoft Visual C++6.0 中不再支持PASCAL调用约定,取而代之的是_stdcall。两者在实质上是一致的,即调用时函数的参数自右向左通过内存栈传递,被调用函数返回时清理传递参数的内存栈。
C调用约定(用_cdecl关键字说明)与_stdcall调用约定在参数传递顺序上是一致的,不同的是用于传递参数的内存栈是由调用者来维护的,使用便餐的函数只能使用该调用约定。
_fastcall调用约定的主要特点是快,因为它是通过寄存器来传递参数的,实际上它使用寄存器ECX和EDX传送前两个汉字,剩下的参数仍旧自右向左通过内存栈传递,被调用函数返回时清理传递参数的内存栈。因此,对于参数较少的函数使用关键字_fastcall可以提高其运行速度。
关键字_stdcall、_cdecl和_fastcall可以在函数说明时直接写在要输出的函数前面,也可以在Microsoft Visual C++6.0编译环境中进行设置。
三:实验内容
(1)在Windows环境下建立一个动态链接库。
(2)使用隐式调用法调用动态链接库。
(3)使用显式调用法调用动态链接库。
四:实验要求
掌握动态链接库建立和调用方法。在Windows XP + Visual C++6.0 环境下建立一个动态链接库,并分别使用隐式和显式方式将其调用,从而体会使用动态链接库的优点。
五:实验结果
可以看到通过建立动态链接库之后两个calldll01和calldll02分别可以运行,结果如下:
六:源代码
dll:#include "stdafx.h" extern "C"_declspec(dllexport) int Add (int x,int y); extern "C"_declspec(dllexport) int Sub (int x,int y); bool APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } int Add(int x,int y) { int z; z=x+y; return z; } int Sub(int x,int y) { int z; z=x-y; return z; }
calldll01:
#include "stdafx.h" #include "calldll01.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif / // The one and only application object CWinApp theApp; extern "C" _declspec(dllimport) int Add (int x,int y); extern "C" _declspec(dllimport) int Sub (int x,int y); using namespace std; int _tmain(int argc,TCHAR* argv[],TCHAR* envp[]) { int nRetCode=0; int x=7; int y=6; int add=0; int sub=0; printf("Call Dll Now!\n"); add=Add(x,y); sub=Sub(x,y); printf("7+6=%d,7-6=%d\n",add,sub); return nRetCode; }
calldll02:
#include "stdafx.h" #include "calldll02.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif / // The one and only application object CWinApp theApp; using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { int s; int nRetCode=0; typedef int (*pAdd)(int x,int y); typedef int (*pSub)(int x,int y); HMODULE hDll; pAdd add; pSub sub; hDll=LoadLibrary("dll.dll"); if(hDll=NULL) { printf("LoadLibrary Error.\n"); return nRetCode; } else printf("LoadLibrary Success.\n"); add=(pAdd)GetProcAddress(hDll,"Add"); s=add(6,2); printf("6+2=%d\n",s); sub=(pSub)GetProcAddress(hDll,"Sub"); s=sub(6,2); printf("6-2=%d\n",s); FreeLibrary(hDll); return nRetCode; }
七:实验总结
本实验介绍了在Windows XP + Microsoft Visual C++6.0环境下建立于调用动态链接库的方法,使用动态链接库,除了可以节省内存空间、实现代码共享之外,还可以实现多种编程语言书写的程序相互调用,本次创建并使用动态链接库完成了calldll01与calldll02的实验,分别为静态加载和动态加载。 -
java使用动态链接库
2022-01-28 10:36:30java使用动态链接库 java可以使用native关键字调用第三方库,现在我们简单使用当前两种操作系统的库编写第三方接口来支持java调用 先定义好java的对应接口 package chingee.test.dll; public class HelloWorld { ... -
动态链接库与静态链接库的区别
2021-04-26 18:23:00动态链接库与静态链接库的区别 库文件的概念 库文件是计算机上的一类文件,提供给使用者一些开箱即用的变量、函数或类。库文件分为静态库和动态库,静态库和动态库的区别体现在程序的链接阶段:静态库在程序的... -
C++引用动态链接库
2020-09-16 14:40:22因为要使用到C++的动态链接库,所以就特意网上找了一下资料实现了一下。 文章目录一、lib与dll文件二、创建dll文件三、dll隐式链接四、显式链接五、小结 一、lib与dll文件 之前我一直以为动态链接库就是指dll文件,... -
区别动态链接库和静态链接库
2021-03-29 09:42:08动态链接库和静态链接库: 首先理解两种生成可执行程序的链接方式: 动态链接: 链接动态库,只是在生成的程序中记录库中的函数信息表,并没有将具体代码实现写入程序中,所以依靠动态链接生成的程序在运行时,需要... -
Android动态链接库减小体积
2021-09-07 18:11:21用NDK编译动态链接库(so); 编译完成后,如果生成的库体积比较大,可以用NDK自带工具strip.exe来瘦身 64位: ndk\21.4.7075529\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\aarch64-linux-... -
QT 创建并调用 动态链接库Dll
2021-12-29 20:47:40QT操作动态链接库 自定义目录QT操作动态链接库1.新建动态链接库2.调用动态链接库 为了提高代码的复用性,实现模块化开发,需要对一些常用函数进行封装,可以通过调用共享库的方式实现;本教程以Qt Creater编译器为... -
使用gdb调试动态链接库
2019-05-10 08:59:04在使用gdb进行调试之前,必须保证编译的可执行程序和想要调试的动态库编译包含了-g选项。这里还有一个坑,有时候我们虽然指定-g进行了编译,但是编译完成后又使用strip命令去除了调试信息,那么最终的程序和库也是... -
C# 调用 c++ 动态链接库,浅学一下~
2022-04-25 01:19:03首先是c++动态库的生成,我这里最开始是没有选择动态链接库创建项目,从生成 .exe项目转换成 .dll 项目。改动两个地方项目属性——配置属性——常规——目标文件扩展名(改为 .dll)——配置类型(改为:动态库.dll)... -
静态链接库和动态链接库的区别
2018-04-23 20:45:30一、静态链接库的使用 静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件。 在VS2015平台上,创建一个静态库(.lib)项目方案,选择【创建项目/Win32/Win32控制台... -
VS2017 动态链接库的制作及调用---从动态链接库中导出函数和类并调用
2019-04-28 14:32:20Step#1: 新建动态链接库文件 文件->新建->项目->选择Visual C+±>Windows桌面->动态链接库 名称DLL-1。新建成功之后, 平台设置为Debug x64平台。 默认生成以下文件: 在Dll-1.cpp中输入如下... -
C#调用C++动态链接库
2018-12-25 18:37:53将静态链接库做成动态链接库新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants... -
cmake:生成动态链接库并使用
2020-06-30 17:46:52库函数的源文件名字叫hello.c,库函数的头文件名字叫hello.h,生成的动态链接库函数的名称叫libhello.so,目标是在/hello/install文件夹下安装这个库,以便后来调用。设库函数源文件在/home/hello/libhello目录,... -
delphi动态链接库dll(内含多个经典例子)
2011-10-27 22:33:50内含多个动态链接库 dll 的经典实例(delphi),封装了单元,做软件用到多线程时,复制一下这个代码就行了,非常方便好用。网上delphi动态链接库的实例比较少,有了这个,就不用怕了!!.. -
为什么要动态链接,动态链接库和静态连接的区别与优势
2018-07-27 14:30:12本文摘抄于程序员的自我修养-链接装载与库7.1节,这段写的很好,直接拿过来来收藏 http://www.wq3028.top/technology/compile/20180727124/ 静态链接使得不同的程序开发者和部门能够相对独立地开发和测试自己的... -
(笔记)Simulink生成动态链接库并使用
2020-09-17 15:03:00前段时间完成了一次生成动态链接库并使用,没有做记录,结果又花了两天重新探索过程,及时记录非常有必要。 1.新建simulink模型,如下图所示。 2.配置仿真条件。 3 . 编译生成C代码 4. 编译生成动态链接库文件 ... -
如何使用JNA调用本地C/C++动态链接库详细示例代码
2011-07-22 15:24:34详细演示了如何通过JNA实现以下形式的动态链接库接口: 1:基本数据类型 2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并... -
动态链接库和静态链接库的区别
2019-08-12 02:12:13静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要... 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在... -
深入浅出Visual C++动态链接库(DLL)编程(pdf版+word版+全部源码)
2011-03-23 07:01:26静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。 对动态链接库,我们还需建立如下概念: (1)DLL 的... -
vs添加动态链接库的三种方法
2019-11-07 14:07:263、添加工程引用的dll动态库:把引用的dll放到工程的可执行文件所在的目录下。 这种方法比较繁琐,且不直观,而且还可能要争对debug版本和release版本作不同的配置,因为我们生成的两个版本的库可能放在不同的目录... -
C++编译为动态链接库并用python调用
2022-04-27 18:39:14将C++编译为动态链接库并用python调用其函数 -
xp系统恢复dll动态链接库的修复步骤
2021-07-22 00:50:42今天和大家分享一下关于对xp系统恢复dll动态链接库设置的方法,在使用xp系统的过程中经常不知道如何去对xp系统恢复dll动态链接库进行设置,有什么好的办法去设置xp系统恢复dll动态链接库呢?在这里小编教你只需要1.... -
linux下简单的制作一个C++动态链接库
2022-04-06 18:34:47} 打开终端输入: g++ dylib_test.cpp -fPIC -shared -o libdylib_test.so 来生成我们的动态库.so文件 然后在目录下:sudo cp libdylib_test.so /usr/lib/ 将我们的so文件拷贝到用户系统库里 再:sudo cp dylib_... -
动态链接库和静态链接库的相互包含问题及实现
2018-12-19 09:58:15动态链接库和静态链接库的基本概念请参考 1.《深入理解计算机系统》第七章链接 2.《程序员的自我修养》第4章 静态链接和第7章 动态链接 3.http://blog.jobbole.com/86852/ 目的 为了封装代码,简化接口,... -
动态链接库和静态链接库
2018-08-28 11:49:041. 库的介绍 库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。...库有两种:静态库(.a、.lib)和动态库(.so... -
2.动态链接库的生成和使用
2018-08-10 01:41:421.什么是动态链接库 动态链接库(Dynamic-Link Library)通常包含程序员自定义的变量和函数,可以在运行时动态链接到可执行文件中 2.动态库扩展名 Windows下是.dll,Linux下是.so 3.Windows系统动态链接库 ...