【C++百科】C++標准庫到底是什么?


C/C++ 標准庫

在學習 C/C++ 的日子里,我們經常會有一個困惑:我們在代碼里使用的標准庫函數和類都是哪里來的?誰實現了它們?它們是打包好在操作系統里了嗎?有沒有官方的 C/C++ 手冊呢?

在這篇文章里,我會通過講述C和C++的一些核心概念以及庫函數實現等,盡力去回答這些問題。

C和C++是如何被創造出來的

當我們在談論C和C++時,我們實際上是在談論一系列的規則,這些規則定義了這個語言應該做什么,怎么做,以及需要提供什么樣的功能。C/C++編譯器必須遵循這些規則,從而處理C/C++源代碼以生成可執行程序。這聽起來與HTML很相似:HTML是瀏覽應該遵循的一系列指令從而使其能夠以一種確定的方式渲染出網頁。

與HTML類似,C/C++語言中的規則也是理論性的。國際標准化組織(ISO)的一大群人每年聚會幾次,討論並在紙上定義語言規則。是的,C/C++是標准化的產物。他們最后會產出一本叫做 「標准」(Standard)的白皮書,並且你可以從他們的網站上買到。隨着語言的不斷發展,每次發布一版白皮書,都會定義一個新標准,這也是為什么我們有不同的C和C++版本:C99,C11,C++03,C++11,C++14等等,這里的數字代表的是發布年份。

這些「標准」是非常細節化的技術文檔:所以我們一般不會把它當作書籍去閱讀。它通常被分為兩部分:

  1. C/C++特性和功能。
  2. C/C++ API —— 一開發人員在其 C/C++ 程序中使用的一系列類,函數和宏的集合。

例如,下面是從C標准中第一部分中節選的一部分,這里定義了main函數的結構。
在這里插入圖片描述

下面則是從同一份標准中節選的對一個 C API - fmin函數的描述。
在這里插入圖片描述

正如我們所看到的,在這份標准里是幾乎沒有代碼的。所以必須有人閱讀這份標准,然后將其實現成計算機能夠使用的形式。這也就是致力於「編譯器」和「標准庫實現」的人們所做的事情:前者制作一個可以讀取和處理C和C++源文件的工具,后者則將標准庫轉換為代碼。下面讓我們深入看一看。

C 標准庫

C標准庫(C Standard Library / ISO C Library),是用於諸如輸入/輸出處理,字符串處理,內存管理,數學計算和許多其他操作系統服務之類任務的宏,類型和函數的集合。它是在C標准中(例如C11標准)中被指定的。它的內容分布在不同的頭文件中,例如上文提到過的math.h

C++ 標准庫

與 C標准庫 的概念相似,只不過是針對C++的。C++標准庫是一系列的C++模板,其中提供了一些常用的數據結構和函數,例如列表,棧,數組,常用算法,迭代器,以及其他你能想到的C++組件。同時,C++標准庫還包含了C標准庫,並且C++標准中也指定了C標准。

C/C++ 標准庫的實現

現在我們來討論真正的代碼。致力於標准庫實現的程序員們在閱讀官方C/C++標准后,用代碼將它們實現出來。它們必須依賴操作系統所提供的函數(讀寫文件,內存分配,線程創建等系統調用)來實現標准庫,因此每一個操作系統都有自己的標准庫實現,有時它是系統的一個核心部分,有時它作為附加部分——編譯器,必須單獨下載安裝。

NU/Linux 實現

GNU C Library,也被稱作glibc,是GNU工程對C標准庫的實現。並且,不是所有的標准C函數都能在glibc中找到:大部分數學函數都在另一個叫做libm的庫中實現。

雖然在今天,glibc是Linux中使用最為廣泛的C標准庫,然后,在90年代有一段時間,它有一個叫做Linux libc(or just libc)的競爭對手,這個對手是從glibc 1.x版本中fork出來的。在那段時間里,libc是許多Linux發行版中的C標准庫實現。

在多年的發展后,glibc開始比Linux libc要優秀的多,因此所有的Linux發行版都用回了glibc。因此如果你現在在你的Linux系統中發現了名叫libc.so.6的文件,不要擔心,它並不是Linux libc,而是現代的glibc,之所以將版本號加到6,就是因為要與之前的Linux libc進行區分。而之所以不叫glibc.so.6的原因則是所有的Linux庫都必須以lib開頭。

