C/C++ 跨平臺交叉編譯、靜態庫/動態庫編譯、MinGW、Cygwin、CodeBlocks使用原理及連接參數選項

目錄html

0. 引言
1. 交叉編譯
2. Cygwin簡介
3. 靜態庫編譯及使用
4. 動態庫編譯及使用
5. MinGW簡介
6. CodeBlocks簡介

 

0. 引言linux

UNIX是一個註冊商標,是要知足一大堆條件而且支付可觀費用纔可以被受權使用的一個操做系統。linux是unix的克隆版本,是由其創始人Linus和諸多世界知名的黑客手工打造的一個操做系統。爲何linux和unix之間有不少軟件能夠很輕鬆的移植?由於linux也知足POSIX規範,因此在運行機制上跟unix相近。同時,POSIX標準也是Linux、windows下可以進行交叉編譯的基礎ios

0x1: POSIX(Portable Operating System Interface)c++

可移植操做系統接口(Portable Operating System Interface POSIX),是IEEE爲要在各類UNIX操做系統上運行的軟件,而定義API的一系列互相關聯的標準的總稱,其正式稱呼爲IEEE 1003,而國際標準名稱爲ISO/IEC 9945。此標準源於一個大約開始於1985年的項目。POSIX這個名稱是由理查德·斯托曼應IEEE的要求而提議的一個易於記憶的名稱。它基本上是(Portable Operating System Interface可移植操做系統接口)的縮寫,而X則代表其對Unix API的傳承shell

Linux基本上逐步實現了POSIX兼容,但並無參加正式的POSIX認證。微軟的Windows NT聲稱部分實現了POSIX標準,由於有POSIX標準的存在,咱們在unix、linux、windows上進行編程的時候,會發現有不少API都是通用的,雖然大多數狀況下進行跨系統兼容編程是很困難的編程

當前的POSIX主要分爲四個部分windows

1. Base Definitions
2. System Interfaces
3. Shell and Utilities
4. Rationale

0x2: POSIX 1.1標準api

POSIX(Portable Operating System Interface for Computing Systems)是由IEEE 和ISO/IEC 開發的一簇標準。該標準是基於現有的UNIX實踐和經驗,描述了操做系統的調用服務接口,用於保證編制的應用程序能夠在源代碼一級上在多種操做系統上移植運行。
緩存

1. 1003.0
管理POSIX開放式系統環境(OSE)。IEEE在1995年經過了這項標準。ISO的版本是ISO/IEC 14252:1996

2. 1003.1
被普遍接受、用於源代碼級別的可移植性標準。1003.1提供一個操做系統的C語言應用編程接口(API)。IEEE和ISO已經在1990年經過了這個標準,IEEE在1995年從新修訂了該標準。

3. 1003.1b
一個用於實時編程的標準(之前的P1003.4或POSIX.4)。這個標準在1993年被IEEE經過,被合併進ISO/IEC 9945-1

4. 1003.1c
一個用於線程(在一個程序中當前被執行的代碼段)的標準。之前是P1993.4或POSIX.4的一部分,這個標準已經在1995年被IEEE經過,納入ISO/IEC 9945-1:1996

5. 1003.1g
一個關於協議獨立接口的標準,該接口可使一個應用程序經過網絡與另外一個應用程序通信。1996年,IEEE經過了這個標準

6. 1003.2
一個應用於shell和工具軟件的標準,它們分別是操做系統所必須提供的命令處理器和工具程序。1992年IEEE經過了這個標準。ISO也已經經過了這個標準(ISO/IEC 9945-2:1993)

7. 1003.2d
改進的1003.2標準

8. 1003.5
一個至關於1003.1的Ada語言的API。在1992年,IEEE經過了這個標準。並在1997年對其進行了修訂。ISO也經過了該標準

9. 1003.5b
一個至關於1003.1b(實時擴展)的Ada語言的API。IEEE和ISO都已經經過了這個標準。ISO的標準是ISO/IEC 14519:1999

10. 1003.5c
一個至關於1003.1q(協議獨立接口)的Ada語言的API。在1998年,IEEE經過了這個標準。ISO也經過了這個標準。

11. 1003.9
一個至關於1003.1的FORTRAN語言的API。在1992年,IEEE經過了這個標準,並於1997年對其再次確認。ISO也已經經過了這個標準

12. 1003.10
一個應用於超級計算應用環境框架(Application Environment Profile,AEP)的標準。在1995年,IEEE經過了這個標準

13. 1003.13
一個關於應用環境框架的標準,主要針對使用POSIX接口的實時應用程序。在1998年,IEEE經過了這個標準 

14. 1003.22
一個針對POSIX的關於安全性框架的指南

15. 1003.23
一個針對用戶組織的指南,主要是爲了指導用戶開發和使用支持操做需求的開放式系統環境(OSE)框架

16. 2003
針對指定和使用是否符合POSIX標準的測試方法,有關其定義、通常需求和指導方針的一個標準。在1997年,IEEE經過了這個標準

17. 2003.1
這個標準規定了針對1003.1的POSIX測試方法的提供商要提供的一些條件。在1992年,IEEE經過了這個標準

18. 2003.2
一個定義了被用來檢查與IEEE 1003.2(shell和工具API)是否符合的測試方法的標準。在1996年,IEEE經過了這個標準

0x3: POSIX標準的意義
安全

POSIX的意義在於提供了"跨操做系統兼容性編譯"的能力,遵循了POSIX標準的C/C++程序源代碼,能夠直接在Linux/BSD環境下用GCC編譯,或者在windows下用Cygwin/MinGW編譯(Cygwin、MinGW提供了跨操做系統的兼容編譯)。這叫跨操做系統的編譯,注意要和"跨平臺交叉編譯"區分開來

Relevant Link:

http://zh.wikipedia.org/wiki/POSIX
http://i.linuxtoy.org/docs/guide/ch48s05.html

 

1. 交叉編譯 

0x1: 交叉編譯簡介

從編譯所在的平臺和運行所在的平臺這點來看,有兩種編譯概念

1. 本地編譯
咱們常見的軟件開發,都是屬於"本地編譯"。在當前的PC下,x86的CPU下,直接編譯出來程序,能夠運行的程序(或者庫文件),其能夠直接在當前的環境,即x86的CPU下,當前電腦中,運行。
此時的編譯,能夠叫作"本地編譯",即在當前目標平臺下,編譯出來的程序,也只是放到當前平臺下,就能夠運行的

2. 交叉編譯
這是一個和本地編譯相對應的概念。而所謂的"交叉編譯",就是在一種平臺上編譯,編譯出來的程序,是放到別的平臺上運行
即編譯的環境,和運行的環境,不同,屬於交叉的,此所謂cross交叉編譯,這個概念,主要和嵌入式開發有關

一種最多見的例子就是:

在進行嵌入式開發時,手上有個嵌入式開發板,CPU是arm的,而後在x86的平臺下開發,好比Ubuntu的Linux,或者是Win7。而後就須要在x86的平臺上,(用交叉編譯器)去編譯你寫好的程序代碼,編譯生成的(可執行的)程序,是放
到目標開發板,arm的CPU上運行的 此所謂:在x86平臺上編譯,在ARM平臺上運行

交叉編譯,英文常寫做cross compile,也有其餘寫法:crosscompile, cross compiling等

0x2: 爲什麼要有交叉編譯

之因此要有交叉編譯,主要緣由是:

1. 嵌入式系統中的資源太少
交叉編譯出來的程序,所要運行的目標環境中,各類資源,都相對有限,因此很難進行直接的本地編譯,最多見的狀況是:
由於編譯,開發,都須要相對比較多的CPU,內存,硬盤等資源,而嵌入式開發上的那點資源,只夠嵌入式(Linux)系統運行的,沒太多剩餘的資源,供你本地編譯。因此須要在別的平臺上進行跨平臺編譯,而後在其餘的平臺上運行

0x3: 跨平臺編譯和跨操做系統編譯的差異

這裏須要注意的是"平臺"的概念,實際上包含兩個概念

1. 體系結構(Architecture): 同一個體系結構能夠運行不一樣的操做系統
2. 操做系統(Operating System): 同一個操做系統也能夠在不一樣的體系結構上運行

舉例來講,咱們常說的x86 Linux平臺其實是Intel x86體系結構和Linux for x86操做系統的統稱;而x86 WinNT平臺其實是Intel x86體系結構和Windows NT for x86操做系統的簡稱

像crosstool-NG這類交叉編譯器和Cygwin這類跨操做系統平臺編譯器的區別在於

1. crosstool-NG跨平臺編譯(跨體系結構、操做系統)

2. Cygwin跨平臺編譯(提供*inux到windows系統的代碼級編譯兼容性)

Relevant Link:

http://www.crifan.com/files/doc/docbook/cross_compile/release/html/cross_compile.html
http://zh.wikipedia.org/wiki/%E4%BA%A4%E5%8F%89%E7%B7%A8%E8%AD%AF%E5%99%A8
http://baike.baidu.com/view/650389.htm

 

2. Cygwin簡介

0x1: 簡介

Cygwin是許多自由軟件的集合,最初由Cygnus Solutions開發,用於各類版本的Microsoft Windows上,運行類UNIX系統。Cygwin的主要目的是經過"從新編譯"(注意:是從新編譯),將POSIX系統(例如Linux、BSD,以及其餘Unix系統)上的軟件移植到Windows上。Cygwin移植工做在Windows NT上比較好,在Windows 95和Windows 98上,相對差勁一些。目前Cygwin由Red Hat等負責維護

首要須要明白的是,Cygwin不是一個跨平臺模擬器,它不能讓咱們把linux上編譯出來的程序在windows上運行(像wine那樣),而是一個跨平臺的編譯器,也就是提供代碼級的跨操做系統兼容性,咱們在linux下寫的符合POSIX標準的C程序能夠在windows下面進行編譯,Cygwin提供了一套在windows下可使用的Linux的API

Cygwin包括了一套庫,該庫在Win32系統下實現了POSIX系統調用的API。還有一套GNU開發工具集(好比GCC、GDB),這樣能夠進行簡單的軟件開發。還有一些UNIX系統下的常見程序。2001年,新增了X Window System

0x2: Cygwin的特性

Cygwin is:
1. a large collection of GNU and Open Source tools which provide functionality similar to a Linux distribution on Windows.
2. a DLL (cygwin1.dll) which provides substantial POSIX API functionality.

Cygwin is not:
1. a way to run native Linux apps on Windows. You must rebuild your application from source if you want it to run on Windows.
2. a way to magically make native Windows apps aware of UNIX® functionality like signals, ptys, etc. Again, you need to build your apps from source if you want to take advantage 
of Cygwin functionality.

0x3: Cygwin原理

cygnus當初首先把GCC,GDB,GAS等開發工具進行了改進,使他們可以生成並解釋win32的目標文件。而後,他們要把這些工具移植到windows平臺上去。一種方案是基於win32 api對這些工具的源代碼進行大幅修改,這樣作顯然須要大量工做。所以,他們採起了一種不一樣的方法

1. 他們寫了一個共享庫(就是cygwin.dll),把win32 api中沒有的unix風格的調用(如fork、spawn、signals、select、sockets等)封裝在裏面
2. 也就是說,他們基於win32 api寫了一個unix系統庫的模擬層(這個模擬層是一個關鍵,它的底層是win32 api,上層提供unix風格的調用,因此咱們才能夠在windows下編譯unix風格的C程序)
3. 這樣,只要把這些工具的源代碼和這個共享庫鏈接到一塊兒,就可使用unix主機上的交叉編譯器來生成能夠在windows平臺上運行的工具集
4. 以這些移植到windows平臺上的開發工具爲基礎,cygnus又逐步把其餘的工具(幾乎不須要對源代碼進行修改,只須要修改他們的配置腳本)軟件移植到windows上來。這樣,在windows平臺上運行bash和開發工具、用戶工具,感受好
像在unix上工做

0x4: 使用Cygwin編程

code

#iuclude <stdio.h>

main()
{
    printf("hello world!!\n");
}

編譯

gcc hello.c -o hello.exe

獲得hello.exe,這個程序能夠在windows上直接點擊運行

Relevant Link:

http://zh.wikipedia.org/wiki/Cygwin
https://www.cygwin.com/
http://www.ibm.com/developerworks/cn/linux/l-cn-cygwin/

 

3. 靜態庫編譯及使用

0x1: 什麼是庫

庫是寫好的現有的,成熟的,能夠複用的代碼。現實中每一個程序都要依賴不少基礎的底層庫,不可能每一個人的代碼都從零開始,所以庫的存在是頗有必要的
本質上來講庫是一種可執行代碼的二進制形式,能夠被操做系統載入內存執行。庫有兩種

1. 靜態庫(.a、.lib)
2. 動態庫(.so、.dll)

所謂靜態、動態是指"連接"的過程存在區別

0x2: 動態庫和靜態庫的默認路徑PATH搜索順序

庫文件在鏈接(靜態庫和共享庫)和運行(僅限於使用共享庫的程序)時被使用,其搜索路徑是在系統中進行設置的

1. 靜態庫的搜索路徑順序
    1) /lib
    2) /usr/lib  
    3) /etc/ld.so.conf文件中添加庫的搜索路徑
    4) /etc/ld.so.conf.d下新建一個.conf文件,這種方法能夠很靈活地將不一樣軟件的庫搜索路徑區分開來
2. 動態庫的搜索路徑順序
    1) LD_LIBRARY_PATH 
    2) /lib
    3) /usr/lib  
    4) /etc/ld.so.cache(使用ldconfig生成的庫路徑緩存)
    5) /etc/ld.so.conf文件中添加庫的搜索路徑
    6) /etc/ld.so.conf.d下新建一個.conf文件,這種方法能夠很靈活地將不一樣軟件的庫搜索路徑區分開來

0x3: 靜態庫

之因此稱之爲"靜態庫",是由於在連接階段,會將彙編生成的目標文件.o與引用到的庫一塊兒連接打包到可執行文件中。所以對應的連接方式稱爲靜態連接。
從本質上來講,一個靜態庫能夠簡單當作是一組目標文件(.o/.obj文件)的集合,靜態庫與彙編生成的目標文件(.o/.obj)一塊兒連接爲可執行文件
靜態庫和.o文件格式類似。即不少目標文件通過壓縮打包後造成的一個文件

靜態庫特色總結:

1. 靜態庫對函數庫的連接是放在編譯時期完成的
2. 程序在運行時與函數庫再無瓜葛,移植方便,由於代碼已經嵌入到程序裏面了,能夠直接跟着程序走,不存在對外部文件的依賴
3. 浪費空間和資源,由於全部相關的目標文件與牽涉到的函數庫被連接合成一個可執行文件,會增長本來程序的空間

0x4: 靜態庫編程

咱們接下來學習一下如何建立用於C++應用的靜態庫(一個.lib 文件)。 使用靜態庫是重用代碼的一種絕佳方式。 你沒必要在要求功能的每一個應用中從新實現同一例程,而只需將其寫入靜態庫一次,而後從應用引用它們便可。 從靜態庫連接的代碼成爲了應用的一部分,這樣你就沒必要安裝另外一個文件來使用代碼。

1. VS編譯、使用靜態庫

//建立靜態庫項目
1. 在菜單欄上,依次選擇"文件""新建""項目"
2. 在"新建項目"對話框的左窗格中,依次展開"已安裝""模板""Visual C++",而後選擇"Win32"
3. 在中間窗格中,選擇"Win32 控制檯應用程序"
4. 在"名稱"框中爲項目指定名稱,例如 MathFuncsLib。 在"解決方案名稱"框中爲解決方案指定名稱,例如 StaticLibrary。 選擇"肯定"按鈕
5. 在"Win32 應用程序嚮導"對話框的"概述"頁上,選擇"下一步"按鈕
6. 在"應用程序設置"頁的"應用程序類型"下,選擇"靜態庫"
7. 在"應用程序設置"頁的"附加選項"下,清除"預編譯頭"複選框
8. 選擇"完成"按鈕建立項目

MathFuncsLib.h

// MathFuncsLib.h
#ifndef MATHFUNCSLIB_H  
#define MATHFUNCSLIB_H  
  
namespace MathFuncs
{
    class MyMathFuncs
    {
    public:
        // Returns a + b
        static double Add(double a, double b);

        // Returns a - b
        static double Subtract(double a, double b);

        // Returns a * b
        static double Multiply(double a, double b);

        // Returns a / b
        static double Divide(double a, double b);
    };
}  
  
#endif  

MathFuncsLib.cpp

// MathFuncsLib.cpp
// compile with: cl /c /EHsc MathFuncsLib.cpp
// post-build command: lib MathFuncsLib.obj

#include "MathFuncsLib.h"

#include <stdexcept>

using namespace std;

namespace MathFuncs
{
    double MyMathFuncs::Add(double a, double b)
    {
        return a + b;
    }

    double MyMathFuncs::Subtract(double a, double b)
    {
        return a - b;
    }

    double MyMathFuncs::Multiply(double a, double b)
    {
        return a * b;
    }

    double MyMathFuncs::Divide(double a, double b)
    {
        return a / b;
    }
}

編譯靜態庫文件

//編譯此靜態庫
1. 在菜單欄上依次選擇"生成""生成解決方案"
2. 這將建立一個可供其餘程序使用的靜態庫

vs是windows操做系統下的編譯平臺,經過vs編譯獲得的.lib靜態庫只能在windows的程序代碼中使用,主要是編譯器、彙編器和鏈接器的不一樣,所以兩者庫的二進制是不兼容的。文章以後會學習到如何將linux下編譯的靜態庫經過跨平臺編譯連接到windows的程序代碼中

要在其餘程序中使用靜態庫中的功能,必須引用靜態庫才能使用其中的例程

//建立引用靜態庫的 C++ 控制檯應用
1. 在菜單欄上,依次選擇"文件""新建""項目"2. 在左窗格中的"Visual C++"下,選擇"Win32"3. 在中間窗格中,選擇"Win32 控制檯應用程序"4. 在"名稱"框中爲項目指定名稱,例如 MyExecRefsLib。 在"解決方案"旁的下拉列表中選擇"添加到解決方案"。 這會將新項目添加到包含此靜5. 態庫的解決方案。 選擇"肯定"按鈕。
6. 在"Win32 應用程序嚮導"對話框的"概述"頁上,選擇"下一步"按鈕。
7. 在"應用程序設置"頁的"應用程序類型"下,選擇"控制檯應用程序"8. 在"應用程序設置"頁的"附加選項"下,清除"預編譯頭"複選框。
9. 選擇"完成"按鈕建立項目。
 
//在應用中使用靜態庫的功能
1. 在建立一個控制檯應用程序後,一個空的程序已經爲你建立好了。 源文件的名稱與你以前選擇的名稱相同。 在此示例中,源文件名爲 MyExecRefsLib.cpp。
2. 必須引用靜態庫才能使用其中的算術例程。 爲此,請在"解決方案資源管理器"中打開 MyExecRefsLib 項目的快捷菜單,而後選擇"引用"。 在 MyExecRefsLib"屬性頁"對話框中,展開"通用屬性"節點,選擇"框架和引用",而後
選擇"添加新引用"按鈕。 有關"引用"對話框的更多信息,請參見"<Projectname> 屬性頁"對話框 ->"通用屬性"->"框架和引用"3. "添加引用"對話框列出了能夠引用的庫。 "項目"選項卡列出了當前解決方案中的全部項目以及它們包含的全部庫。 在"項目"選項卡上,選中"MathFuncsLib"複選框,而後選擇"肯定"按鈕。 4. 若要引用 MathFuncsLib.h 頭文件,必須修改包含的目錄路徑。 在 MyExecRefsLib"屬性頁"對話框中,依次展開"配置屬性"節點和"C/C++"節點,而後選擇"常規"。 在"附加包含目錄"旁,指定 MathFuncsLib 目錄的路徑或
瀏覽至該目錄。
5. 若要瀏覽至目錄路徑,請打開屬性值下拉列表框,而後選擇"編輯"。 在"附加包含目錄"對話框中,在文本框中選擇一個空行,而後選擇行尾的省略號按鈕 (…)。 在"選擇目錄"對話框中,選擇 MathFuncsLib 目錄,而後選擇"選擇
文件夾
"按鈕以保存所作選擇並關閉對話框。 在"附加包含目錄"對話框中,選擇"肯定"按鈕,而後在"屬性頁"對話框中,選擇"肯定"按鈕以保存對該項目進行的更改。

MyExecRefsLib.cpp

// MyExecRefsLib.cpp
// compile with: cl /EHsc MyExecRefsLib.cpp /link MathFuncsLib.lib

#include <iostream>

#include "MathFuncsLib.h"

using namespace std;

int main()
{
    double a = 7.4;
    int b = 99;

    cout << "a + b = " <<
        MathFuncs::MyMathFuncs::Add(a, b) << endl;
    cout << "a - b = " <<
        MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
    cout << "a * b = " <<
        MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
    cout << "a / b = " <<
        MathFuncs::MyMathFuncs::Divide(a, b) << endl;

    return 0;
}

Relevant Link:

http://msdn.microsoft.com/zh-cn/library/ms235627.aspx#BKMK_CreateLibProject

2. GCC編譯、使用靜態庫

靜態庫的後綴是.a(並無強制規定),它的產生分兩步

1. 由源文件編譯生成一堆.o,每一個.o裏都包含這個編譯單元的符號表
2. ar命令將不少.o轉換成.a,成爲靜態庫,從這點也能夠看出來,庫是不少.o文件的集合

在linux下,庫文件通常放在/usr/lib和/lib下
靜態庫的名字通常爲libxxxx.a,其中xxxx是該lib的名稱
動態庫的名字通常爲libxxxx.so.major.minor,xxxx是該lib的名稱,major是主版本號,minor是副版本號(若是庫的命名不遵循 libXXXXX.a的格式就找不到相應文件)

