天天看點

動态編譯與靜态編譯動态編譯與靜态編譯

動态編譯與靜态編譯

動态編譯

動态編譯是某些程式語言在執行時用來增進效能的方法。盡管這技術源于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();

繼續閱讀