C 编程/标准库
C 标准库 是一组标准化的 头文件 和库例程,用于实现常见的操作,例如输入/输出和字符字符串处理。与其他语言(例如 COBOL、Fortran 和 PL/I)不同,C 不包含用于这些任务的内置关键字,因此几乎所有 C 程序都依赖于标准库来运行。
C 编程语言以前没有提供任何基本函数,例如 I/O 操作。随着时间的推移,C 用户社区分享了想法和实现以提供这些函数。这些想法变得普遍,并最终在 1989 年被纳入标准化 C 编程语言的定义中。这些现在被称为 C 标准库。
Unix 和 C 都是 20 世纪 60 年代后期和 70 年代初期在 AT&T 的贝尔实验室创建的。在 20 世纪 70 年代,C 编程语言变得越来越流行,许多大学和组织开始为自己的项目创建他们自己的语言变体。到 20 世纪 80 年代初,各种 C 实现之间的兼容性问题变得明显。1983 年,美国国家标准协会 (ANSI) 成立了一个委员会,以制定一个称为“ANSI C”的 C 标准规范。这项工作最终在 1989 年产生了所谓的 C89 标准。由此产生的标准的一部分是一组软件库,称为 ANSI C 标准库。
C 标准的后续修订在库中添加了几个新的必需头文件。对这些新扩展的支持在不同的实现之间有所不同。
头文件 <iso646.h>、<wchar.h> 和 <wctype.h> 是在 1995 年批准的 C 标准的规范性增补 1(以下简称 NA1)中添加的。
头文件 <complex.h>、<fenv.h>、<inttypes.h>、<stdbool.h>、<stdint.h> 和 <tgmath.h> 是在 1999 年发布的 C 标准修订版 C99 中添加的。
每个函数的声明都保存在头文件中,而函数的实际实现则分离到库文件中。头文件的命名和范围已变得普遍,但库的组织仍然多种多样。标准库通常与编译器一起提供。由于 C 编译器通常提供不在 ANSI C 中指定的额外函数,因此特定编译器的标准库通常与其他编译器的标准库不兼容。
事实证明,C 标准库的许多部分设计得很好。从后视镜的角度来看,有一些部分被认为是错误的。字符串输入函数 gets()
(以及使用 scanf()
读取字符串输入)是许多缓冲区溢出的根源,大多数编程指南建议避免使用这种用法。另一个奇怪的地方是 strtok()
,它是一个设计为原始 词法分析器 的函数,但它非常“脆弱”并且难以使用。
ANSI C 标准库由 24 个 C 头文件组成,可以通过单个指令将这些头文件包含到程序员的项目中。每个头文件包含一个或多个函数声明、数据类型定义和宏。这些头文件的内容如下。
与其他一些语言(例如 Java)相比,标准库非常小。该库提供了一组基本的数学函数、字符串操作、类型转换以及基于文件和控制台的 I/O。它不包括像 C++ 标准模板库那样的“容器类型”的标准集,更不用说完整的图形用户界面 (GUI) 工具包、网络工具和 Java 作为标准提供的众多其他功能。小型标准库的主要优点是,提供一个工作的 ANSI C 环境比其他语言更容易,因此将 C 移植到新平台相对容易。
许多其他库已被开发出来,以提供与其他语言在其标准库中提供的功能等效的功能。例如,GNOME 桌面环境项目开发了 GTK+ 图形工具包和 GLib,这是一个容器数据结构库,还有许多其他著名的例子。可用库的多样性意味着一些优秀的工具包在历史上已经证明了自己的价值。相当大的缺点是它们通常不能很好地协同工作,程序员通常熟悉不同的库集,并且在任何特定平台上都可能提供不同的库集。
<assert.h> | 包含 assert 宏,用于帮助在调试版本的程序中检测逻辑错误和其他类型的错误。 |
<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> | 用于计算常见的数学函数 -- 有关详细信息,请参阅 更多数学 或 C++ 编程/代码/标准 C 库/数学。 |
<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 中新增) |
常用支持库
[edit | edit source]虽然没有标准化,但 C 程序可能依赖于包含编译器在运行时使用的代码的运行时库例程。 例如,在调用 main()
之前初始化操作系统进程的代码是在给定供应商的编译器的 C 运行时库中实现的。 运行时库代码可能有助于其他语言功能的实现,例如处理未捕获的异常或实现浮点代码。
C 标准库只记录本文中提到的特定例程的可用性和行为方式。 由于编译器实现可能依赖于这些额外的实现级函数的可用性,因此供应商特定的例程很可能与 C 标准库打包在同一个模块中,因为它们都可能被使用其工具集构建的任何程序使用。
虽然由于这种打包方式,C 运行时库经常与 C 标准库混淆,但 C 运行时库不是语言的标准化部分,它是供应商特定的。
编译器内置函数
[edit | edit source]一些编译器(例如,GCC)提供了许多 C 标准库函数的内置版本; 也就是说,函数的实现被写入编译后的目标文件,程序调用内置版本而不是 C 库共享目标文件中的函数。 这减少了函数调用开销,尤其是在函数调用被替换为内联变体的情况下,并允许其他形式的优化(因为编译器知道内置变体的控制流特性),但可能会在调试时造成混淆(例如,内置版本无法被替换为检测变体)。
POSIX 标准库
[edit | edit source]POSIX(以及 Single Unix Specification)指定了一些例程,这些例程应该在 C 标准库本身之外提供; 这些例程通常与 C 标准库函数一起实现,其紧密程度各不相同。 例如,glibc 在 libc.so 中实现 fork 等函数,但在 NPTL 合并到 glibc 之前,它是一个单独的库,有自己的链接器标志。 通常,这种 POSIX 指定的函数将被视为库的一部分; C 库本身可能会被标识为 ANSI 或 ISO C 库。
以下库由 POSIX 识别
c |
此选项应提供 POSIX.1-2008 的系统接口卷中引用的所有接口,除了那些被列为驻留在 <aio.h>、<arpa/inet.h>、<complex.h>、<fenv.h>、<math.h>、<mqueue.h>、<netdb.h>、<net/if.h>、<netinet/in.h>、<pthread.h>、<sched.h>、<semaphore.h>、<spawn.h>、<sys/socket.h>、<signal.h> 中的 pthread_kill() 和 pthread_sigmask()、<trace.h>、<sys/mman.h> 中标记为可选的接口、<fcntl.h> 中标记为 ADV(咨询信息)的接口,以及以 clock_ 或 time_ 为前缀的接口在 <time.h> 中。 此选项不必存在以导致搜索此库。 |
l |
此选项应提供 lex 的 C 语言输出所需的所有接口,这些接口不是通过 -l c 选项提供的。 (flex 程序,一个 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>、<semaphore.h> 和 <spawn.h> 中引用的所有接口,<sys/mman.h> 中标记为可选的接口,<fcntl.h> 中标记为 ADV(咨询信息)的接口,以及 <time.h> 中以 clock_ 和 time_ 为前缀的接口。 实现可能在没有此选项的情况下搜索此库。 |
trace |
此选项应提供 <trace.h> 中引用的所有接口。 实现可能在没有此选项的情况下搜索此库。 |
xnet |
此选项应提供 <arpa/inet.h>、<netdb.h>、<net/if.h>、<netinet/in.h> 和 <sys/socket.h> 中引用的所有接口。 实现可能在没有此选项的情况下搜索此库。 |
y |
此选项应提供 yacc 的 C 语言输出所需的所有接口,这些接口不是通过 -l c 选项提供的。 (yacc 的一些克隆,包括 bison 和 byacc,在生成的文件中包含整个库,因此无需使用 -l y。) |