运行库

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.