另一方面,C++標准庫實現則是在libstdc++中,也叫The GNU Standard C++ Library,這是一個在 GNU/Linux 上實現標准C++庫的正在進行的項目。通常,默認情況下,所有常用的Linux發行版都將使用libstdc++

Mac和iOS中的實現

在Mac和iOS中,C標准庫的實現在libSystem中,這是一個位於/usr/lib/libSystem.dylib文件中的核心庫。libSystem中還有一些其他的部分,例如math庫,thread庫以及其他底層函數。

而對於C++標准庫而言,在 OS X Mavericks(version 10.9)之前,默認使用libstdc++. 這與現代Linux系統中的實現是相同的。而從 OS X Mavericks開始,蘋果轉向了libc++,一個LLVM工程中用來代替GNU libstdc++的C++標准庫實現。

ios開發者可以使用 iOS SDK( Software Development Kit)來調用標准庫(iOS SDK 是用來開發iOS應用的一套工具)。

Windows中的實現

在Windows中,標准庫的實現一直嚴格綁定到Visual Studio中。他們稱其為 C/C++ Run-time Library(CRT),其中同時實現了C和C++標准庫。

在很早的時候,CRT的實現在名為CRTDLL.DLL文件中(當時應該沒有C++標准庫)。從Windows95開始,微軟開始把它封裝為MSVCRT[version-number].DLL(例如 MSVCRT20.DLL, MSVCRT70.DLL 等等),這時大概包含了C++標准庫。大約在1997年,他們決定將名稱簡化為MSVCRT.DLL,不幸的是,這導致了令人討厭的DLL混亂。因此,從 Visual Studio version 7.0 開始,他們又換回了帶上版本號的表示方式。

Visual Studio 2015 帶來了一次深度的CRT重構。C/C++標准庫實現移動到了一個新的庫中,the Universal C Runtime Library(Universal CRT or UCRT). UCRT現在是Windows的組件,從Windows10開始作為操作系統的一部分提供。

Android中的實現

Bionic是Google為其安卓操作系統編寫的C標准庫實現。第三方開發者能夠通過Android Native Development Kit(NDK)來訪問Bionic(NDK是能夠讓你使用C/C++來編寫安卓應用的一套工具)。

而對於C++而言,NDK 則提供了幾種實現:

  1. libc++,安卓現在所使用的官方C++標准庫。從 NDK Release 17 開始,它將成為NDK中唯一支持的C++標准庫。
  2. gnustl,這是libstdc++的別名,也就是 GNU/Linux 中使用的C++標准庫。在安卓中這個庫的使用已經不建議了,並且它將從NDK Release 18 開始被移除。
  3. STLPort,這是一個由 STLport 項目 實現的C++標准庫,從2008年開始就不活躍了。與gnustl類似,這個庫也會在 NDK Release 18 中被移除。

我可以替換默認的標准庫實現嗎?

通常來說,如果我們在資源極其有限的系統上工作,那么可能會需要一個不同的C標准庫實現。僅舉幾例,包括uClibc-ngmusl libcDiet libc,它們都致力於在嵌入式Linux系統中進行開發,以提供較小的二進制文件和較小的內存使用。

C++標准庫也有不同的實現方式:例如 Apache C++標准庫,uSTL 或 EASTL 等。因為考慮到開發速度,最后兩個實際上只實現了模板部分,而沒有實現整個庫。而Apache版本則側重於可移植性。

如果我不使用標准庫呢?

不使用標准庫十分簡單:只要不引入任何它的頭文件即可。然而,為了讓你的程序能夠做到某些功能,你需要通過系統調用直接與操作系統打交道。正如我之前所說的,這本來是標准庫中用於實現這些功能的方法。並且很有可能你還需要使用匯編語言來與硬件借口打交道。

如果這聽起來讓你感到興奮,那么我可以告訴你,Internet 上的某些人正在嘗試編寫不使用標准庫的工作程序。用這種方式寫程序,你會失去可移植性,因為你使用了操作系統本身提供的系統調用函數。然而使用這種方式編程可能會使你學到很多知識,並且讓你在使用抽象庫的時候真正知道自己在干什么。

