重庆的企业网站,英语教学网站建设意见,app拉新推广平台渠道商,做网站要学什么软件文章目录 第21章 标准库21.1 标准库的使用21.1.1 对标准库中所用名字的限制21.1.2 使用宏隐藏的函数 21.2 C89标准库概述21.3 C99标准库更新21.4 stddef.h#xff1a;常用定义21.5 stdbool.h#xff1a;布尔类型和值(C99)21.6 C11标准更新(C1X)21.7 stda… 文章目录 第21章 标准库21.1 标准库的使用21.1.1 对标准库中所用名字的限制21.1.2 使用宏隐藏的函数 21.2 C89标准库概述21.3 C99标准库更新21.4 stddef.h常用定义21.5 stdbool.h布尔类型和值(C99)21.6 C11标准更新(C1X)21.7 stdalign.h:地址的对齐(C1X)21.8 stdnoreturn.h:宏noreturn的定义(C1X)问与答写在最后 第21章 标准库
——每个程序都可以是其他程序的一部分但很少是正合适的。
前面几章中零散地介绍了一些C语言标准库的相关知识。本章将完整地讨论标准库。21.1节列举使用库的一些通用的指导原则并介绍了会在一些库的头中发现的技巧使用宏来“隐藏”函数。21.2节会对C89库的每个头分别做概述性介绍21.3节会对C99库的新头做概述性介绍21.4节会对C11库的新头做概括性介绍。
随后几章将深入讨论标准库的头并将相关联的头放在一起讨论。其中stddef.h、stdbool.h、stdalign.h和stdnoreturn.h非常简短所以会在本章中加以讨论分别在21.5节、21.6节、21.7节和21.8节。 21.1 标准库的使用 C89标准库总共划分成15个部分每个部分用一个头描述。C99新增了9个头C11新增了5个头总共有29个: assert.hctype.herrno.hfloat.hlimits.hlocale.hmath.hsetjmp.hsignal.hstdarg.hstddef.hstdio.hstdlib.hstring.htime.h 从C99开始引入9个 complex.hfenv.hinttypes.hiso646.hstdbool.hstdint.htgmath.hwchar.hwctype.h 从C11开始引入5个 stdalign.hstdatomic.hstdnoreturn.hthreads.huchar.h
大多数编译器会使用更大的库其中包含很多上述列表中没有的头。额外添加的头当然不属于标准库的范畴因此不能假设其他的编译器也支持这些头。这类头通常提供一些针对特定机型或特定操作系统的函数这也解释了为什么它们不属于标准库它们可能会提供允许对屏幕或键盘做更多控制的函数。用于支持图形或窗口界面的头也是很常见的。
标准头主要由函数原型、类型定义以及宏定义组成。如果我们的文件中调用了头中声明的函数或是使用了头中定义的类型或宏就需要在文件开头将相应的头包含进来。当一个文件包含多个标准头时#include指令的顺序无关紧要。多次包含同一个标准头也是合法的。 21.1.1 对标准库中所用名字的限制 任何包含了标准头的文件都必须遵守2条规则。 第一该文件不能将头中定义过的宏的名字用于其他目的。例如如果某个文件包含了stdio.h就不能重新定义NULL了因为使用这个名字的宏已经在stdio.h中定义过了。第二具有文件作用域的库名尤其是typedef名也不可以在文件层次重定义。因此一旦文件包含了stdio.h由于stdio.h中已经将size_t定义为typedef名在文件作用域内都不能将size_t重定义为任何标识符。 上述这些限制是显而易见的但C语言还有一些其他的限制可能是你想不到的: 由一个下划线和一个大写字母开头或由两个下划线开头的标识符是为标准库保留的标识符。程序不允许为任何目的使用这种形式的标识符。由一个下划线开头的标识符被保留用作具有文件作用域的标识符和标记。除非在函数内部声明否则不应该使用这类标识符。在标准库中所有具有外部链接的标识符被保留用作具有外部链接的标识符。特别是所有标准库函数的名字都被保留。因此即使文件没有包含stdio.h也不应该定义名为printf的外部函数因为在标准库中已经有一个同名的函数了。
这些规则对程序的所有文件都起作用不论文件包含了哪个头都是如此。虽然这些规则并不总是强制性的但不遵守这些规则可能会导致程序不具有可移植性。
上面列出的规则不仅适用于库中现有的名字也适用于留作未来使用的名字。至于哪些名字是保留的完整的描述太冗长了你可以在C标准的“future library directions”中找到。例如C保留了以str和一个小写字母开头的标识符使得具有这类名字的函数可以被添加到string.h头中。 21.1.2 使用宏隐藏的函数 C程序员经常会用带参数的宏来替代小的函数这在标准库中同样很常见。 C标准允许在头中定义与库函数同名的宏为了起到保护作用还要求有实际的函数存在。因此对于库的头声明一个函数并同时定义一个有相同名字的宏的情况并不少见。 我们已经见过宏与库函数同名的例子。getchar是声明在stdio.h中的库函数具有如下原型
int getchar(void);stdio.h通常也把getchar定义为一个宏
#define getchar() getc(stdin) 在默认情况下对getchar的调用会被看作宏调用因为宏名会在预处理时被替换。 在大多数情况下我们喜欢使用宏来替代实际的函数因为这样可能会提高程序的运行速度。然而在某些情况下我们需要一个真实的函数可能是为了尽量缩小可执行代码的大小。 如果确实存在这种需求可以使用#undef指令14.3节来删除宏定义。例如可以在包含stdio.h后删除宏getchar的定义
#include stdio.h
#undef getchar即使getchar不是宏这样的做法也不会带来任何坏处因为当给定的名字没有被定义成宏时#undef指令不会起任何作用。 此外也可以通过给名字加圆括号来禁用个别宏调用 ch (getchar)(); /* instead of ch getchar(); */ 预处理器无法分辨出带参数的宏除非宏名后跟着一个左圆括号。编译器则不会这么容易被欺骗它仍可以将getchar识别为函数。 21.2 C89标准库概述 现在简单讨论一下C89标准库中的头。本节可以作为一张“路线图”帮助你分辨出需要的是C标准库的哪一部分。本章及后续各章节会对每个头做更详细的介绍: assert.h诊断(断言) assert.h头24.1节仅包含assert宏它允许我们在程序中插入自我检查。一旦任何检查失败程序就会被终止。 ctype.h字符处理 ctype.h头23.5节提供用于字符分类及大小写转换的函数。 errno.h错误 errno.h头24.2节提供了errno“error number”。errno是一个左值lvalue可以在调用特定库函数后进行检测从而判断调用过程中是否有错误发生。 float.h浮点类型的特性 float.h头23.1节提供了用于描述浮点类型特性的宏包括值的范围及精度。 limits.h整数类型的大小 limits.h头23.2节提供了用于描述整数类型包括字符类型特性的宏包括它们的最大值和最小值。 locale.h本地化 locale.h头25.1节提供一些函数来帮助程序适应针对某个国家或地区的特定行为方式。这些与本地化相关的行为包括显示数的方式如用作小数点的字符、货币的格式如货币符号、字符集以及日期和时间的表示形式。 math.h数学计算 math.h头23.3节提供了常见的数学函数包括三角函数、双曲函数、指数函数、对数函数、幂函数、邻近舍入函数、绝对值运算函数以及取余函数。 setjmp.h非本地跳转 setjmp.h头24.4节提供了setjmp函数和longjmp函数。setjmp函数会“标记”程序中的一个位置随后可以用longjmp返回被标记的位置。这些函数可以用来从一个函数跳转到另一个仍然在活动中的函数中而绕过正常的函数返回机制。setjmp函数和longjmp函数主要用来处理程序执行过程中出现的严重问题。 signal.h信号处理 signal.h头24.3节提供了用于处理异常情况信号的函数包括中断和运行时错误。signal函数可以设置一个函数使系统会在给定信号发生后自动调用该函数raise函数用来产生信号。 stdarg.h可变参数 stdarg.h头26.1节提供了一些工具用于编写参数个数可变的函数就像printf和scanf函数一样。 stddef.h常用定义 stddef.h头21.4节提供了经常使用的类型和宏的定义。 stdio.h输入/输出 stdio.h头22.1节~22.8节提供了大量的输入/输出函数包括对顺序访问和随机访问文件的操作。 stdlib.h常用实用程序 stdlib.h头26.2节包含了大量无法归入其他头的函数。包含在stdlib.h中的函数可以将字符串转换成数、产生伪随机数、执行内存管理任务、与操作系统通信、执行搜索与排序以及在多字节字符与宽字符之间进行转换。 string.h字符串处理 string.h头23.6节提供了用于进行字符串操作包括复制、拼接、比较及搜索的函数以及对任意内存块进行操作的函数。 time.h日期和时间 time.h头26.3节提供相应的函数来获取时间和日期、操纵时间以及格式化时间的显示。 21.3 C99标准库更新 C99对标准库的改变主要分为以下三类: 新增头。在C99标准库中有9个头是C89中没有的。事实上其中3个iso646.h、wchar.h和wctype.h 在1995年修订C89时就增加到C中另外6个complex.h、fenv.h、inttypes.h、stdbool.h、stdint.h和tgmath.h是C99新增的。新增宏和函数。C99标准在一些已有的头中增加了宏和函数这些头主要有float.h、math.h和stdio.h。math.h头中增加了非常多的内容将专门用一节即23.4节来讲述。对已有函数的改进。一些已存在的函数包括printf和scanf在C99中具有了更多的功能。 接下来快速浏览一下C99标准库中新增的9个头就像在21.2节中浏览C89库中的头一样 complex.h复数算术 complex.h头27.4节定义了complex和I宏这两个宏对于复数运算来说非常有用。该头还提供了对复数进行数学运算的函数。 fenv.h浮点环境 fenv.h头27.6节提供了对浮点状态标志和控制模式的访问。例如程序可以测试标志来判断浮点数运算过程中是否发生了溢出或者设置控制模式来指定如何进行舍入。 inttypes.h整数类型格式转换 inttypes.h头27.2节定义了可用于stdint.h中声明的整数类型输入/输出的格式化字符串的宏还提供了处理最大宽度整数的函数。 iso646.h拼写转换 iso646.h头25.3节定义了可代表特定运算符包含字符、|、~、!和^的运算符的宏。当编程环境的本地字符集没有这些字符时这些宏非常有用。 stdbool.h布尔类型和值 stdbool.h头21.5节定义了bool、true和false宏同时还定义了一个可以用于测试这些宏是否已被定义的宏。 stdint.h整数类型 stdint.h头27.1节声明了指定宽度的整数类型并定义了相关的宏例如指定每种类型的最大值和最小值的宏同时也定义了用于构建具体类型的整型常量的带参数的宏。 tgmath.h泛型数学 在C99中math.h和complex.h头中的许多数学函数有多个版本。tgmath.h头27.5节中的泛型宏可以检测传递给它们的参数类型并替代为相应的math.h或complex.h中函数的调用。 wchar.h扩展的多字节和宽字符实用工具 wchar.h头25.5节提供了宽字符输入/输出和宽字符串操作的函数。 wctype.h宽字符分类和映射实用工具 wctype.h头25.6节是ctype.h的宽字符版本提供了对宽字符进行分类和修改的函数。 21.4 stddef.h常用定义 stddef.h头提供了常用类型和宏的定义但没有声明任何函数。定义的类型包括以下几个: ptrdiff_t。指针相减运算结果的类型。size_t。sizeof运算符返回的类型。wchar_t。一种足够大的、可以用于表示所有支持的地区的所有字符的类型。
以上这3种类型都是整数类型。其中ptrdiff_t必须是有符号类型size_t必须是无符号类型。关于wchar_t的更多细节见25.2节。
stddef.h头中还定义了两个宏。一个宏是NULL用来表示空指针。另一个宏是offsetof需要两个参数类型一种结构类型和成员指示符结构的一个成员。offsetof宏会计算结构的起点到指定成员间的字节数。 考虑下面的结构 struct s { char a; int b[2]; float c;
}; offsetof(struct s, a)的值一定是0C语言确保结构的第一个成员的地址与结构自身地址相同。我们无法确定地说出b和c的偏移量是多少。一种可能是offsetof(struct s, b)是1因为a的长度是1字节而offsetof(struct s, c)是9假设整数是32位。然而一些编译器会在结构中留下一些空洞不使用的字节见第16章的“问与答”部分从而会影响到offsetof产生的值。例如如果编译器在a后面留下了3字节的空洞那么b和c的偏移量分别是4和12。但这正是offsetof宏的魅力所在对任意编译器它都能返回正确的偏移量从而使我们可以编写可移植的程序。
offsetof有很多用途。例如假如我们需要将结构s的前两个成员写入文件但忽略成员c。我们不使用fwrite函数22.6节来写sizeof(struct s)字节因为这样会将整个结构写入。我们只需要写offsetof(struct s, c)字节。 最后一点一些在stddef.h中定义的类型和宏在其他头中也会出现。例如NULL宏不仅在C99的头wchar.h中有定义在locale.h、stdio.h、stdlib.h、string.h和time.h中也有定义。因此只有少数程序的确需要包含stddef.h。 21.5 stdbool.h布尔类型和值(C99) stdbool.h头定义了4个宏 bool定义为_Booltrue定义为1false定义为0__bool_true_false_are_defined定义为1。
我们已经见过很多使用bool、true和false的例子。对__bool_true_false_are_defined宏的应用相对少一些。在尝试定义自己的bool、true或false之前可以使用预处理指令如#if或者#ifdef来测试这个宏。 21.6 C11标准更新(C1X) 从C11开始对标准库的改变主要体现在以下几个方面: 新增头。在C11标准库中有5个头是之前没有的它们分别是stdatomic.h、threads.h、stdalign.h、uchar.h和stdnoreturn.h。新增宏和函数。C11标准在一些已有的头中增加了宏和函数这些头主要有float.h、complex.h、time.h等。对已有函数的改进和移除。一些已存在的函数包括printf和scanf在C11中具有了更多的功能。同时出于对安全性的考虑从头stdio.h中移除了gets函数并将它从新标准中废除。 接下来我们快速浏览一下C11标准库中新增的5个头: stdatomic.h原子类型和原子操作 stdatomic.h头定义了现有数据类型的原子类型并提供了大量的宏用于执行原子类型变量的初始化和读写操作。 threads.h多线程环境 threads.h头提供了线程的创建和管理函数以及互斥锁、条件变量和线程局部存储的功能。 stdalign.h数据对齐 stdalign.h头提供了4个宏定义21.7节。 uchar.h新的宽字符类型和实用工具 uchar.h头定义了新的宽字符类型char16_t和char32_t并提供了从多字节字符到这些宽字符类型的转换函数。 stdnoreturn.h函数指定符_Noreturn相关 stdnoreturn.h头非常简单只定义了一个宏noreturn21.8节。 21.7 stdalign.h:地址的对齐(C1X) stdalign.h头定义了4个宏 alignas定义为_Alignasalignof定义为_Alignof__alignas_is_defined定义为整型常量1__alignof_is_defined定义为整型常量1。
以上的后两个宏适合在预处理指令#if中使用。如果已经定义了这两个宏则说明另外两个宏alignas和alignof也被定义如果你想自行定义alignas和alignof应当先做这样的测试 21.8 stdnoreturn.h:宏noreturn的定义(C1X) 头stdnoreturn.h非常简单它只是定义了宏noreturn被定义为_Noreturn。 问与答 问1我注意到书中使用术语“标准头”而不是“标准头文件”。不使用“文件”有什么具体原因吗 答是的。依据C标准“标准头”不一定是文件。虽然大部分编译器确实将标准头以文件形式存储但标准头实际上可以直接内置在编译器自身中。 问214.3节描述了用带参数的宏替代函数的一些缺点。鉴于这些缺点为标准库函数提供同名的宏版本不是很危险吗 答根据C标准用于替代库函数的带参数的宏必须用圆括号“完全保护”起来而且只能对参数进行一次求值。这些规则可以避免14.3节提到的大多数问题。 写在最后 本文是博主阅读《C语言程序设计现代方法第2版·修订版》时所作笔记日后会持续更新后续章节笔记。欢迎各位大佬阅读学习如有疑问请及时联系指正希望对各位有所帮助Thank you very much!