程式員應該知道的關於Windows API、CRT和STL二三事

來源:互聯網
上載者:User
1.本文編寫目的

    本文是為了協助一些人弄清一些關於Windows API, C運行時程式庫(CRT), 和標準C++庫(STL)的基本概念。有很多人甚至是有經驗的程式員在這些概念上是含糊不清的甚至是有錯誤觀點。如果你想知道他們是在什麼基礎上實現的並且一直沒時間弄清楚這些概念,請花費點時間閱讀下本文。

2. 基本概念

   下面這幅圖代表了WinAPI、CRT和STL三者之間的關係。

    表 1: Windows API、CRT和C++標準庫之間的關係圖


    相鄰的模組之間可以相互連訊。為什麼這麼說呢?下面依照這幅圖從下到上依次說說明。

2.2. 硬體層

    每個硬體部分都有自己的命令集,作業系統通過這些命令集控制硬體並與硬體通訊。不同硬體部分的命令集數量和複雜度差別很大。通常,同樣的硬體如果由不同的硬體廠商實現,它們通常會在標準的命令集外提供一些擴充,使用這些擴充通常可以發揮這些硬體的特性。如果每個程式編寫者都針對這些硬體重寫一遍程式,每個硬體的生產廠家都有很多,那會把碼農們累死滴!於是上帝說,要有作業系統,於是人類 有了一個方便的訪問硬體的統一平台。

2.3 作業系統

    作業系統的目的之一就是把底層硬體的特性給封裝起來,然後提供一個統一的介面,以便於電腦操作人員控制硬體。現代版本的作業系統不再允許應用程式直接存取硬體,作業系統能直接存取硬體層時,稱作業系統處於核心態(kernel mode)。

    一些老版本的作業系統,如MS-DOS,允許編程人員直接存取硬體。雖然這樣做可以使他們寫出來的程式能夠在一段短的時間內應用硬體的某些進階特性,但是長遠來說這樣做對這個程式是有害的,因為這個程式將不能在這個硬體的新版本上運行。

2.4 API(Application Programming Interface)

    現代的應用程式如果想訪問硬體,就必須通過作業系統,具體來說就是使用作業系統提供的API。一個API就是一些功能的統一介面,它能把硬體的這些功能抽象地統一起來,讓程式員集中精力實現他們想要的功能。應用程式已經不可能繞過現代的作業系統直接存取硬體,一種更普遍的說法就是應用程式以使用者態 (user mode)運行。MS Windows提供的API是一群C函數的集合。Windows平台的最低層次的開發語言就是C語言。

2.4.1 平台軟體開發包(Platform Software Development Kit)

    MS發布了一個免費的平台軟體開發包(簡稱之為Platform SDK或PSDK)以促使軟體開發員在windows平台上開發軟體。PSDK包含如下東東:

   1. 包含這些API生命的標頭檔

   2. 用於串連的相應的庫檔案(通過它們能夠在相應的DLL中定位到這些API函數)

   3. 相關的說明文檔

   4. 各種協助工具

    例如,如果要開啟或建立一個檔案,我們要調用函數CreateFile,它在標頭檔"WinBase.h"中聲明,通過庫檔案"Kernel32.lib"在dll檔案中進行函數定位。

    Windows API的函數名稱依照駱駝命名法命名,因此不同的函數很容易區分。常量和宏的命名一般是大寫。在相應的文檔裡,會詳細的說明每個函數所需要的標頭檔、庫檔案以及能夠啟動並執行相關平台。

    一個Windows應用程式能夠調用任何windows的API函數,前提是應用程式能夠正確的找到這個函數的簽名並且能夠連結到相應的庫檔案,或者通過函數GetProcAddress和函數簽名(一般就是函數的名稱)直接連接到它在dll庫檔案中的地址。

2.5 C運行時程式庫

    基於作業系統的API函數,軟體廠商實現了C運行時程式庫(CRT)。CRT由一些標頭檔和相應的源檔案構成,這些檔案實現了一些基礎的公用操作,如字串操作、一些數學運算函數和基本的輸入\輸出等操作。通常的,如果一家廠商發布了一個C編譯器,它會附帶一些CRT庫。一些國際標準組織負責制定C語言的標準並實現一些運行時程式庫。

2.5.1 標準和擴充

    理論上,如果一個程式使用標準C語言開發的,並且有一個平台支援其相應的標準C編譯器和動態庫,那麼這個程式就能在這個平台上運行。但是實際上每個C編譯器開發廠家都會對C語言做一些有利於自己的擴充,程式員可以很方便的利用這些擴充開發程式,但是付出的代價就是這個程式不再具有可移動性。

    CRT的函數名稱一般都是小寫,宏和常數是大寫的,而一些擴充則通常具有一定的以底線作為開頭的標識,例如函數_mkdir。針對這些擴充,相應的文檔都會詳細的予以說明。

2.6 關於Unicode的認知

2.6.1 PSDK支援Unicode

    實際上,以上提到的Win32 API的名稱並不是它們的真實名稱。這些名稱僅僅是一些宏,你可以在PSDK的標頭檔中找到這些宏對用的函數名稱。所以,如果PSDK的文檔提到一個函數,如CreateFile,開發人員應該意識到它僅僅是一個宏。它的真實名稱是CreateFileA和CreateFileW。是的,它代表了“兩個”函數名,而不是一個,是同一個函數在不同Win32函數的兩個不同的版本。以'A'結尾的函數接受ANSI字串,即Unicode字串,即 wchar_ts型字串。兩種版本的函數都在模組kernel32.dll中實現,如果你的編程環境是Unicode則,則宏CreateFile在編譯是會被CreateFileW代替,否則用CreateFileA代替。

    Windows作業系統有三大家族:MS-DOS/9x-based,Windows CE,Windows NT。

       1. MS-DOS/9x-based系列,包括了Windows 1.0-3.11,95,98和Windows ME都給予MS-DOS作業系統。Windows作業系統的早期版本:1.0-2.0和16-bit作業系統。而其後的作業系統,3.0,95,98和 ME則是16位和32位作業系統的混合體。它們實際最低支援16位的作業環境,也能運行一些受限制的32位應用程式。之所以受限制,就是它們只支援 ANSI版本的win32函數。現代這個系列的作業系統已經絕跡了,並且微軟不再維護這些系統。

       2. Windows NT系列始自於上世界90年代的Window NT 3.1,並且包括了以後的Windows NT 4,Windows 2000,Windows XP,Window Vista,以及這些系統的server版本。Windows NT系列是真正的32位作業系統。它們既支援Unicode版本的win32函數,也支援ANSI版本的win32函數。NT系列的作業系統內部的字串是Unicode型字元,ANSI型的Win32 API函數實際上是Unicode版本的函數的封裝。

       3. Windows CE系列則是針對移動和嵌入式裝置開發的系統。它們是32位作業系統。Windows CE只支援Unicode版Win32 API。

2.6.2 PSDK的字串解決方案:TCHARs

    為了避免為不同的windows作業系統開發不同版本的PSDK,微軟制訂了一個統一的字串類型TCHARs。TCHAR以及其他的相應的宏在標頭檔 WinNT.h中有定義。程式員在程式中不需要為使用char還是wchar_t而糾結,只需要使用宏TCHAR就可以了。根據Unicode環境是否存在,編譯器會自動進行相應的轉換。同樣道理,程式員不需要為使用'A'還是'W'型Win32 API函數糾結。

// Generic code

//

LPCTSTR psz = TEXT("Hello World!");

TCHAR szDir[MAX_PATH] = { 0 };

GetCurrentDirectory(MAX_PATH, szDir);


// 如果UNICODE符號沒有被定義

//

const char* psz = "Hello World!";

char szDir[MAX_PATH] = { 0 };

GetCurrentDirectoryA(MAX_PATH, szDir);


//在純32位作業系統上GetCurrentDirectoryA只是一個外封裝,實際工作流程如下:

// 1. 分配一個臨時的固定大小的wchar_t緩衝區。

// 2. 調用實際的工作者:GetCurrentDirectoryW.

// 3. 根據調用線程的active code page ,調用函數WideCharToMultiByte把wchar_t變為char字串。

//    如果一個字串不能找到對應的符號,則用符號'?'代替。


// 如果宏UNICODE被定義

//

const wchar_t* psz = L"Hello World!";

wchar_t szDir[MAX_PATH] = { 0 };

GetCurrentDirectoryW(MAX_PATH, szDir);

//直接調用實際的幹活的,不需要先找包工頭這個中間人

    使用TCHAR可以方便程式員為ANSI和Unicode builds兩種編譯環境寫一份代碼。在今天這個時代,你不可能為舊有的作業系統Windows 9x/Me編寫程式,你可以安全的使用Unicode字串。這樣的一個好處就是Unicode應用程式可以忘記code pages hustle。

    一個記住PSDK字串聲明的簡單辦法如下:

                L P C T STR = const TCHAR* 

                ^ ^ ^ ^ ^ 

                | | | | | 

    Long -------+ | | | | 

    Pointer to ---+ | | | 

    Constant -------+ | | 

    TCHAR ------------+ | 

    STRing -------------+


    有時候L(即"Long")被忽略掉,因為long和short型指標在Win32平台上的區別是過時的。所以PTSTR = "pointer to TCHAR string",也即TCHAR*。

    下面是同一個程式在兩種不同運行環境下的運行結果。第一個是在ANSI環境下的運行結果,第二個則是在Unicode環境下的運行結果。


    20世紀的幼稚的ANSI程式,把一切非英語字元都轉換為'?'。


    現代的Unicode程式則可以識別其他語言的字串。

2.6.3 CRT的字串解決方案:_TCHARs

    PSDK採用了一種泛型文字轉換技術,它能夠根據具體的CRT環境實現文字類型轉換。CRT用了一個額外的標頭檔"tchar.h"來實現這種泛型技術。為了和C語言標準相容,所有非標準的名稱都已底線開頭。所以,CRT使用宏_T來代替在檔案"WinNT.h"中定義的宏TEXT()。CRT的開發人員為了使得這個庫能夠廣泛的應用開來,所以目前CRT能識別三種字元集:

    * SBCS - 單一位元組字元集。這類字元集的字元類型是char。一個char元素儲存一個ASCII字元。不必為每個單獨的工程定義一個字元集。這個字元集來源於上世紀 70年代的C語言,0x00 - 0x7F留給英語字元集合,而剩餘的0x80 - 0xFF就留給非英語字元集合使用,具體的這個集合中每個字元代表什麼含義則由當前的active code page決定。

    * _MBCS - 多位元組字元集。多位元組string字元集也依賴char型字元類型。一個多位元組字元可能佔用一個或兩個char型元素空間儲存。如果要用多位元組字元集,就需要在工程中定義宏_MBCS。宏_MBCS向後相容SBCS模式,並且MS Visual C++ 8.0(2005)以前的版本都預設使用這種字元集。東亞語言,如日語、韓語和中文,一般採用這種字元集。現在,多位元組字元集已經被Unicode字元集代替。過去在Windows 9x/Me平台上,只有多位元組字元集才能處理東亞語言。

    * _UNICODE -統一編碼字元集。這種字元集以來wchar_t字元類型。一個Unicode字元佔用一個wchar_t類型空間。windows上的wchar_t型一般佔用16位元大小,所以在windows上可以使用使用大約65535個不同的字元。從MS Visual C++ 8.0 (2005)以後預設使用這種字元集。

    CRT使用_MBCS和_UNICODE兩個宏來區分多位元組字元集和Unicode字元集。

Diagram #2: The Generic Text Mapping in CRT

// Generic code; names are not standard, hence the leading underscore.

//

_TCHAR message[128] = _T("The time is: ");

_TCHAR* now = _tasctime(&tm);

_tcscat(message, now);

_putts(message);


// What happens if no symbol is defined at all (SBCS).

//

char message[128] = "The time is: ";

char* now = asctime(&tm);

strcat(message, now);

puts(message);


// What happens if _MBCS symbol is defined (Multi-byte Character Set);

// non-standard names are with the leading underscore.

//

char message[128] = "The time is: ";

char* now = asctime(&tm);

_mbscat(message, now);

puts(message);


// What happens if _UNICODE symbol is defined (Unicode Character Set);

// non-standard names are with the leading underscore.

//

wchar_t message[128] = L"The time is: ";

wchar_t* now = _wasctime(&tm);

wcscat(message, now);

_putws(message);

2.7 C++標準庫

    C++程式設計語言有其自己的一套標準庫。這些庫包含了一系列的類和函數,可以在編程中使用。

    通常,人們一提起C++標準庫就下意識地直接聯想到STL。STL是標準模板庫(Standard Template Library)的縮寫。STL最近的版本是作為C++標準庫的一個子集發布的。但是現實中STL已經無處不在了,成了C++標準庫的同義語。

    國際標準組織(IOS,International Organization for Standardization)負責定義C++語言的標準以及其標準庫。

2.7.1 C++標準庫的內容

    C++標準庫大概可以劃分為以下幾個部分:

   1.容器。一般都是一些普通的資料結構,如vector,set,list,map.

   2.迭代器。提供了一種統一的訪問標準容器的方法。

   3. 演算法。實現了一些常用的演算法,這些演算法一般都通過迭代器訪問容器,而不是直接存取容器,所以一種演算法可以作用於不同的容器。

   4. 分配器。負責容器內每個元素的記憶體空間的分配與回收。

   5. 函數對象以及Utilities。利用他們便於演算法作用於各種容器。

   6. 字元流。把輸入/輸出流作為一個統一的對象處理。

   7. C動態運行庫。由於要向後相容C,所以CRT要作為C++標準庫的一部分出現。

2.8 多平台方面的進展

     有時候客觀需求要求一個軟體能在多個電腦平台上運行。程式員可能需要為某個特定的平台開發特定的軟體版本,這種方法不僅過時,而且容易出錯。因為相同的函數模組可能需要真多多種不同的平台實現不同的版本,多以不僅浪費時間而且非常浪費開發資源。

    所以一般的方法就是,編寫的軟體所基於的平台可以在不同的平台運行,它不能調用特定平台的API函數,而且不能使用特定的標準庫開發商提供的標準庫的擴充。這就使得開發非常困難,但是從長遠來看,所有的平台能從軟體的新特性以及改正的bug中受益。

3. 代碼重利用

    有兩種方式可以實現把CRT和C++庫的代碼嵌入一個項目中:靜態連結和動態連結。以下針對這兩種方式的討論,只針對CRT介紹開來,但是這些概念在C++標準庫上也是同義的或者說是近似的。

3.1 靜態連結

    如果靜態連結CRT/C++庫,則程式啟動時這些庫的代碼也一併會被載入緊記憶體中,這種方法的優點和缺點並存。

    優點:

   1. 使用方便。通過這種方式很容易把程式拷貝到目標電腦,並使它運行起來。不用為CRT/C++複雜的使用步驟擔心。

   2. 不需要額外的檔案。通過這種方式,一些小的應用程式可以很方便地把這些庫放進一個可執行檔當中。這種程式可以很容易的從網上下載下來,不用擔心它的完整性遭到破壞。

    缺點:

   1. 不耐用。通過靜態連結方式實現的程式不能區分一個庫的版本,即舊版本和新版本對它來說都是一樣的。

   2. 靜態連結容易導致多米諾效應。當下,一個程式不可能完全由一個組織去實現。現代的軟體項目已經非常複雜,並且開始嚴重依賴第三方的構件和庫。所以一個軟體一般被劃分為若干個耦合關係非常鬆散的模組。如果靜態連結到CRT中的某一部分,將嚴重影響到這些模組之間的互通性,這也將迫使程式員依賴於這些模組之間的最大的公用部分。下面這一章節將詳細的討論相關的細節。

3.1.1 把CRT當做一個黑盒子

    一個問題就是不同的CRT執行個體之間很難共用一個內建的CRT對象,一個CRT執行個體所分配的記憶體空間還必須由這個執行個體來釋放,一個CRT執行個體所開啟的檔案也只能有這個執行個體來操作並完成關閉,等等。這是因為此時CRT只能內部操作這些擷取的資源。任何一個CRT執行個體如果想釋放另一個CRT執行個體的記憶體活通過 FILE*指標訪問另一個執行個體開啟的檔案都將改變被訪問者的狀態甚至導致被訪問者的程式的崩潰。

    所以靜態地使用CRT的程式員一般都要提供額外的函數來釋放他所開發的模組所使用的資源,並且要求調用這個模組的人一定要記住通過這些函數來對這些資源進行釋放,否則將造成記憶體泄露。如果不同的模組通過靜態方式串連到CRT,則這些模組之間就不能共用STL的容器物件或C++對象。下表說明了通過調用 malloc函數來使用一段記憶體緩衝。

      

     上面這個表中,模組1以靜態方式串連到CRT,而模組2和3則以動態方式串連到CRT,模組2和3之間可以相互傳遞CRT所用有的對象。例如,模組3 使用malloc所分配的一段記憶體空間可以由模組2釋放,因為對malloc和free的調用都是由同一個CRT對象實現的。

    但是,模組1的資源就很難由其他模組釋放。模組1所佔有的每個資源都應該由它自己來釋放。因為模組1是以靜態方式連結到CRT的一個執行個體。上面圖中的模組2就必須通過調用模組1所提供的函數來釋放它通過模組1所擷取的記憶體空間。

3.2 動態連結

    如果通過動態連結的方式連結到CRT/C++庫,則程式運行時只需要把很小一部分需要的匯入庫連結到程式中。這些匯入庫包含了說明CRT/C++的相應的函數的具體的實現的地址所在。程式啟動時,程式會根據這些指令的說明把一些相關的dll載入進程式的記憶體空間中來。

    優點:

   1.容易實現模組化。就像前面所述的那樣,一個程式可以通過動態連結很方便的實現模組化。一個程式可以很方便的劃分為若干個模組,並且各個模組之間可以很方便的共用比較進階的對象資料。

   2. 更快的啟動。系統啟動時已經載入了這些CRT DLL,所以當使用這些dll的程式啟動時就無需在載入這些DLL了,而且這不僅節省了記憶體空間,更節省了頁交換的時間。

    缺點:

   1.部署起來比較複雜。這些CRT庫必須被重新被分配一遍,而且為了一個程式能夠工作,這些庫在記憶體中必須有序地放置。這就需要一個額外的啟動項目,並且部署的時候要小心謹慎。

4. 總結

    本文大體上介紹了Windows API、CRT和STL相互之間的關係和依賴順序。對於使用者態的程式來說,Windows API是可以用到的電腦的最低一層。處於Windows API之上的則是C的動態運行庫,它對作業系統進行了封裝,並隱藏了不同的作業系統之間的差異。標準C++庫則提供了更多的功能,並且把CRT作為它的一部分。通過標準的函數以及相關類可以寫出跨平台的程式,這個程式只需在新的平台上重新編譯一次發布出來,代碼不需要改動。

    根據應用程式的客觀需要,它可以靜態活動態連結到C運行時程式庫和標準C++程式庫。每種方法都有它自己的優缺點。

原文是:http://www.codeproject.com/KB/cp ... aspx?display=Mobile

文章的前幾段文字摘引:1. The Purpose

The purpose of this article is to clear the essential points about the Windows API, the C Runtime Library (CRT), and the Standard C++ Library (STL). It is not uncommon that even experienced developers have confusion and hold onto misconceptions about the relationship
between these parts. If you ever wondered what is implemented on top of what and never had a time to figure it out, then keep reading.2. Basics

The following diagram represents the relationship between WinAPI, CRT, and STL.


原文:http://www.cnblogs.com/menggu***yuan/archive/2011/06/09/2075910.html
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.