ldd命令能夠查看一個可執行程序依賴的共享庫 
ldd /bin/ping
    linux-gate.so.1 =>  (0x006cd000)
    libidn.so.11 => /lib/libidn.so.11 (0x005d6000)
    libc.so.6 => /lib/libc.so.6 (0x00927000)
    /lib/ld-linux.so.2 (0x005ac000)

首先,咱們先完成函數庫(靜態庫的代碼)的編碼

hello.h: 函數庫(靜態庫)的頭文件

#ifndef HELLO_H
#define HELLO_H

void hello(const char* name);

#endif

hello.c: 函數庫的實現代碼

#include <stdio.h>

void hello(const char* name)
{
    printf("Hello%s!\n", name);
}

如今,咱們能夠將當前的代碼編譯爲靜態庫文件,須要注意的,靜態庫和可執行在本質上都是可執行代碼,可是靜態庫沒有main主程序,因此不能獨立運行,須要被引入到別的程序中進行運行

//將代碼編譯爲對象文件.o
gcc -c hello.c
//將.o連接爲靜態庫文件
ar rcs libhello.a hello.o

編譯好靜態庫文件以後,咱們就能夠在其餘程序中使用靜態庫文件中的函數了

1. 只須要在使用到這些公用函數的源程序中包含這些公用函數的原型聲明(include對應的頭文件)
2. 而後在用gcc命令生成目標文件時指明靜態庫名
3. gcc將會從靜態庫中將公用函數鏈接到目標文件中
4. 注意,gcc會在靜態庫名前加上前綴lib,而後追加擴展名.a獲得的靜態庫文件名來查找靜態庫文件,所以,咱們在寫須要鏈接的庫時,只寫名字就能夠,如libhello.a的庫,只寫: -lhello

main.c: 調用靜態庫的程序代碼

#include "hello.h"

int main()
{
    hello("LittleHann");
    return 0;
}

編譯

gcc -o hello main.c -L. -lhello

關於gcc的編譯指令,請參閱另外一篇文章

http://www.cnblogs.com/LittleHann/p/3855905.html

Relevant Link:

http://wenku.baidu.com/view/7d8602b265ce050877321301.html

 

4. 動態庫編譯及使用

0x1: 動態庫

動態庫文件名命名規範和靜態庫文件名命名規範相似,也是在動態庫名增長前綴lib,但其文件擴展名爲.so。例如:咱們將建立的動態庫名爲myhello,則動態庫文件名就是libmyhello.so。

接下來咱們繼續學習如何建立用於 C++ 應用程序的動態連接庫 (DLL)。 使用庫是重用代碼的一種絕佳方式。 您沒必要在本身建立的每一個程序中從新實現同一例程,而只需對這些例程寫入一次,而後從須要該功能的應用程序引用它們便可。 經過將代碼放入 DLL,您節省在引用它的每一個應用程序的空間,並且,您能夠更新 DLL,而無需從新編譯全部應用程序

0x2: 動態庫編程

1. VS編譯、使用靜態庫

//建立動態連接庫 (DLL) 項目
1. 在菜單欄上,依次選擇"文件""新建""項目"2. 在"新建項目"對話框的左窗格中,依次展開"已安裝""模板""Visual C++",而後選擇"Win32"3. 在中間窗格中,選擇"Win32 控制檯應用程序"4. 在"名稱"框中爲項目指定名稱,例如,MathFuncsDll。 在"解決方案名稱"框中爲解決方案指定一個名稱,例如 DynamicLibrary。 選擇"肯定"按鈕。
5. 在"Win32 應用程序嚮導"對話框的"概述"頁上,選擇"下一步"按鈕。
6. 在"應用程序設置"頁上的"應用程序類型"下,選擇"DLL"7. 選擇"完成"按鈕建立項目。

編寫動態庫DLL的頭文件,MathFuncsDll.h

// MathFuncsDll.h

#ifndef MATHFUNCSDll_H
#define MATHFUNCSDll_H

/*
當定義了 MATHFUNCSDLL_EXPORTS 符號時,MATHFUNCSDLL_API 符號將在此代碼中的成員函數聲明中設置 __declspec(dllexport) 修飾符,此修飾符使函數能做爲 DLL 導出,以供其餘應用程序調用
當 MATHFUNCSDLL_EXPORTS 未定義時,MATHFUNCSDLL_API 會在成員函數聲明中定義 __declspec(dllimport) 修飾符。 此修飾符可以使編譯器優化從 DLL 導入的用於其餘應用程序的函數
默認狀況下,生成 MathFuncsDll 項目時會定義 MATHFUNCSDLL_EXPORTS
*/
#ifdef MATHFUNCSDLL_EXPORTS
    #define MATHFUNCSDLL_API __declspec(dllexport) 
#else
    #define MATHFUNCSDLL_API __declspec(dllimport) 
#endif

namespace MathFuncs
{
    // This class is exported from the MathFuncsDll.dll
    class MyMathFuncs
    {
    public: 
        // Returns a + b
        static MATHFUNCSDLL_API double Add(double a, double b); 

        // Returns a - b
        static MATHFUNCSDLL_API double Subtract(double a, double b); 

        // Returns a * b
        static MATHFUNCSDLL_API double Multiply(double a, double b); 

        // Returns a / b
        // Throws const std::invalid_argument& if b is 0
        static MATHFUNCSDLL_API double Divide(double a, double b); 
    };
}

#endif

編寫動態庫DLL的函數實現的.cpp文件

// MathFuncsDll.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "MathFuncsDll.h"
#include <stdexcept>

using namespace std;

namespace MathFuncs
{
    double MyMathFuncs::Add(double a, double b)
    {
        return a + b;
    }

    double MyMathFuncs::Subtract(double a, double b)
    {
        return a - b;
    }

    double MyMathFuncs::Multiply(double a, double b)
    {
        return a * b;
    }

    double MyMathFuncs::Divide(double a, double b)
    {
        if (b == 0)
        {
            throw invalid_argument("b cannot be zero!");
        }

        return a / b;
    }
}

編譯後能夠獲得一個.dll文件

編譯獲得一個dll文件後,咱們就在其餘的程序代碼中去引入這個dll文件,並使用其中的函數功能了

//建立引用 DLL 的應用程序
1. 爲了建立一個項目引用你剛剛建立好的DLL,在菜單欄中選擇 文件>新建>項目。
2. 在左窗格中的"Visual C++"下,選擇"Win32"3. 在中間窗格中,選擇"Win32 控制檯應用程序"4. 在"名稱"框中爲項目指定名稱,例如,MyExecRefsDll。 從"解決方案"旁邊的下拉列表中選擇"添加到解決方案"。 這會將新項目添加到包含 DLL 的同一個解決方案中。 選擇"肯定"按鈕。
5. 在"Win32 應用程序嚮導"對話框的"概述"頁上,選擇"下一步"按鈕。
6. 在"應用程序設置"頁的"應用程序類型"下,選擇"控制檯應用程序"7. 在"應用程序設置"頁的"附加選項"下,清除"預編譯頭"複選框。
8. 選擇"完成"按鈕建立項目。

//在應用程序中使用類庫的功能
1. 在建立一個控制檯應用程序後,一個空的程序已經爲你建立好了。 源文件的名稱與你以前選擇的名稱相同。 在本示例中,名爲"MyExecRefsDll.cpp"2. 若要使用您 DLL 中建立的算術例程,必須引用 DLL。 爲此,請在 解決方案資源管理器 中選擇 MyExecRefsDll 項目,而後在菜單欄上,選擇 項目,引用。 在"屬性頁"對話框中,展開"通用屬性"節點,選擇"框架和引用",然
後選擇"添加新引用"按鈕。 有關"引用"對話框的更多信息,請參見"<Projectname> 屬性頁"對話框 ->"通用屬性"->"框架和引用"3. "添加引用"對話框列出了能夠引用的庫。 "項目"選項卡列出了當前解決方案中的全部項目,以及它們包含的全部庫。 在"項目"選項卡上,選中"MathFuncsDll"旁邊的複選框,而後選中"肯定"按鈕。 4. 若要引用 DLL 的頭文件,必須修改包含的目錄路徑。 爲此,請在"屬性頁"對話框中展開"配置屬性"節點,而後展開"C/C++"節點,並選擇"常規"。 在"附加包含目錄"旁邊,指定 MathFuncsDll.h 頭文件所在位置的路徑。 能夠
使用相對路徑(例如 ..\MathFuncsDll\),而後選擇"肯定"按鈕。 5. 如今便可在此應用程序中使用 MyMathFuncs 類。 使用如下代碼替換""的內容:

MyExecRefsDll.cpp

// MyExecRefsDll.cpp
// compile with: /EHsc /link MathFuncsDll.lib

#include <iostream>

#include "MathFuncsDll.h"

