运行库
C/C++运行库
任何一个c程序都需要一套庞大的代码,这套代码包含着入口函数及其所依赖的函数所构成的集合,被称之为运行时库。
一个C运行库大致包含如下功能:
- 启动和退出:包括入口函数及入口函数所依赖的其他函数
- 标准函数:由C语言标准规定标准库所拥有的的函数实现
- I/O:I/O功能的封装和实现
- 堆:堆的实现和封装
- 语言实现:语言的一些特殊功能的实现
- 调试:实现调试功能的代码
C语言标准库
ANSI C 的标准库主要由24个C语言头文件组成,例如:
变长参数
int printf(const char* format,...);
printf函数的参数是典型的变长参数。
除了第一个参数类型为const char*
外,其后可以追加任意数量、任意类型的参数。
这是因为在printf函数实现中,其中有一个va_list ap;
类型变量。
该变量以后将会一次指向各个可变参数,但是要注意,所有的变长参数需要是同类型。
在函数结束前,还必须用宏va_end
来清理现场。
变长参数实现原理
假设有如下代码:
int sum(unsigned num,...);
int n = sum(3,16,38,53);
这样就会在栈上形成如下布局:
因为是同类型的,我们可以用指针+1
的方法来计算出其他参数的地址。
运行库和多线程
多线程困扰
我们知道,线程的访问能力非常自由,它可以自由的访问进程内存里的所有数据,甚至包括其他线程的堆栈信息。
但是!你知道么,线程也是有自己的私有存储空间的:
- 栈
- 线程局部存储
- 寄存器
以下表详细记录了线程私有与公有的资源:
- C++多线程运行库详解(未完成)
线程私有实现
如果要定义一个全局变量为线程私有的(TLS),只需要在定义前加上相应的关键字__thread
即可。
__thread int number;
一旦一个全局变量被定义成TLS类型,那么每个线程会有拥有这个变量的副本。
C++全局构造与析构
构造
同样,我们这里我们也只讨论glibc
的全局构造与析构。
在入口函数那一篇博客我曾提到过.init
以及.finit
两个段,这两个段中的代码会被拼成_init()
和_finit()
两个函数。
请看下面这段代码:
通过对地址的跟踪,init实际指向了__lib_csu_init
函数。
这里调用的_init()
函数就是.init
段,也就是说,用户所有存放在此的代码就将在这里被执行,当然也包括构造函数。
析构
析构和构造基本都是类似的。
不够这里要注意的就是,先构造的对象后析构
参考文献
[1] 俞甲子 石凡 潘爱明.程序员的自我修养.电子工业出版社,2009.4.