一 产生
运行时库是程序在运行时所需要的库文件,通常以LIB或DLL形式提供。
C运行时库就是C run-time library,诞生于20世纪70年代,是C而非C++语言世界的概念,C程序运行时需要这些库中的函数。
C 语言是所谓的“小内核”语言,就其语言本身来说很小(不多的关键字,程序流程控制,数据类型等)。所以,C语言内核开发出来之后,Dennis Ritchie和Brian Kernighan就用C本身重写了90%以上的UNIX系统函数,并且把其中最常用的部分独立出来,形成头文件和对应的LIBRARY,C运行时库就是这样形成的。
随着C语言的流行,各个C编译器的生产商/个体/团体都遵循老的传统,在不同平台上都有相对应的Standard Library,但大部分实现都是与各个平台有关的。由于各个C编译器对C的支持和理解有很多分歧和微妙的差别,所以就有了ANSI C;ANSI C(主观意图上)详细的规定了C语言各个要素的具体含义和编译器实现要求,引进了新的函数声明方式,同时订立了Standard Library的标准形式。所以C运行时库由编译器生产商提供。至于由其他厂商/个人/团体提供的头文件和库函数,应当称为第三方C运行库(Third
party C run-time libraries)。
二 发展
到了 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
C运行库刚诞生的时候,程序世界还很单纯,应用程序都是单线程的,多任务或多线程机制在此时还属于新观念。所以这个时期的C运行时库都是单线程的。
随着操作系统多线程技术的发展,最初的C运行时库无法满足程序的需求,出现了严重的问题。C运行时库使用了多个全局变量(例如errno)和静态变量,这可能在多线程程序中引起冲突。假设两个线程都同时设置errno,其结果是后设置的errno会将先前的覆盖,用户得不到正确的错误信息。
因此,Visual C++提供了两种版本的C运行时库。一个版本供单线程应用程序调用,另一个版本供多线程应用程序调用。多线程运行时库与单线程运行时库有两个重大差别:
(1)类似errno的全局变量,每个线程单独设置一个。这样从每个线程中可以获取正确的错误信息。
(2)多线程库中的数据结构以同步机制加以保护。 这样可以避免访问时候的冲突。
Visual C++提供的多线程运行时库又分为静态链接库和动态链接库两类,而每一类运行时库又可再分为debug版和release版,因此Visual C++共提供了6个运行时库。如下表:(单线程和多线程都有debug版和release版;只有多线程才有静态链接库和动态链接库之分)
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) | msvcrt.lib(导入库) |
Debug multiThread(dynamic link) | msvcrtd.lib(导入库) |
三 C运行库的作用
(1)C运行时库包含了C程序运行的最基本和最常用的函数(如memcpy、printf、malloc等)。
(2)C运行时库除了给我们提供必要的库函数调用之外,它提供的另一个最重要的功能是为应用程序添加启动函数。C运行时库启动函数的主要功能为进行程序的初始化,对全局变量进行赋初值,加载用户程序的入口函数。
不采用宽字符集的控制台程序的入口点为mainCRTStartup(void)。下面我们以该函数为例来分析运行时库究竟为我们添加了怎样的入口程序。这个函数在crt0.c中(crt0.c是静态链接CRT,动态链接则为crtexe.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语言运行时库和应用程序才能正常地工作起来。
你写的程序可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C run-time库,main()就不会被调用,exit()也不能被响应。
除了crt0.c外,C运行时库中还包含wcrt0.c、 wincrt0.c、wwincrt0.c三个文件用来提供初始化函数。wcrt0.c是crt0.c的宽字符集版,wincrt0.c中包含windows应用程序的入口函数,而wwincrt0.c则是wincrt0.c的宽字符集版。
Visual C++的运行时库源代码缺省情况下不被安装。如果您想查看其源代码,则需要重装Visual C++,并在重装在时选中安装运行库源代码选项。
四 各种C运行时库的区别
编译器链接选项:
Visual C++编译时到底哪个C运行时库联入程序取决于编译选项,选项告诉编译器应用程序想使用什么版本的C标准程序库。与标准程序库有关的选项:/ML、/MLd、/MT、/MTd、/MD、/MDd
/ML对应单线程静态版的标准程序库(libc.lib);
/MT对应多线程静态版标准库(libcmt.lib),此时编译器会自动定义_MT宏;
/MD对应多线程DLL版(导入库msvcrt.lib,DLL是msvcrt.dll),编译器自动定义_MT和_DLL两个宏。
后面加d的选项都会让编译器自动多定义一个_DEBUG宏,表示要使用对应标准库的调试版,因此:
/MLd对应调试版单线程静态标准库(libcd.lib);
/MTd对应调试版多线程静态标准库(libcmtd.lib);
/MDd对应调试版多线程DLL标准库(导入库msvcrtd.lib,DLL是msvcrtd.dll)。
VC2005/VC6.0中通过以下方法设置选择哪个C run-time library联入你的程序:
VC2005: To find these options in the development environment, click Project on the Project menu, choose Properties... item. Then click the C/C++ tab,
and click Code Generation in the Category box. See the Runtime Library drop-down box.
VC6.0: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运行时库的区别:
(1)静态链接的单线程库
静态链接的单线程库只能用于单线程的应用程序,C运行时库的目标代码最终被编译在应用程序的二进制文件中。通过/ML编译选项可以设置Visual C++使用静态链接的单线程库。
(2)静态链接的多线程库
静态链接的多线程库的目标代码也最终被编译在应用程序的二进制文件中,但是它可以在多线程程序中使用。通过/MT编译选项可以设置Visual C++使用静态链接的单线程库。
(3)动态链接的运行时库
动态链接的运行时库将所有的C库函数保存在一个单独的动态链接库MSVCRTxx.DLL中,MSVCRTxx.DLL处理了多线程问题。使用/MD编译选项可以设置Visual C++使用动态链接的运行时库。
/MLd、 /MTd或/MDd选项使用Debug Runtime Library(调试版本的运行时刻函数库),与/ML、/MT或/MD分别对应。Debug版本的Runtime Library包含了调试信息,并采用了一些保护机制以帮助发现错误,加强了对错误的检测,因此在运行性能方面比不上Release版本。
程序运行时,很大一部分时间是在这些运行库里运行。在程序(Release版)被编译时,VC会根据编译选项(单线程、多线程或DLL)自动将相应的运行时库文件(libc.lib、libcmt.lib或Import library msvcrt.lib)链接进来。
注:修改编译选项,将/MD或/MDd改为/MT或/MTd,就实现了对VC运行时库的静态链接,在运行时就不再需要VC的dll了。(静态链接将目标代码直接编译在应用程序的二进制文件中,这样运行时就不需要dll了)
参考
1 http://blog.chinaunix.net/u2/75914/showart_1861328.html
2 http://www.99inf.net/SoftwareDev/VC/28090.htm
转自:http://blog.csdn.net/maomaokuaile/article/details/5070320
注:
- vs2005 call stack
- kernel32.dll ---> mainCRTStartup ---> main