using namespace std;

int main()
{
    double a = 7.4;
    int b = 99;

    cout << "a + b = " <<
        MathFuncs::MyMathFuncs::Add(a, b) << endl;
    cout << "a - b = " <<
        MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
    cout << "a * b = " <<
        MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
    cout << "a / b = " <<
        MathFuncs::MyMathFuncs::Divide(a, b) << endl;

    try
    {
        cout << "a / 0 = " <<
            MathFuncs::MyMathFuncs::Divide(a, 0) << endl; 
    }
    catch (const invalid_argument &e) 
    {
        cout << "Caught exception: " << e.what() << endl; 
    }

    return 0;
}

2. GCC編譯、使用靜態庫

foo.h:

#ifndef foo_h__
#define foo_h__
 
extern void foo(void);
 
#endif  // foo_h__

foo.c:

#include <stdio.h>
 
void foo(void)
{
    puts("Hello LittleHann, I'm a shared library");
}

編譯動態庫文件.so

gcc -shared -Wall -Werror -fpic -o libfoo.so foo.c

編譯好動態庫文件以後,咱們就能夠在其餘程序中引入這個動態庫文件.so,並使用其中的導出函數

main.c:

#include <stdio.h>
#include "foo.h"
 
int main(void)
{
    puts("This is a shared library test...");
    foo();
    return 0;
}

在main.c中引入了foo.h頭文件

gcc -L. -Wall -o test main.c -lfoo

編譯成功後,還有一件很重要的事,咱們回想一下Linux下靜態庫、動態庫的默認搜索順序

1) LD_LIBRARY_PATH 
2) /lib
3) /usr/lib  
4) /etc/ld.so.cache(使用ldconfig生成的庫路徑緩存)
5) /etc/ld.so.conf文件中添加庫的搜索路徑
6) /etc/ld.so.conf.d下新建一個.conf文件,這種方法能夠很靈活地將不一樣軟件的庫搜索路徑區分開來

linux是默認不會去搜索當前目錄的,因此咱們必須將.so文件複製到默認路徑下、或者使用LD_LIBRARY_PATH顯示指定

cp libfoo.so /usr/lib
./test
rm -f /usr/lib/libfoo.so

Relevant Link:

http://msdn.microsoft.com/zh-cn/library/ms235636.aspx
http://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html

 

5. MinGW簡介

0x1: MinGW是什麼

MinGW(Minimalist GNU for Windows),又稱mingw32,是將GCC編譯器和GNU Binutils移植到Win32平臺下的產物,包括一系列頭文件(Win32API)、庫和可執行文件

GCC支持的語言大多在MinGW也受支持,其中涵蓋

1. C 
2. Objective-C
3. Fortran
4. Ada
5. 對於C語言以外的語言,MinGW使用標準的GNU運行庫,如C++使用GNU libstdc++ 

可是MinGW使用Windows中的C運行庫。所以用MinGW開發的程序不須要額外的第三方DLL支持就能夠直接在Windows下運行,並且也不必定必須聽從GPL許可證。這同時形成了MinGW開發的程序只能使用Win32API和跨平臺的第三方庫,而缺乏POSIX支持 ,大多數GNU軟件沒法在不修改源代碼的狀況下用MinGW編譯

GCC是一個本來用於Unix系統下編程的編譯器。不過,如今GCC也有了許多Win32下的移植版本,目前GCC在windows下有三個移植版本

1. MinGW
2. Cygwin
3. Djgpp

MinGW是Minimalistic GNU for Windows 的縮寫。它是一個創建在GCC和binutils 項目上的編譯器系統。和其餘GCC的移植版相比,它能夠說是最接近Win32的一個了。由於,MinGW幾乎支持全部的Win32 API,這也是MinGW的特點之一。它所鏈接的程序,不須要任何第三方庫就能夠運行了。在某種程度上看,MinGW更像是VC的替代品

0x3: MinGW的Linker參數

0x3: 編程示例

Relevant Link:

http://www.mingw.org/
https://code.google.com/p/msys-cn/wiki/ChapterThree
http://wenku.baidu.com/view/c6f71522af45b307e87197a6.html

6. CodeBlocks簡介

Relevant Link:

http://bbs.chinaunix.net/thread-3640636-1-1.html
http://zh.wikipedia.org/wiki/Code::Blocks
http://blog.csdn.net/wtfmonking/article/details/17487705

 

Copyright (c) 2014 LittleHann All rights reserved

相關文章
相關標籤/搜索