說明:(1)轉載請註明出處:http://www.cnblogs.com/opangle/p/4298155.html
(2)以下以VS2013爲例,並假設VC安裝路徑爲%VC_INSTALL_PATH%(本人的安裝目錄爲D:\Program Files (x86)\Microsoft Visual Studio 12.0)。
一、環境配置
■ 方法一:使用MS提供的Developer Command Prompt快捷方式
“開始” => “Visual Studio 2013” => “Visual Studio Tools” => “Developer Command Prompt for VS2013”。
■ 方法二:使用MS提供的vcvarsall.bat腳本
在命令行窗口中進入%VC_INSTALL_PATH%目錄,執行“vcvarsall.bat”腳本。
■ 方法三:手動配置環境變量
手動配置環境變量,至少要設置一下三個環境變量:
※ PATH:默認情況下cl命令(微軟編譯器)是不可以使用的,需要將cl.exe文件所在的路徑(即%VC_INSTALL_PATH%\bin目錄)添加到PATH環境變量中。
※ INCLUDE:默認情況下cl命令不知道從何處查找系統頭文件的,該環境變量告訴cl命令從何處查找系統頭文件。
※ LIB:與INCLUDE環境變量類似,LIB環境變量用來告訴鏈接器:從何處查找庫文件、目標文件等。
本人試過的最小配置如下:
環境變量 |
配置說明(多個路徑之間用分號分隔) |
PATH |
將以下路徑加入PATH環境變量中: %VC_INSTALL_PATH%\bin |
INCLUDE |
將以下路徑加入INCLUDE環境變量中: %VC_INSTALL_PATH%\include C:\Program Files (x86)\Windows Kits\8.1\include\shared C:\Program Files (x86)\Windows Kits\8.1\include\um |
LIB |
將以下路徑加入到LIB環境變量中: %VC_INSTALL_PATH%\lib C:\Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x86 |
注:最完整的配置方式可以參考“方法一”、“方法二”中的環境變量配置方式。
■ 測試環境配置
寫一個簡單的“Hello World”程序(假設爲hello.cpp),在命令行下執行:cl hello.cpp,如果能成功生成hello.exe可執行文件,即說明配置成功!
二、常用的命令行選項
下表列出了一些常用的命令行選項,並同時列出了gcc中相對應的選項,熟悉gcc的朋友可以不用看“說明”應該也能明白各選項的含義和作用。
MSVC |
gcc |
說明 |
/E |
-E |
輸出預處理結果 |
/Dname /Dname=value |
-Dname -Dname=value |
定義一個宏 |
/Idirecotry |
-Idirecotry |
指定頭文件搜索路徑 |
/c |
-c |
編譯、彙編生成目標文件 |
/libpath:direcotry |
-Ldirecotry |
指定庫文件搜索路徑(MSVC的/libpath屬於鏈接選項,第一個鏈接選項之前要指定/link選項,用來告訴編譯器驅動,後續選項傳給鏈接器使用) |
另外,如果覺得每次編譯都要使用同樣的選項,敲太長的命令實在是一件累人的事,微軟同樣爲大家提供了省事的方式:仍然是設置環境變量。詳細說明如下:
※ CL環境變量:其中可以指定多個常用的選項,cl命令會自動將該環境變量的內容加入編譯命令。
※ LINK環境變量:鏈接器會自動將該環境變量中的內容加入到鏈接命令中。
三、靜態庫的創建與使用
■ 創建靜態庫
linux下創建靜態庫(*.a)通常需要藉助ar命令,例如:“ar -r mylib.a foo.o bar.o”。微軟也提供了類似的命令——lib命令,該命令的詳細使用方法可參考其幫助文檔“lib /?”,下面僅給出一個簡單的示例:
cl /c foo.cpp cl /c bar.cpp lib foo.obj bar.obj /out:mylib.lib
注:其中“/out選項”用於指定生成的靜態庫文件名。
■ 使用靜態庫
測試目錄結構如下:
. +-- lib | +-- mylib.lib | +-- mylib.h +-- test +-- test.cpp
測試代碼如下:
// mylib.h extern "C" void foo(); // test.cpp #include <mylib.h> int main() { foo(); return 0; }
在test目錄下編譯test.cpp文件:
cl /I..\lib test.cpp /link /libpath:..\lib mylib.lib
其中/link選項用於告訴cl命令:後續選項爲鏈接選項,請從..\lib目錄查找mylib.lib庫文件!
除了使用命令行選項告訴鏈接器應該鏈接的庫、從何處查找庫,還可以使用“#pragma comment”指令。
例1(編譯命令cl /I..\lib test.cpp /link /libpath:..\lib):
#include <mylib.h> #pragma comment(lib, "mylib.lib") // 此處包含庫名信息,因此命令行選項中不需指定庫名 int main() { foo(); return 0; }
例2(編譯命令cl /I..\lib test.cpp):
#include <mylib.h> #pragma comment(lib, "..\\lib\\mylib.lib") // 此處包含庫名及路徑信息,注意轉義符用“\\” int main() { foo(); return 0; }
四、動態庫的創建與使用
與靜態庫的創建相比,動態庫的創建相對複雜。爲了更容易理解,這裏先介紹一些關於windows動態鏈接庫(DLL)基本概念,然後再介紹動態庫的創建和使用方法。
■ 基本概念一:動態庫的分類
微軟將動態庫分爲四類:
※ 非MFC動態鏈接庫(Non-MFC DLL)
※ 靜態鏈接MFC的正規DLL(Regular DLLs statically linked to MFC)
※ 動態鏈接MFC的正規DLL(Regular DLLs dynamically linked to MFC)
※ 擴展DLL(Extension DLLs)
其中只有第一種動態庫不需要鏈接微軟的MFC,後三種都需要鏈接MFC(不管你是否使用MFC),個人感覺微軟這樣做,似乎有點將自己的產品強加於人的感覺,當然,這純屬本人的個人想法,也許有人真的需要MFC吧。這裏需要特別說明的是:下文中所提到的“動態庫”,特指上述第一種動態庫,而不再贅述成“非MFC動態鏈接庫(Non-MFC DLL)”了。
■ 基本概念二:符號的導出與導入
符號的導出與導入的分兩個方向:
※ 從動態庫導出符號:生成導出符號表;
※ 嚮應用程序導入符號:告知鏈接器——我需要的符號來自某個動態庫。
下面首先介紹符號的導出,再介紹符號的導入。
windows下的動態庫文件(.dll)與可執行文件(.exe)在文件的組織結構上最大的區別在於:動態庫文件中包含一個導出符號表。只有存在於該導出符號表中的符號(名字)纔可以被其它程序直接訪問,我們可以使用dumpbin命令來查看一個動態庫的導出符號表,例如:
dumpbin /exports mylib.dll
在動態庫中導出符號有兩種方式:
(1) 創建模塊定義文件(.def)(Exporting a symbol from DLL by ordinal):
優點:可以減小導出符號表的大小;
缺點:當導出C++函數時,需要使用名字修飾後的符號名。
(2) 使用__declspec(dllexport)關鍵字(Exporting a symbol from DLL by name)。
優點:不用考慮名字修飾的問題;
缺點:將符號名存儲在導出符號表中,當導出內容較多時,會導致符號表變得非常龐大。
■ 創建動態庫
示例1:創建模塊定義文件來導出函數
// foo.cpp #include <stdio.h> extern "C" void foo() { printf("foo()\n"); } // bar.cpp #include <stdio.h> extern "C" void bar() { printf("bar()\n"); } // mylib.def LIBRARY mylib EXPORTS foo @1 bar @2
編譯生成動態庫:
cl foo.cpp bar.cpp /link /dll /def:mylib.def /out:mylib.dll
示例2:使用__declspec(dllexport)關鍵字來導出函數
// foo.cpp __declspec(dllexport) void foo() { printf("foo()\n"); } // bar.cpp __declspec(dllexport) void __stdcall bar() { printf("foo()\n"); }
編譯生成動態庫:
cl foo.cpp bar.cpp /link /dll /out:mylib.dll
注:上述兩個示例中,無論使用哪種方式來導出符號,最終都會生成以下三個文件:
※ mylib.dll:動態鏈接庫;
※ mylib.lib:導入庫,後面“使用動態庫”一節中“使用加載時動態鏈接”的示例中會用到。
※ mylib.exp:暫未知。
■ 使用動態庫
示例1:加載時動態鏈接(Using Load-Time Dynamic Linking)
extern "C" __declspec(dllimport) void foo(); extern "C" void bar(); // __declspec(dllimport)並不是必須的 int main() { foo(); bar(); return 0; }
編譯鏈接上述代碼時需要鏈接導入庫mylib.lib,例如:
cl test.cpp /link /libpath:..\lib mylib.lib
示例2:運行時動態鏈接(Using Run-Time Dynamic Linking)
#include <windows.h> typedef void (FunType)(void); int main() { FunType* pfoo, *pbar; HINSTANCE dll = LoadLibrary(TEXT("mylib.dll")); pfoo = (FunType*)GetProcAddress(dll, "foo"); pbar = (FunType*)GetProcAddress(dll, "bar"); pfoo(); pbar(); FreeLibrary(dll); return 0; }
編譯鏈接上述代碼時需要鏈接導入庫mylib.lib,例如:
cl test.cpp
注:在運行上述兩個示例中生成的test.exe可執行文件時,都需要拷貝一份mylib.dll到test目錄下,或者將mylib.dll所在的路徑加入PATH環境變量中,否則操作系統不知道從何處查找mylib.dll。
五、關於nmake和Makefile
與GNU make類似,VC安裝目錄下還自帶了nmake工具,Makefile的書寫形式也與linux下類似。下面給出一個簡單的示例(示例文件名爲Makefile):
CXX = cl.exe LD = link.exe default: mylib.dll mylib.dll: foo.obj bar.obj $(LD) foo.obj bar.obj /dll /out:mylib.dll foo.obj: foo.cpp $(CXX) /c foo.cpp bar.obj: bar.cpp $(CXX) /c bar.cpp
執行: nmake,即可編譯生成mylib.dll動態庫文件。關於nmake的更多說明可參考:“nmake /?”。
六、參考文檔
Setting the Path and Environment Variables for Command-Line Builds
https://msdn.microsoft.com/en-us/library/f2ccy3wt.aspx
CL Environment Variables
https://msdn.microsoft.com/en-us/library/kezkeayy.aspx
LINK Environment Variables
https://msdn.microsoft.com/en-us/library/6y6t9esh.aspx
Compiler Options Listed by Category
https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
Kinds of DLLs
https://msdn.microsoft.com/en-us/library/9se914de.aspx
Importing and Exporting
https://msdn.microsoft.com/en-us/library/9h658af8.aspx
Exporting from a DLL
https://msdn.microsoft.com/en-us/library/z4zxe9k8.aspx
Importing into an Application
https://msdn.microsoft.com/en-us/library/kh1zw7z7.aspx
Using Load-Time Dynamic Linking
https://msdn.microsoft.com/en-us/library/ms686923.aspx
Using Run-Time Dynamic Linking
https://msdn.microsoft.com/en-us/library/ms686944.aspx
NMAKE Reference
https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx