设为首页 加入收藏

TOP

11.2.2 C语言标准库(1)
2013-10-07 00:45:11 来源: 作者: 【 】 浏览:62
Tags:11.2.2 语言 标准

11.2.2  C语言标准库(1)

在本章节里,我们将介绍C语言标准库的基本函数集合,并对其中一些特殊函数进行详细的介绍。ANSI C的标准库由24个C头文件组成。与许多其他语言(如Java)的标准库不同,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)。

除此之外,C语言标准库还有一些特殊的库,用于执行一些特殊的操作,例如:

变长参数(stdarg.h)。

非局部跳转(setjmp.h)。

相信常见的C语言函数读者们都已经非常熟悉,因此这里就不再一一介绍,接下来让我们看看两组特殊函数的细节。

1. 变长参数

变长参数是C语言的特殊参数形式,例如如下函数声明:

int printf(const char* format, ...);
如此的声明表明,printf函数除了第一个参数类型为const char*之外,其后可以追加任意数量、任意类型的参数。在函数的实现部分,可以使用stdarg.h里的多个宏来访问各个额外的参数:假设lastarg是变长参数函数的最后一个具名参数(例如printf里的format),那么在函数内部定义类型为va_list的变量:
va_list ap;
该变量以后将会依次指向各个可变参数。ap必须用宏va_start初始化一次,其中lastarg必须是函数的最后一个具名的参数。
va_start(ap, lastarg);
此后,可以使用va_arg宏来获得下一个不定参数(假设已知其类型为type):
type next = va_arg(ap, type);
在函数结束前,还必须用宏va_end来清理现场。在这里我们可以讨论这几个宏的实现细节。在研究这几个宏之前,我们要先了解变长参数的实现原理。变长参数的实现得益于C语言默认的cdecl调用惯例的自右向左压栈传递方式。设想如下的函数:
int sum(unsigned num, ...);

其语义如下:

第一个参数传递一个整数num,紧接着后面会传递num个整数,返回num个整数的和。

当我们调用:

int n = sum(3, 16, 38, 53);
参数在栈上会形成如图11-7所示的布局。

 
图11-7  函数参数在栈上分布
在函数内部,函数可以使用名称num来访问数字3,但无法使用任何名称访问其他的几个不定参数。但此时由于栈上其他的几个参数实际恰好依序排列在参数num的高地址方向,因此可以很简单地通过num的地址计算出其他参数的地址。sum函数的实现如下:
int sum(unsigned num, ...)
{
int* p = &num + 1;
int ret = 0;
while (num--)
ret += *p++;
return ret;
}

在这里我们可以观察到两个事实:

(1)sum函数获取参数的量仅取决于num参数的值,因此,如果num参数的值不等于实际传递的不定参数的数量,那么sum函数可能取到错误的或不足的参数。

(2)cdecl调用惯例保证了参数的正确清除。我们知道有些调用惯例(如stdcall)是由被调用方负责清除堆栈的参数,然而,被调用方在这里其实根本不知道有多少参数被传递进来,所以没有办法清除堆栈。而cdecl恰好是调用方负责清除堆栈,因此没有这个问题。

printf的不定参数比sum要复杂得多,因为printf的参数不仅数量不定,而且类型也不定。所以printf需要在格式字符串中注明参数的类型,例如用%d表明是一个整数。printf里的格式字符串如果将类型描述错误,因为不同参数的大小不同,不仅可能导致这个参数的输出错误,还有可能导致其后的一系列参数错误。

【小实验】

printf的狂乱输出
#include <stdio.h>
int main()
{
printf("%lf\t%d\t%c\n", 1, 666, 'a');
}
在这个程序里,printf的第一个输出参数是一个int(4字节),而我们告诉printf它是一个double(8字节以上),因此printf的输出会错误,由于printf在读取double的时候实际造成了越界,因此后面几个参数的输出也会失败。该程序的实际输出为(根据实际编译器和环境可能不同):
0.000000     97

【责任编辑:云霞 TEL:(010)68476606】

回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇11.2.2 C语言标准库(2) 下一篇11.2.1 C语言运行库

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: