动态编译与静态编译

动态编译

动态编译是某些程式语言在执行时用来增进效能的方法。尽管这技术源于Self,但使用此技术最为人所知的是Java。它可以做一些只在执行时才能得知的最佳化。使用动态编译的执行环境一开始几分钟执行慢,之后,完成大部分的编译和再编译后,会执行快。因为初始效能的延迟,动态编译不适用于一些情况。在许多实作中,一些可以在编译时期做的最佳化被延到执行时期才编译,导致不必要的效能降低。即时编译是一种动态编译的形式。

一个非常近似的技术是递增式编译。递增式编译器用于POP-2、POP-11、一些Lisp的版本,如Maclisp和最少一种版本的ML语言(Poplog ML)。这要求编程语言的编译器成为执行环境的一部分。结果,可以在任何时候从终端、从档案、或从执行中程式所建造数据结构中读取源码。然后,转成机器码区块或函数(有可能取代之前同名的函数),之后可立即被程式使用。因为执行中对互动开发和测试的速度的要求,编译后的机器码所做的最佳化程度不如标准“批次编译器”。然而,递增式编译过的程式跑起来通常比同一个程式的解译版本还快。递增式编译因而提供了编译和解译语言优点的混合版。为了移植性,递增式编译通常采用两个步骤。第一个步骤会编译到中间与平台独立的语言,然后再到机器码。在这例子,移植只须改变“后端”编译器。不同于动态编译,递增式编译在程式执行后不会做更进一步的最佳化。

静态编译

静态编译,就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a或.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。

动态编译与静态编译的区别

动态编译的可执行文件需要附带一个动态链接库。在执行时,需要调用其对应动态链接库中的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点一是哪怕是很简单的程序,只用到了链接库中的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。

静态编译就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应静态库(.a或.lib)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。所以其优缺点与动态编译的可执行文件正好互补。

静态编译lib

所谓静态链接就是把函数或过程直接链接到可执行文件中,成为可执行程序中的一部分,当多个程序调用同样的函数时,内存里就会有这个函数的多个拷贝,浪费内存资源。生成lib文件,包含了函数索引以及实现,这个LIB会比较大。

生成和使用静态库

生成静态库

新建项目–win32项目–填写项目名–确定–下一步–应用程序类型:选择静态库。

静态库项目没有main函数,也没有像dll项目中的dllmain。

创建项目后添加.h文件,添加相应的导出函数、变量或类,如下所示:

// .h
#ifndef _MYLIB_H_
#define _MYLIB_H_
 
void fun(int a);
 
extern int k;
 
class testclass
{
public:
    testclass();
    void print();
};
 
#endif

 

//.cpp文件如下

#include "stdafx.h"
#include "lib.h"
#include <iostream>
 
void fun(int a)
{
    std::cout<<a<<"lib gen\n";
}
 
int k = 222;
 
testclass::testclass()
{
    std::cout<<"123\n";
}
 
void testclass::print()
{
    std::cout<<"this is testcalss\n";

}//编译工程后就会生成一个.lib文件

使用静态库

需要.h文件,lib文件

(1)设置项目属性–vc++目录–库目录为lib所在的路径;

(2)将lib添加到项目属性–链接器–输入–附加依赖项(或者直接在源代码中加入#pragma comment(lib, “**.lib”));

(3)在源文件中添加.h头文件。

然后就像平常一样调用普通函数、类、变量,举例如下:

#include <iostream>
#include "lib.h"
 
#pragma comment(lib, "lib.lib")
 
int main()
{
    fun(4);
    std::cout<<k<<std::endl;
    testclass tc; 
    tc.print();
    return 0;
}

 

动态编译dll

动态链接则是提供了一个函数的描述信息给可执行文件(并没有内存拷贝),当程序被夹在到内存里开始运行的时候,系统会在底层创建DLL和应用程序之间的连接关系,当执行期间需要调用DLL函数时,系统才会真正根据链接的定位信息去执行DLL中的函数代码。

生成dll文件和lib文件,dll包含实现部分,lib包含索引部分。

Dll的调用过程:在WINDOWS32系统底下,每个进程有自己的32位的线性地址空间,若一个DLL被进程使用,则该DLL首先会被调入WIN32系统的全局堆栈,然后通过内存映射文件方式映射到这个DLL的进程地址空间。若一个DLL被多个进程调用,则每个进程都会接收到该DLL的一个映像,而非多份的拷贝。但,在WIN16系统下,每个进程需要拥有自己的一份DLL空间,可以理解为何静态链接没啥区别。

生成和使用动态库

生成动态库

新建项目–win32项目–填写项目名–确定–下一步–应用程序类型:选择dll–附加选项:选择导出符号–完成。

可以看到生成了一个dllmain.cpp 文件,这是dll应用程序的入口,注意它和普通工程的入口main函数不同,这个文件我们不需要修改。

在这个动态库中我们举例导出一个变量,一个类,一个函数,头文件dll.h如下:

//mydll.h
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#prama comment(lib, "mydll.lib")
#endif
 
//1导出变量,变量在.cpp文件中定义
extern "C" MYDLL_API int ndll;
 
//2导出函数,加extern "C",是为了保证编译时生成的函数名不变,这样动态调用dll时才能
//正确获取函数的地址
extern "C" MYDLL_API int fndll(void);
 
// 3导出类
class MYDLL_API Cdll {
public:
    Cdll(void);
    // TODO: 在此添加您的方法。
};
 
// 4(建议使用)导出接口
class Idll2 {
public:
    virtual bool func() = 0;
    virtual ~Idll2(void) {};
};
 
extern "C" MYDLL_API Idll2* CreateInstance();
//mydll.cpp
#include "stdafx.h"
#include "mydll.h"
 
 
// 这是导出变量的一个示例
MYDLL_API int ndll=6;
//
// 这是导出函数的一个示例。
MYDLL_API int fndll(void)
{
    return 42;
}
 
// 这是已导出类的构造函数。
Cdll::Cdll()
{
    return;
}
 
// 导出接口
class Cdll2:public Idll2
{
public:
    bool func(){return 0;}
};
 
MYDLL_API Idll2* CreateInstance()
{
    return new Cdll2;
}

调用动态库

有两种方法调用动态库,一种隐式链接,一种显示链接。

调用动态库:隐式链接

隐式链接 需要.h文件,dll文件,lib文件

(1)将dll放到工程的工作目录;

(2)设置项目属性–vc++目录–库目录为lib所在的路径;

(3)(直接在源代码中加入#pragma comment(lib, “**.lib”))或者将lib添加到项目属性–链接器–输入–附加依赖项;

(4)在源文件中添加.h头文件。

然后就像平常一样调用普通函数、类、变量

调用动态库:显示链接

显示链接只需要.dll文件,但是这种调用方式不能调用dll中的变量或者类(其实可以调用类,但是相当麻烦,有兴趣者可参考)。

显示调用主要使用WIN32 API函数LoadLibrary、GetProcAddress,举例如下:

typedef int (*dllfun)(void);//定义指向dll中函数的函数指针

HINSTANCE hlib = LoadLibrary("mydll.dll");
    if(!hlib)
    {
        std::cout<<"load dll error\n";
        return -1;
    }
    dllfun fun = (dllfun)GetProcAddress(hlib,"fndll");
    if(!fun)
    {
        std::cout<<"load fun error\n";
        return -1;
    }
    fun();