除了學習之外,你在為嵌入式系統編程的時候也不會想包含標准庫:在有限的內存下,每一個字節都很重要,因此你更傾向於編寫更多的匯編代碼,因為這時的代碼不需要可移植性。The demoscene是一個項目,在其中人們努力將精美的視聽演示內容放入有限大小的程序二進制文件中 —— 4K仍然不是最低限度:一些演示機構組織1K,256字節,64字節甚至32字節的比賽,其中是不許使用標准庫的。

 

C++標准庫都包含哪些部分?

簡單地說,C++   標准庫包含了三個部分:C   標准庫的   C++   版本;C++   IO   庫;C++   STL     
IO   庫最常用的   HEADER   是   <IOSTREAM>   頭文件   
STL   包括了很多容器類(vector,   list,   deque,   stack...),還有   functinal,   algorithm,   iterator   等   
    
C   標准庫的   C++   版本:設原來頭文件是   <*.h>   則   C++   標准頭文件是   <c*>   
    
C++   98   STD   版本的標准庫頭文件一概沒有   .h   后綴,並且把幾乎所有內容都加入了   namespace   std,需要   unsing   指令才能使用。

 

C++標准庫的所有頭文件都沒有擴展名。C++標准庫的內容總共在50個標准頭文件中定義,其中18個提供了C庫的功能。<cname>形式的標准頭文件【<complex>例外】其內容與ISO標准C包含的name.h頭文件相同,但容納了C++擴展的功能。在<cname>形式標准的頭文件中,與宏相關的名稱在全局作用域中定義,其他名稱在std命名空間中聲明。在C++中還可以使用name.h形式的標准C庫頭文件名。

C++標准庫的內容分為10類:

C1.語言支持 C2.輸入/輸出 C3.診斷 C4.一般工具 C5.字符串 

C6.容器 C7.迭代器支持 C8.算法 C9.數值操作 C10.本地化



C1 標准庫中與語言支持功能相關的頭文件 頭文件  描述  
<cstddef> 定義宏NULL和offsetof,以及其他標准類型size_t和ptrdiff_t。與對應的標准C頭文件的區別是,NULL是C++空指針常量的補充定義,宏offsetof接受結構或者聯合類型參數,只要他們沒有成員指針類型的非靜態成員即可。 
<limits> 提供與基本數據類型相關的定義。例如,對於每個數值數據類型,它定義了可以表示出來的最大值和最小值以及二進制數字的位數。 
<climits> 提供與基本整數數據類型相關的C樣式定義。這些信息的C++樣式定義在<limits>中 
<cfloat> 提供與基本浮點型數據類型相關的C樣式定義。這些信息的C++樣式定義在<limits>中 
<cstdlib> 提供支持程序啟動和終止的宏和函數。這個頭文件還聲明了許多其他雜項函數,例如搜索和排序函數,從字符串轉換為數值等函數。它與對應的標准C頭文件stdlib.h不同,定義了abort(void)。abort()函數還有額外的功能,它不為靜態或自動對象調用析構函數,也不調用傳給atexit()函數的函數。它還定義了exit()函數的額外功能,可以釋放靜態對象,以注冊的逆序調用用atexit()注冊的函數。清除並關閉所有打開的C流,把控制權返回給主機環境。 
<new> 支持動態內存分配 
<typeinfo> 支持變量在運行期間的類型標識 
<exception> 支持異常處理,這是處理程序中可能發生的錯誤的一種方式 
<cstdarg> 支持接受數量可變的參數的函數。即在調用函數時,可以給函數傳送數量不等的數據項。它定義了宏va_arg、va_end、va_start以及va_list類型 
<csetjmp> 為C樣式的非本地跳躍提供函數。這些函數在C++中不常用 
<csignal> 為中斷處理提供C樣式支持 

C2 支持流輸入/輸出的頭文件 頭文件  描述  
< iostream> 支持標准流cin、cout、cerr和clog的輸入和輸出,它還支持多字節字符標准流wcin、wcout、wcerr和wclog。 
<iomanip> 提供操縱程序,允許改變流的狀態,從而改變輸出的格式。 
<ios> 定義iostream的基類 
<istream> 為管理輸出流緩存區的輸入定義模板類 
<ostream> 為管理輸出流緩存區的輸出定義模板類 
<sstream> 支持字符串的流輸入輸出 
<fstream> 支持文件的流輸入輸出 
<iosfwd> 為輸入輸出對象提供向前的聲明 
<streambuf> 支持流輸入和輸出的緩存 
<cstdio> 為標准流提供C樣式的輸入和輸出 
<cwchar> 支持多字節字符的C樣式輸入輸出 

C3 與診斷功能相關的頭文件 頭文件 描述 
<stdexcept> 定義標准異常。異常是處理錯誤的方式 
<cassert> 定義斷言宏,用於檢查運行期間的情形 
<cerrno> 支持C樣式的錯誤信息 

C4 定義工具函數的頭文件 頭文件 描述 
<utility> 定義重載的關系運算符,簡化關系運算符的寫入,它還定義了pair類型,該類型是一種模板類型,可以存儲一對值。這些功能在庫的其他地方使用 
<functional> 定義了許多函數對象類型和支持函數對象的功能,函數對象是支持operator()()函數調用運算符的任意對象 
<memory> 給容器、管理內存的函數和auto_ptr模板類定義標准內存分配器 
<ctime> 支持系統時鍾函數 

C5 支持字符串處理的頭文件 頭文件 描述 
<string> 為字符串類型提供支持和定義,包括單字節字符串(由char組成)的string和多字節字符串(由wchar_t組成) 
<cctype> 單字節字符類別 
<cwctype> 多字節字符類別 
<cstring> 為處理非空字節序列和內存塊提供函數。這不同於對應的標准C庫頭文件,幾個C樣式字符串的一般C庫函數被返回值為const和非const的函數對替代了 
<cwchar> 為處理、執行I/O和轉換多字節字符序列提供函數,這不同於對應的標准C庫頭文件,幾個多字節C樣式字符串操作的 一般C庫函數被返回值為const和非const的函數對替代了。 
<cstdlib> 為把單字節字符串轉換為數值、在多字節字符和多字節字符串之間轉換提供函數 

C6 定義容器類的模板的頭文件 頭文件 描述 
<vector> 定義vector序列模板,這是一個大小可以重新設置的數組類型,比普通數組更安全、更靈活 
<list> 定義list序列模板,這是一個序列的鏈表,常常在任意位置插入和刪除元素 
<deque> 定義deque序列模板,支持在開始和結尾的高效插入和刪除操作 
<queue> 為隊列(先進先出)數據結構定義序列適配器queue和priority_queue 
<stack> 為堆棧(后進先出)數據結構定義序列適配器stack 
<map> map是一個關聯容器類型,允許根據鍵值是唯一的,且按照升序存儲。multimap類似於map,但鍵不是唯一的。 
<set> set是一個關聯容器類型,用於以升序方式存儲唯一值。multiset類似於set,但是值不必是唯一的。 
<bitset> 為固定長度的位序列定義bitset模板,它可以看作固定長度的緊湊型bool數組 

C7 支持迭代器的頭文件 頭文件 描述 
<iterator> 給迭代器提供定義和支持 

C8 有關算法的頭文件 頭文件 描述 
<algorithm> 提供一組基於算法的函數,包括置換、排序、合並和搜索 
<cstdlib> 聲明C標准庫函數bsearch()和qsort(),進行搜索和排序 
<ciso646> 允許在代碼中使用and代替&& 

C9 有關數值操作的頭文件 頭文件 描述 
<complex> 支持復雜數值的定義和操作 
<valarray> 支持數值矢量的操作 
<numeric> 在數值序列上定義一組一般數學操作,例如accumulate和inner_product 
<cmath> 這是C數學庫,其中還附加了重載函數,以支持C++約定 
<cstdlib> 提供的函數可以提取整數的絕對值,對整數進行取余數操作 

C10 有關本地化的頭文件 頭文件 描述 
<locale> 提供的本地化包括字符類別、排序序列以及貨幣和日期表示。 
<clocale> 對本地化提供C樣式支持 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM