C運行時庫詳解

運行時庫是程序在運行時所需要的庫文件,通常運行時庫是以LIB或DLL形式提供的。C運行時庫誕生於20世紀70年代,當時的程序世界還很單純,應用程序都是單線程的,多任務或多線程機制在此時還屬於新觀念。所以這個時期的C運行時庫都是單線程的。

  隨着操作系統多線程技術的發展,最初的C運行時庫無法滿足程序的需求,出現了嚴重的問題。C運行時庫使用了多個全局變量(例如errno)和靜態變量,這可能在多線程程序中引起衝突。假設兩個線程都同時設置errno,其結果是後設置的errno會將先前的覆蓋,用戶得不到正確的錯誤信息。

  因此,Visual C++提供了兩種版本的C運行時庫。一個版本供單線程應用程序調用,另一個版本供多線程應用程序調用。多線程運行時庫與單線程運行時庫有兩個重大差別:

  (1)類似errno的全局變量,每個線程單獨設置一個;

  這樣從每個線程中可以獲取正確的錯誤信息。

  (2)多線程庫中的數據結構以同步機制加以保護。

  這樣可以避免訪問時候的衝突。

  Visual C++提供的多線程運行時庫又分爲靜態鏈接庫和動態鏈接庫兩類,而每一類運行時庫又可再分爲debug版和release版,因此Visual C++共提供了6個運行時庫。如下表:

C運行時庫 庫文件
Single thread(static link)   libc.lib
Debug single thread(static link)   libcd.lib
MultiThread(static link)   libcmt.lib
Debug multiThread(static link) libcmtd.lib
MultiThread(dynamic link) msvert.lib
Debug multiThread(dynamic link) msvertd.lib  

  2.C運行時庫的作用

  C運行時庫除了給我們提供必要的庫函數調用(如memcpy、printf、malloc等)之外,它提供的另一個最重要的功能是爲應用程序添加啓動函數。

  C運行時庫啓動函數的主要功能爲進行程序的初始化,對全局變量進行賦初值,加載用戶程序的入口函數。

  不採用寬字符集的控制檯程序的入口點爲mainCRTStartup(void)。下面我們以該函數爲例來分析運行時庫究竟爲我們添加了怎樣的入口程序。這個函數在crt0.c中被定義,下列的代碼經過了筆者的整理和簡化:

void mainCRTStartup(void)
{
 int mainret;
 /*獲得WIN32完整的版本信息*/
 _osver = GetVersion();
 _winminor = (_osver >> 8) & 0x00FF ;
 _winmajor = _osver & 0x00FF ;
 _winver = (_winmajor << 8) + _winminor;
 _osver = (_osver >> 16) & 0x00FFFF ;

 _ioinit(); /* initialize lowio */

 /* 獲得命令行信息 */
 _acmdln = (char *) GetCommandLineA();

 /* 獲得環境信息 */
 _aenvptr = (char *) __crtGetEnvironmentStringsA();

 _setargv(); /* 設置命令行參數 */
 _setenvp(); /* 設置環境參數 */

 _cinit(); /* C數據初始化:全局變量初始化,就在這裏!*/

 __initenv = _environ;
 mainret = main( __argc, __argv, _environ ); /*調用main函數*/

 exit( mainret );
}


  從以上代碼可知,運行庫在調用用戶程序的main或WinMain函數之前,進行了一些初始化工作。初始化完成後,接着才調用了我們編寫的main或WinMain函數。只有這樣,我們的C語言運行時庫和應用程序才能正常地工作起來。

  除了crt0.c外,C運行時庫中還包含wcrt0.c、 wincrt0.c、wwincrt0.c三個文件用來提供初始化函數。wcrt0.c是crt0.c的寬字符集版,wincrt0.c中包含windows應用程序的入口函數,而wwincrt0.c則是wincrt0.c的寬字符集版。

  Visual C++的運行時庫源代碼缺省情況下不被安裝。如果您想查看其源代碼,則需要重裝Visual C++,並在重裝在時選中安裝運行庫源代碼選項。

  3.各種C運行時庫的區別

  (1)靜態鏈接的單線程庫

  靜態鏈接的單線程庫只能用於單線程的應用程序,C運行時庫的目標代碼最終被編譯在應用程序的二進制文件中。通過/ML編譯選項可以設置Visual C++使用靜態鏈接的單線程庫。

  (2)靜態鏈接的多線程庫

  靜態鏈接的多線程庫的目標代碼也最終被編譯在應用程序的二進制文件中,但是它可以在多線程程序中使用。通過/MD編譯選項可以設置Visual C++使用靜態鏈接的單線程庫。

  (3)動態鏈接的運行時庫

  動態鏈接的運行時庫將所有的C庫函數保存在一個單獨的動態鏈接庫MSVCRTxx.DLL中,MSVCRTxx.DLL處理了多線程問題。使用/ML編譯選項可以設置Visual C++使用動態鏈接的運行時庫。

  /MDd、 /MLd 或 /MTd 選項使用 Debug runtime library(調試版本的運行時刻函數庫),與/MD、 /ML 或 /MT分別對應。Debug版本的 Runtime Library 包含了調試信息,並採用了一些保護機制以幫助發現錯誤,加強了對錯誤的檢測,因此在運行性能方面比不上Release版本。

  下面看一個未正確使用C運行時庫的控制檯程序:

#include <stdio.h>
#include <afx.h>
int main()
{
 CFile file;
 CString str("I love you");
 TRY
 {
  file.Open("file.dat",CFile::modeWrite | CFile::modeCreate);
 }
 CATCH( CFileException, e )
 {
  #ifdef _DEBUG
  afxDump << "File could not be opened " << e->m_cause << "\n";
  #endif
 }
 END_CATCH

 file.Write(str,str.GetLength());
 file.Close();
}

  我們在"rebuild all"的時候發生了link錯誤:

nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
main.exe : fatal error LNK1120: 2 unresolved externals
Error executing cl.exe.

  發生錯誤的原因在於Visual C++對控制檯程序默認使用單線程的靜態鏈接庫,而MFC中的CFile類已暗藏了多線程。我們只需要在Visual C++6.0中依次點選Project->Settings->C/C++菜單和選項,在Project Options裏修改編譯選項即可。

如何理解C run-time library (C運行時庫)2007-12-14 21:13

注: 以下內容部分引自CSND中相關討論的帖子,並結合自己的理解整理而成。僅供參考。

1)運行時庫就是 C run-time library,是 C 而非 C++ 語言世界的概念:取這個名字就是因爲你的 C 程序運行時需要這些庫中的函數.

2)C 語言是所謂的“小內核”語言,就其語言本身來說很小(不多的關鍵字,程序流程控制,數據類型等);所以,C 語言內核開發出來之後,Dennis Ritchie 和 Brian Kernighan 就用 C 本身重寫了 90% 以上的 UNIX 系統函數,並且把其中最常用的部分獨立出來,形成頭文件和對應的 LIBRARY,C run-time library 就是這樣形成的。

3)隨後,隨着 C 語言的流行,各個 C 編譯器的生產商/個體/團體都遵循老的傳統,在不同平臺上都有相對應的 Standard Library,但大部分實現都是與各個平臺有關的。由於各個 C 編譯器對 C 的支持和理解有很多分歧和微妙的差別,所以就有了 ANSI C;ANSI C (主觀意圖上)詳細的規定了 C 語言各個要素的具體含義和編譯器實現要求,引進了新的函數聲明方式,同時訂立了 Standard Library 的標準形式。所以C運行時庫由編譯器生產商提供。至於由其他廠商/個人/團體提供的頭文件和庫函數,應當稱爲第三方 C 運行庫(Third party C run-time libraries)。

4)C run-time library裏面含有初始化代碼,還有錯誤處理代碼(例如divide by zero處理)。你寫的程序可以沒有math庫,程序照樣運行,只是不能處理複雜的數學運算,不過如果沒有了C run-time庫,main()就不會被調用,exit()也不能被響應。因爲C run-time library包含了C程序運行的最基本和最常用的函數。


5)到了 C++ 世界裏,有另外一個概念:Standard C++ Library,它包括了上面所說的 C run-time library 和 STL。包含 C run-time library 的原因很明顯,C++ 是 C 的超集,沒有理由再重新來一個 C++ run-time library. VC針對C++ 加入的Standard C++ Library主要包括:LIBCP.LIB, LIBCPMT.LIB和 MSVCPRT.LIB

6)Windows環境下,VC提供的 C run-time library又分爲動態運行時庫和靜態運行時庫。
動態運行時庫主要是DLL庫文件msvcrt.dll(or MSVCRTD.DLL for debug build),對應的Import library文件是MSVCRT.LIB(MSVCRTD.LIB for debug build)
靜態運行時庫(release版)對應的主要文件是:
LIBC.LIB (Single thread static library, retail version)
LIBCMT.LIB (Multithread static library, retail version)

msvcrt.dll提供幾千個C函數,即使是像printf這麼低級的函數都在msvcrt.dll裏。其實你的程序運行時,很大一部分時間時在這些運行庫裏運行。在你的程序(release版)被編譯時,VC會根據你的編譯選項(單線程、多線程或DLL)自動將相應的運行時庫文件(libc.lib,libcmt.lib或Import library msvcrt.lib)鏈接進來。

編譯時到底哪個C run-time library聯入你的程序取決於編譯選項:
/MD, /ML, /MT, /LD   (Use Run-Time Library)
你可以VC中通過以下方法設置選擇哪個C run-time library聯入你的程序:
To find these options in the development environment, click Settings on the Project menu. Then click the C/C++ tab, and click Code Generation in the Category box. See the Use Run-Time Library drop-down box.

從程序可移植性考慮,如果兩函數都可完成一種功能,選運行時庫函數好,因爲各個 C 編譯器的生產商對標準C Run-time library提供了統一的支持.

發佈了5 篇原創文章 · 獲贊 3 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章