C标准库是一个标准化的头文件和库例程集合,用于实现常见操作,如输入/输出和字符字符串处理。与其他语言(如COBOL、Fortran和PL/I)不同,C语言没有内建的关键字来执行这些任务,因此几乎所有C程序都依赖标准库来操作。

历史

C编程语言最初并没有提供任何基础函数,如I/O操作。随着时间的推移,C的用户社区共享了想法和实现来提供这些功能。这些想法逐渐变得普遍,并最终在1989年被纳入标准化的C语言定义中。这些实现现在被称为C标准库。

Unix和C语言都是在AT&T贝尔实验室于1960年代末和1970年代初期创建的。在1970年代,C编程语言变得越来越流行,许多大学和组织开始为他们自己的项目创建C语言的变体。到1980年代初期,不同C实现之间的兼容性问题变得显著。1983年,美国国家标准协会(ANSI)成立了一个委员会,制定C语言的标准规范,称为“ANSI C”。这项工作最终在1989年形成了所谓的C89标准。由此产生的标准的一部分是一个称为ANSI C标准库的软件库集。

C标准的后续修订增加了几个新的必要头文件。对于这些新扩展的支持在不同的实现中有所不同。

头文件 <iso646.h><wchar.h><wctype.h> 是在1995年通过标准附录1(简称NA1)添加的。

头文件 <complex.h><fenv.h><inttypes.h><stdbool.h><stdint.h><tgmath.h> 是在C99中增加的,该修订版本于1999年发布。

注意:

C++编程语言包含了ANSI C89标准库中的函数,但对这些函数做了一些修改,例如将所有标识符放入std命名空间,并将头文件名从 <xxx.h> 改为 <cxxx>(尽管仍然可以使用C风格的名字,尽管它们已被弃用)。

设计

每个函数的声明都保存在一个头文件中,而函数的实际实现则被分离到库文件中。头文件的命名和范围已经变得标准化,但库的组织结构仍然保持多样性。标准库通常与编译器一起提供。由于C编译器通常提供一些在ANSI C中未指定的额外函数,因此某些特定编译器的标准库通常与其他编译器的标准库不兼容。

C标准库的许多部分被证明是设计良好的。回顾历史,有些部分被认为是错误的。例如,字符串输入函数gets()(以及使用scanf()读取字符串输入)是许多缓冲区溢出的源头,绝大多数编程指南建议避免使用这些函数。另一个怪异之处是strtok(),一个被设计为基本词法分析器的函数,但它非常“脆弱”,且难以使用。

ANSI标准

ANSI C标准库包含24个C头文件,可以通过单个指令包含到程序员的项目中。每个头文件包含一个或多个函数声明、数据类型定义和宏。以下是这些头文件的内容。

与某些其他语言(例如Java)相比,标准库非常小。它提供了基本的数学函数、字符串处理、类型转换以及文件和控制台的I/O功能。它不包含像C++标准模板库那样的标准“容器类型”,更不用说Java标准库中提供的完整图形用户界面(GUI)工具包、网络工具和其他大量功能。标准库的小型化的主要优点是,提供一个可用的ANSI C环境比其他语言要容易得多,因此将C移植到新平台的难度相对较小。

许多其他库已经被开发出来,用来提供与其他语言标准库中相同功能的实现。例如,GNOME桌面环境项目开发了GTK+图形工具包和GLib,一个容器数据结构库,还有许多其他知名的示例。由于可用库的多样性,某些优越的工具包通过历史证明了它们的价值。其显著的缺点是,这些库往往无法很好地协同工作,程序员往往熟悉不同的库集合,而在某个平台上可能提供不同的库集合。

ANSI C库头文件

  • <assert.h>:包含assert宏,用于帮助在调试版本的程序中检测逻辑错误和其他类型的bug。
  • <complex.h>:用于操作复数的函数集合(C99新增)。
  • <ctype.h>:包含用于按类型分类字符或在不同字符集(通常为ASCII或其扩展,尽管也有基于EBCDIC的实现)中进行大小写转换的函数。
  • <errno.h>:用于测试库函数报告的错误代码。
  • <fenv.h>:用于控制浮点环境(C99新增)。
  • <float.h>:包含定义常量,指定浮点库的实现特性,如最小差异(_EPSILON)、最大精度(_DIG)和可以表示的数字范围(_MIN,_MAX)。
  • <inttypes.h>:用于精确的整数类型转换(C99新增)。
  • <iso646.h>:用于编程ISO 646变体字符集(NA1新增)。
  • <limits.h>:包含定义常量,指定整数类型的实现特性,如可以表示的数字范围(_MIN,_MAX)。
  • <locale.h>:用于setlocale()及相关常量。用于选择适当的区域设置。
  • <math.h>:用于计算常见数学函数。
  • <setjmp.h>:用于非局部跳转的setjmp和longjmp。
  • <signal.h>:用于控制各种异常情况。
  • <stdarg.h>:用于访问传递给函数的可变数量的参数。
  • <stdbool.h>:用于布尔数据类型(C99新增)。
  • <stdint.h>:用于定义各种整数类型(C99新增)。
  • <stddef.h>:用于定义几个有用的类型和宏。
  • <stdio.h>:提供C语言的核心输入输出功能。此文件包括著名的printf函数。
  • <stdlib.h>:用于执行各种操作,包括转换、伪随机数、内存分配、进程控制、环境、信号、搜索和排序。
  • <string.h>:用于操作几种类型的字符串。
  • <tgmath.h>:用于类型泛化数学函数(C99新增)。
  • <time.h>:用于转换不同的时间和日期格式。
  • <wchar.h>:用于操作宽字符流和多种类型的宽字符字符串,是支持多种语言的关键(NA1新增)。
  • <wctype.h>:用于分类宽字符(NA1新增)。

常见支持库

尽管没有标准化,C程序可能依赖于一个运行时库,其中包含编译器在运行时使用的代码。例如,在调用main()之前,操作系统用来初始化进程的代码通常实现于C运行时库。C运行时库代码还可能帮助实现其他语言特性,例如处理未捕获的异常或实现浮点代码。

C标准库只文档化了本文提到的特定例程及其行为。由于编译器的实现可能依赖于这些附加的实现级函数,因此编译器特定的例程通常与C标准库捆绑在一起,因为它们在使用该工具集编译的任何程序中都可能需要。

尽管通常因其捆绑而与C标准库混淆,C运行时库并不是语言的标准化部分,而是供应商特定的。

编译器内建函数

一些编译器(例如GCC)提供了许多C标准库函数的内建版本;也就是说,函数的实现被写入编译后的目标文件中,程序调用的是内建版本,而不是C库共享对象文件中的函数。这减少了函数调用的开销,尤其是当函数调用被内联变体替代时,并允许其他形式的优化(因为编译器知道内建变体的控制流特征),但在调试时可能会造成困惑(例如,内建版本无法替换为有仪器的变体)。

POSIX标准库

POSIX(以及单一Unix规范)指定了一些例程,这些例程应当在C标准库之外提供;这些通常与C标准库函数一起实现,并且有不同程度的紧密度。例如,glibc在libc.so中实现了如fork等函数,但在NPTL合并到glibc之前,它曾是一个独立的库,并有自己的链接器标志。通常,这些POSIX指定的函数被视为库的一部分;C库本身可能被标识为ANSI或

ISO C库。

POSIX认可的库包括:

  • c:提供POSIX.1-2008系统接口卷中引用的所有接口(可能不包括一些接口,如<aio.h>等中的接口)。
  • l:提供lex程序输出需要的接口,lex用fl而不是l。
  • pthread:提供<pthread.h>中引用的所有接口以及在<signal.h>中引用的pthread_kill()pthread_sigmask()
  • m:提供<math.h><complex.h><fenv.h>中引用的所有接口。
  • rt:提供<aio.h><mqueue.h><sched.h>等中引用的所有接口。
  • trace:提供<trace.h>中引用的所有接口。
  • xnet:提供<arpa/inet.h><netdb.h>等中引用的所有接口。
  • y:提供yacc程序输出需要的接口。
最后修改: 2025年01月12日 星期日 13:25