C/C++ 运行库
2017-07-08 11:12
113 查看
C/C++ 运行库
任何一个C 程序,它的背后都有一套庞大的代码来进行支撑,以使得该程序能够正常运行。这套代码至少包括入口函数,及其所依赖的函数所构成的函数集合。另外包括各种标准库函数的实现。这样的一个代码集合称之为运行时库(Runtime Library),C 语言的运行时库,称为 C 运行库(CRT)。
C 运行库大致包含的功能:
启动与退出函数
由C 语言标准规定的C 语言标准库所拥有的函数实现。
I/O 功能的封装与实现。
堆的封装和实现
语言中一些特殊功能的实现。
实现调试功能的代码。
C语言标准库
C语言的标准库非常轻量,仅仅包含了数学函数、字符/字符串处理,I/O 等基本方面。标准输入输出—-stdio.h
文件操作—-stdio.h
字符操作—-ctype.h
字符串操作—-string.h
数学函数—-math.h
资源管理—-stdlib.h
格式管理—-stdlib.h
时间/日期—-time.h
断言—-assert.h
各种类型的常数—-limits.h & float.h
变长参数—-stdarg.h
非局部跳转—-setjmp.h
变长参数宏
GCC:
#define printf(args...) fprintf(stdout,##args)
MSVC:
#define printf(...) fprintf(stdout,__VA_ARGS__);
非局部跳转
使用非局部跳转,可以实现从一个函数体内向另一个事先登记过的函数体内跳转,而不必担心堆栈混乱。
hljs cpp">#include<setjmp.h> jmp_buf b; void f() { longjmp(b,1); } int main() { if(setjmp(b)) { printf("World!\n"); } else { printf("Hello "); f(); } return 0; }
运行库是平台相关的,C 语言的运行库从某种程度上说是C 语言的程序和不同操作系统平台之间的抽象层,将不同的操作系统API 抽象成了相同的库函数。但是C 运行库的功能毕竟有限,比如创建线程等操作,不同的操作系统完全不同。我们不得不通过其它的方法,绕过C 语言运行库直接调用操作系统API 或使用其它的库。Linux 和 Windows 平台下两个主要C 语言的运行库分别为glibc 和 MSVCRT。像线程操作这样的功能,虽然不是标准C 语言运行库的一部分,也现在两个库中,因此glibc 和 MSVCRT 事实上是标准C 语言运行库的超集。
glibc 组成
主要包含两部分:1.头文件,比如stdio.h、stdlib.h等。往往位于/usr/include。2.库的二进制文件部分,分为静态和动态两个版本。动态:/lib/libc.so.6 。非静态:/usr/lib/libc.a。另外还有辅助程序运行的运行库:/usr/lib/crtl.o、/usr/lib/crti.o 和 /usr/lib/crtn.o。这几个文件虽然小,但都是程序运行的最关键的文件。
MSVC CRT
命名规则如下:
libc [p] [mt] [d] .lib
p 表示 C++ 标准库
mt 表示支持多线程
d 表示调试版本
常见的MSVC CRT 版本
文件名 | 相关的DLL | 属性 | 编译器选项 | 预编译宏 |
---|---|---|---|---|
libcmt.lib | 无 | 多线程,静态链接 | /MT | _MT |
msvcrt.lib | msvcr80.dll | 多线程、动态链接 | /MD | _MT、_DLL |
libcmtd.lib | 无 | 多线程、静态链接、调试 | /MTd | _DEBUG、_MT |
msvcrtd.lib | msvcr90d.dll | 多线程、动态链接、调试 | /MDd | _DEBUG、_MT、_DLL |
msvcmrt.lib | msvcm90.dll | 托管/本地混合代码 | /clr | |
msvcurt.lib | msvcm90.dll | 纯托管代码 | /clr:pure |
C++ CRT
文件名 | 相关的DLL | 属性 | 编译器选项 | 预编译宏 |
---|---|---|---|---|
LIBCPMT.LIB | 无 | 多线程,静态链接 | /MT | _MT |
MSVCPRT.LIB | MSVCP90.dll | 多线程、动态链接 | /MD | _MT、_DLL |
LIBCPMTD.LIB | 无 | 多线程、静态链接、调试 | /MTd | _DEBUG、_MT |
MSVCPRTD.LIB | MSGCP90D.DLL | 多线程、动态链接、调试 | /MDd | _DEBUG、_MT、_DLL |
运行库与多线程
线程私有 | 线程之间共享 |
---|---|
局部变量 函数的参数 TLS数据 | 全局变量 堆上的数据 函数里的静态变量 程序代码 文件资源 |
提供多线程操作的接口,如创建线程、退出线程、设置线程优先级等函数
运行库本身能够在多线程环境下正确的执行
CRT 为支持多线程所做的改进
使用TLSTLS 即线程局部存储,多线程环境下,设置错误代码时将值设置到TLS 中,以免引起混乱。
加锁
线程不安全的函数内部自动加锁,包括malloc printf 等。
改进函数调用方式
比如strtok 函数内部实现使用了一个char* 类型的静态局部变量,新版本将添加一个char* 指针参数,替代原来的静态局部变量的功能,但同时,使用此函数的源代码需要进行相应的修改。
C++ 全局构造与析构
glibc 全局构造与析构“.init” 和 “.finit”段,这两个段中的代码最终会被拼成两个函数_init() 和 _finit(),这两个函数会先于/后于 main 函数执行。
调用流程如下:
_start->__libc_start_main->__libc_csu_init->_init->__do_global_ctors_aux。
void __do_global_ctors_aux(void) { /* call constructor functions. 4000 */ unsigned long nptrs = (unsigned long) __CTOR_LIST__(0); unsigned i; for(i = nptrs; i >= 1; i--) __CTOR_LIST__[i](); }
_CTOR_LIST_数组的第一个元素为元素的个数,下面的对应个数的函数即各个模块的全局构造函数。
析构函数的实现是通过__exa_atexit()在exit()函数中注册进程退出回调函数来实现析构。按照构造的逆序注册对应的析构函数。
MSVCRT 全局构造与析构
mainCRTStartup->_initterm
typedef void (__cdecl * _PVFV)(); static void __cdecl _initterm(_PVFV* pfbegin,_PVFV* pfend) { while ( pfbegin < pfend) { if(*pfbegin != NULL) (**pfbegin)(); ++ pfbegin; } }
析构函数
同Glibc,通过atexit() 实现全局析构。
相关文章推荐
- Symbian和C++ SDK开发入门之运行
- 如何运行C++ STL程序——morning小品文
- JAVA JNI 调用C/C++库提高运行效率 (图)
- 使用typeid和RTTI C++获取对象运行时类名称
- 在C++中运行.exe程序
- 如何运行C++ STL程序
- 用VS2005写的C++程序能在未安装VS2005的机器上直接运行吗?
- 运行未安装VS2005的机器上C++程序
- C++ SDK+Symbian开发入门之运行
- 如何运行C++ STL程序
- Symbian和C++ SDK开发入门之运行
- 为什么很多C++程序不能运行?
- 让C/C++图形程序独立运行
- C/C++中利用空指针(NULL),提高程序运行效率
- 如何运行C++ STL程序
- 在Borland C++ BuilderX上运行和调试控制台程序
- Symbian和C++ SDK开发入门之运行
- C++ 程序稳定运行一段时间后异常中止,为什么?vc6 运行库的bug!!!
- C++ 计算 代码运行时间的 几种方法
- 无法以main为入口运行C++程序