Statistics
14
Views
0
Downloads
0
Donations
Support
Share
Uploader

高宏飞

Shared on 2026-06-14

Author[美] K. N. 金

第 2 版前言 在计算领域中,把显而易见的转变为有实用价值的,这一过程是“挫折”一词的 生动体现。 自本书第 1 版出版以来,基于 C 的语言大量兴起(其中最杰出的是 Java 和 C#),C++和 Perl 等相关语言也取得了更大的成就。尽管如此,C 语言仍然像当年一样流行,悄无声息地掌控着 世界上的许多软件。跟 1996 年一样,C 语言仍然是计算机领域里的通用语言。 然而,即便是 C 语言也必须随着时间而发展。C99 标准的发布催生了对本书第 2 版的需求, 而且,第 1 版涉及的 DOS 和 16 位处理器也趋于过时。同样,C11 和 C18 的发布促使修订版对 内容进行了全面更新,并在其他许多方面做了改进。 修订版新增内容 下面列出了这一版的新特色和所做的改进。  完整覆盖了 C89、C99 和 C1X 标准。这一版和第 1 版的最大差别就在于覆盖了 C99 和 C1X 标准。我的目标是覆盖 C89、C99 和 C1X 之间的每一个重要差别,包括新标准新增 的所有语言特性和库函数。新标准中的每一处改变都会清楚地用 C99 或者 C1X 标出来, 或者在节标题上,或者进行简短讨论时在正文左边空白处。这样做有两个目的:一是提 醒读者注意新标准中的改变,二是让那些对新标准不感兴趣或没有新标准编译器的读者 知道哪些内容可以跳过。新标准新增的许多内容可能只有特定的读者会感兴趣,但有些 新特性几乎对所有的 C 程序员都有用。这一版中有关 C1X 的内容(包括整个第 28 章) 是由李忠负责完成的,他修正了原书中过时的内容和不准确的表述,调整了部分术语的 中文译法,并更正了上一版中的翻译问题。  提供了对所有 C89、C99 和 C1X 库函数的快速参考。第 1 版附录 E 介绍了 C89 的所有 标准库函数,这一版附录 E 给出了 C89、C99 和 C1X 的所有库函数。  扩展了 GCC 的内容。自本书第 1 版出版以来,GCC(最初是 GNU C Compiler 的简称, 现在指 GNU Compiler Collection)得到了广泛应用。GCC 有很多优点,包括高性能、低 成本(不用花钱)以及在众多软硬件平台之间的可移植性等。由于认识到 GCC 日渐重 要,我在这一版中介绍了更多与 GCC 相关的信息,包括如何使用 GCC 以及常见的 GCC 出错消息和警告。截至目前,GCC 尚未在它的 C 标准库实现中添加多线程支持,在学 习本书第 28 章时,建议使用 Pelles C。尽管不那么有名,但它是当前对标准支持最好的 C 语言开发工具。  增加了对抽象数据类型的讨论。在第 1 版中,第 19 章重点讨论了 C++。这部分内容现 在看起来似乎作用不大,因为本书的读者可能已经学过 C++、Java 或者 C#了。在这一 版中,我将对 C++的介绍替换为讨论如何在 C 中建立抽象数据类型。  扩展了国际化特性的内容。这一版第 25 章更加详尽地讨论了 C 语言的国际化特性,重 点扩展了 Unicode 字符集及编码。 针对 CPU 和操作系统做了更新。当我编写本书第 1 版时,许多读者用的还是 16 位机和 DOS 操作系统,但现在情况不同了。在这一版中,我把讨论的重点放在 32 位机和 64 位 机上。鉴于 Linux 和其他版本 UNIX 的兴起,我们会对该类操作系统给予更多关注。不 过,在可能影响到 C 程序员的场合,我们也会提到 Windows 和 macOS 操作系统的相关 方面。  引入了多线程的内容。从 C11 开始,新标准的最大变化是引入了多线程和原子操作。这 部分内容较多,而且是非常新颖的内容,值得大书特书,并作为一个整体系统性地加以 介绍,为此我们把它放在第 28 章。市面上和 C 语言有关的图书多如牛毛,但据我们所 知,介绍多线程和原子操作的图书,这还是第一本。  更多的练习题和编程题。本书第 1版包括 311道习题,这一版有 500多(准确地说是 502) 道习题,分为两组:练习题与编程题。  练习题和编程题的答案。本书第 1 版的读者反馈最多的问题就是希望我提供习题的答案。 针对读者的这一需求,我将大约三分之一的练习题和编程题的答案放到了网上,见 https://exl.ptpress.cn:8442/ex/l/609033ea。这一特色对于那些没有选修相应大学课程却需 要检验自己工作的读者来说是非常有用的。提供了答案的练习题和编程题①都用 图标做 了标记。

AI Reading Assistant

Summary and highlights from this book's index; jump to passages in the text

Passage locations
Tags
No tags
Publish Year: 2021
Language: 英文
File Format: PDF
File Size: 7.4 MB
Support Statistics
¥.00 · 0times
Text Preview (First 20 pages)
Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

(This page has no text content)
(This page has no text content)
数字版权声明 图灵社区的电⼦子书没有采⽤用专有客户端, 您可以在任意设备上,⽤用⾃自⼰己喜欢的浏 览器器和PDF阅读器器进⾏行行阅读。 但您购买的电⼦子书仅供您个⼈人使⽤用,未 经授权,不不得进⾏行行传播。 我们愿意相信读者具有这样的良知和觉 悟,与我们共同保护知识产权。 如果购买者有侵权⾏行行为,我们可能对该 ⽤用户实施包括但不不限于关闭该帐号等维 权措施,并可能追究法律律责任。
(This page has no text content)
内 容 提 要 时至今日,C 语言仍然是计算机领域的通用语言之一,但今天的 C 语言已经和最初的时候大不相同了。 本书主要目的就是通过一种“现代方法”来介绍 C 语言,书中强调标准 C,强调软件工程,不再强调“手 工优化”。第 2 版修订版中不仅有 C99 中的新特性,还与时俱进地增加了 C11 和 C18 中的内容。本书分 为 C 语言的基础特性、C 语言的高级特性、C 语言标准库和参考资料 4 个部分。每章末尾的“问与答” 部分给出一系列与该章内容相关的问题及答案,此外还包含适量的习题。 本书是 C 开发人员的理想参考书,在国外也被众多大学作为 C 语言课程的教材。 Copyright © 2008, 1996 by W. W. Norton & Company, Inc. All rights reserved. No portion of this book may be reproduced in any form or by any means without the prior written permission of the publisher. 本书中文版由 W. W. Norton 公司授权人民邮电出版社有限公司独家出版。未经出版者书面许可,不 得以任何方式或途径复制或传播本书内容。 版权所有,侵权必究。 定价:129.80元 读者服务热线:(010)84084456 印装质量热线:(010)81055316 反盗版热线:(010)81055315 广告经营许可证:京东市监广登字 20170147 号 著    [美] K. N. 金 译    吕秀锋 黄 倩 审  校 李 忠 责任编辑 温 雪 责任印制 周昇亮 人民邮电出版社出版发行  北京市丰台区成寿寺路11号 邮编 100164  电子邮件 315@ptpress.com.cn 网址 https://www.ptpress.com.cn 北京      印刷 开本:787×1092 1/16 印张:43.5 字数:1273千字 2021年 7 月第 2 版 印数:102 001 — 108 000册 2021年 7 月北京第 1 次印刷 著作权合同登记号 图字:01-2021-0898号 ◆ ◆ ◆
第 2 版前言 1 新世纪的 C 语言“万宝全书” C 语言,从诞生至今,已经有接近 50 年的历史。从最早期的 NB(即 New B;中文读者看到 这个名字更能开怀一笑吧),到写出 UNIX 的 C,再到 ISO/IEC 9899 标准,C 语言在不知不觉中 经历了很多变化。唯一变化不大的地方,似乎就是它的流行程度了。在我当年学习 BASIC和 Pascal 时,少科站的王建德老师就对 C 非常推崇,说真正要用计算机干活还得用 C。所以,在我后来 有机会从 Apple II 转向 IBM PC 时,就顺理成章地用上了 Turbo C。从那时起,就从来没有出现 过一种语言,可以真正取代 C。在 TIOBE 编程社区指数上,C 长期位居前二;最近,在 Java 地 位有所下降的情况下,C 又重新回归榜首的宝座。因此,学习 C 语言一直有着很强的必要性。 C 的教科书可谓汗牛充栋,并有 C 语言发明人写的经典珠玉在前,一直为人所称颂。那么, 本书又有什么特点呢? 回答这个问题之前,我先来问你一下:当前的 C 标准是什么? ——如果你知道 C89,那说明你知道 C 是有标准的。嗯,这比不知道要好。 ——如果你回答 C99,那说明你知道 C 还在演化。不过,那都是上个世纪的事了,对不? ——如果你答出了 C11,你厉害,我相信大部分 C 开发者并不知道不仅 C++有 11 标准,C 也有一个 11 年的标准——虽然这个标准的存在感有点弱,特别是跟 C++11 相比的话。 ——最后,你居然知道 C18(也叫 C17)?太牛了,事实上,在读这本书之前,我都不确 定我之前有没有听说过 C18…… 而本书讨论了 C 的所有特性,从 C89 一直到 C18。内容足够新,也足够全面,这就是它的 最大特点。 克尼汉(Brian W. Kernighan)和里奇(Dennis M. Ritchie)的经典著作《C 程序设计语言 (第 2 版)》(以下简称为 K&R)出版于 1988 年,基本覆盖了 C89。显然,它不可能讨论任何属 于 C99 或是更新 C 标准的内容。目前,我们日常接触的 C 代码也许用到的 C11/C18 特性还不多, 但 C99 特性实际上已经挺多了:如果你的项目里用到了 uint8_t 或是 bool,那你就已经用到 了 C99。C 标准后续还有很多改进(并且大部分得到 GCC 等主流编译器的支持),读完这本书 你也就会基本了解这些内容了。 除了内容够新之外,书中还全面讨论了各种实际的环境和使用场景,包括 Windows、UNIX 和类 UNIX 操作系统(如 Linux)等,而不像 K&R 一样仅限于 UNIX。在 K&R 成书之际,微软 还处于 DOS 的年代(Windows 尚在研发中),为 UNIX 从业者所不齿;Mac 刚刚诞生,还非常 小众;Linux 则还需要一些年头才会问世。显然,K&R 不可能对它们进行讨论。而本书则讨论 了所有这些环境,甚至还包括了今天看来已经过时的 MS DOS。比如,在第 17 章的“问与答” 中提到了“Null pointer assignment”,那可真是我在 DOS 年代编程的梦魇了。虽然今天几乎应该 没人会遇到这个错误了,但我还是很高兴有本书终于清楚地描述了我当年写 C 程序时遇到过的 这个错误的原因。 作为一本教科书(而不是主要作为参考书),本书非常注意循序渐进,对复杂概念的讲述采 用了逐步推进的方法。拿作者自己的话来说:
2 新世纪的 C 语言“万宝全书” 针对有一定难度的主题,我采用了螺旋式的介绍方法。也就是说,对于较难的主 题先进行简要介绍,然后在后续章节中再进行一次或多次介绍,每次逐渐增加一些细 节内容。本书的进度是经过深思熟虑的。每章都按照循序渐进的方式进行组织,前后 内容由浅入深,相互呼应。对于大多数学生来说,这种循序渐进的方法是最合适的: 既能避免产生厌倦,又能防止“信息过载”。 举一个例子,第 3 章就出现了指针,但作者把对指针的完整讲解放到了第 11 章和后续的一 些其他章节中。尤其重要的是,作者把指针相关的复杂声明语法放到了第 18 章里,而没有早早 拿出来对初学者进行“劝退”。并且,在讲述复杂声明时(书中给出的例子是“int *(*x[10]) (void);”),作者给出了两条简单的规则帮助读者来解读,又给出图示展示解读的顺序,最后 还指出可以用 typedef 来简化声明。回想起我初学 C 时教科书中的复杂声明例子,以及知乎上仍 偶尔可见的让初学者更加糊涂的讲解指针的“例子”,我觉得作者的讲述方式真的对初学者非常 友好——尤其考虑到 C 的复杂声明需要从里往外读,实在有点“反人性”(可以对比一下一个 从外往里读的 C++的相似声明:“array<function<int*()>, 10> x;”)。 这种循序渐进不仅体现在跨章节的情况,在一章之内也有类似的处理,最典型的就是把很 多初学者不需要立即了解的知识放到了章尾的“问与答”里。比如,当我读到下面的句子时, “当我们把一个包含小数点的常量赋值给 float 型变量时,最好在该常量后面加一个字母 f”, 我有点奇怪作者为什么没有进一步解释。但作者在此处加上了“Q&A”的标记,对照着读一下 就清楚了。在“问与答”里,作者先给出了简短的解释,然后指出,更详细的解释要看后面的 第 7 章。这种逐步递进、而不是试图一下子就向初学者塞一大堆他们很可能暂时还不需要的知 识的做法,确实对初学者比较友好。 对于我这种已经熟悉 C 语言的人来说,“问与答”反倒是个有意思的冷知识来源。比如, 我之前也不知道,根据 C89 的规范,标识符只有前 6 个字符保证有效,而且不一定会区分大小 写!(幸好大部分实现提供了更好的保证。) 作为一部教科书,代码示例和习题自然是不可缺少的。书中示例和习题都非常充足,足以 满足教学和自学的需要。除了讲述清晰之外,内容的正确性也非常重要。有一部亚马逊上有很 多五星好评的 C 教科书,却被专家评为“彻头彻尾的胡说八道(Complete Nonsense)”,就是因 为作者用明白晓畅的散文写出了错误的内容,文字传达了错误的信息,代码也不能给出期望的 结果。对这本书,我可以很高兴地告诉大家,迄今为止我还没发现任何错误,中文翻译也非常 流畅易懂。考虑到英文版的惊人售价(超过一百美元!),中文版读者真的很幸运。 循序渐进这个优点,在一小部分(能力强的)人眼里也可能成为缺点:他们会认为本书节 奏过慢(亚马逊读者评论的原话是“takes a painfully slow approach to covering even the most basic programming”),而欣赏 K&R 的简约风格(英文版正文部分不足 200 页,不到本书的四分之一)。 我虽然对 C 语言已经相当熟悉,阅读本书时倒也没有这种感觉;我同样相信绝大部分读者会喜 欢这种循序渐进的风格。而即使对于喜欢简约风格的人来说,我也需要指出,K&R 距今已经超 过 30 年,不管是背景知识(没有也不可能介绍 Linux 和 GCC),还是代码风格(如函数前面省 略 int 返回值声明),都有过时之嫌。而后人根据对 C 的多年实践经验总结出的陷阱和教训, 克尼汉和里奇也不可能先知先觉地进行描述。尤其对于初学者,我们确实需要更好的教材。 语言在进化,新的时代需要新的经典。不管是初学者,还是需要了解 C 的新标准特性的老 程序员,都能从这样一本好书中获益。 吴咏炜 Boolan 首席咨询师
第 2 版前言 1 第 2 版前言 在计算领域中,把显而易见的转变为有实用价值的,这一过程是“挫折”一词的 生动体现。 自本书第 1 版出版以来,基于 C 的语言大量兴起(其中最杰出的是 Java 和 C#),C++和 Perl 等相关语言也取得了更大的成就。尽管如此,C 语言仍然像当年一样流行,悄无声息地掌控着 世界上的许多软件。跟 1996 年一样,C 语言仍然是计算机领域里的通用语言。 然而,即便是 C 语言也必须随着时间而发展。C99 标准的发布催生了对本书第 2 版的需求, 而且,第 1 版涉及的 DOS 和 16 位处理器也趋于过时。同样,C11 和 C18 的发布促使修订版对 内容进行了全面更新,并在其他许多方面做了改进。 修订版新增内容 下面列出了这一版的新特色和所做的改进。  完整覆盖了 C89、C99 和 C1X 标准。这一版和第 1 版的最大差别就在于覆盖了 C99 和 C1X 标准。我的目标是覆盖 C89、C99 和 C1X 之间的每一个重要差别,包括新标准新增 的所有语言特性和库函数。新标准中的每一处改变都会清楚地用 C99 或者 C1X 标出来, 或者在节标题上,或者进行简短讨论时在正文左边空白处。这样做有两个目的:一是提 醒读者注意新标准中的改变,二是让那些对新标准不感兴趣或没有新标准编译器的读者 知道哪些内容可以跳过。新标准新增的许多内容可能只有特定的读者会感兴趣,但有些 新特性几乎对所有的 C 程序员都有用。这一版中有关 C1X 的内容(包括整个第 28 章) 是由李忠负责完成的,他修正了原书中过时的内容和不准确的表述,调整了部分术语的 中文译法,并更正了上一版中的翻译问题。  提供了对所有 C89、C99 和 C1X 库函数的快速参考。第 1 版附录 E 介绍了 C89 的所有 标准库函数,这一版附录 E 给出了 C89、C99 和 C1X 的所有库函数。  扩展了 GCC 的内容。自本书第 1 版出版以来,GCC(最初是 GNU C Compiler 的简称, 现在指 GNU Compiler Collection)得到了广泛应用。GCC 有很多优点,包括高性能、低 成本(不用花钱)以及在众多软硬件平台之间的可移植性等。由于认识到 GCC 日渐重 要,我在这一版中介绍了更多与 GCC 相关的信息,包括如何使用 GCC 以及常见的 GCC 出错消息和警告。截至目前,GCC 尚未在它的 C 标准库实现中添加多线程支持,在学 习本书第 28 章时,建议使用 Pelles C。尽管不那么有名,但它是当前对标准支持最好的 C 语言开发工具。  增加了对抽象数据类型的讨论。在第 1 版中,第 19 章重点讨论了 C++。这部分内容现 在看起来似乎作用不大,因为本书的读者可能已经学过 C++、Java 或者 C#了。在这一 版中,我将对 C++的介绍替换为讨论如何在 C 中建立抽象数据类型。  扩展了国际化特性的内容。这一版第 25 章更加详尽地讨论了 C 语言的国际化特性,重 点扩展了 Unicode 字符集及编码。
2 第 2 版前言  针对 CPU 和操作系统做了更新。当我编写本书第 1 版时,许多读者用的还是 16 位机和 DOS 操作系统,但现在情况不同了。在这一版中,我把讨论的重点放在 32 位机和 64 位 机上。鉴于 Linux 和其他版本 UNIX 的兴起,我们会对该类操作系统给予更多关注。不 过,在可能影响到 C 程序员的场合,我们也会提到 Windows 和 macOS 操作系统的相关 方面。  引入了多线程的内容。从 C11 开始,新标准的最大变化是引入了多线程和原子操作。这 部分内容较多,而且是非常新颖的内容,值得大书特书,并作为一个整体系统性地加以 介绍,为此我们把它放在第 28 章。市面上和 C 语言有关的图书多如牛毛,但据我们所 知,介绍多线程和原子操作的图书,这还是第一本。  更多的练习题和编程题。本书第 1版包括 311道习题,这一版有 500多(准确地说是 502) 道习题,分为两组:练习题与编程题。  练习题和编程题的答案。本书第 1版的读者反馈最多的问题就是希望我提供习题的答案。 针对读者的这一需求,我将大约三分之一的练习题和编程题的答案放到了网上,见 https://exl.ptpress.cn:8442/ex/l/609033ea。这一特色对于那些没有选修相应大学课程却需 要检验自己工作的读者来说是非常有用的。提供了答案的练习题和编程题①都用 图标做 了标记。  有密码保护的教师网站。我为这一版建了一个新的教师资源网站(通过 https://exl.ptpress. cn:8442/ex/l/609033ea 访问),给出了其余练习题和编程题的答案以及大部分章节的 PowerPoint 讲义。教师可以通过 cbook@knking.com 与我联系。请使用贵校的邮箱地址并 给出贵系网站的链接,以便我核实您的身份。 此外,我在这一版中对全书的文字和解释说明做了改进。这些改变所需的工作量很大,过 程很辛苦:每句话都检查过并(在必要的时候)重新写过。 尽管这一版改动很大,我仍然尽可能多地保持了原有的章节编号。尽管第 27 章和第 28 章 的内容是全新的,但其他许多章都有新增的内容,少数原有章节的顺序也有所变动。这一版删 去了一个附录(C 语言语法),但新增了 C1X、C99 和 C89 的附录。 目标 这一版的目标与第 1 版一致。  清晰易读,并尽可能带有趣味性。对普通读者来说,许多 C 语言的书过于简洁,而某些 甚至不是编写得一塌糊涂,就是平淡无趣。我试图对 C 语言进行清晰、全面的讲解,并 用适当的幽默来激发读者的阅读兴趣。  适用于广泛的读者。我假设本书的读者都至少有一点点编程经验,但不需要掌握某种具 体的编程语言。我尽量减少“行话”并定义用到的每一个术语。同时,为了鼓励初学者, 我还会尝试将某些高级内容从基本主题中分离出来。  有权威性,但不是学究气十足。为了避免武断地决定应该包含哪些内容、不应该包含哪 些内容,我尽量涵盖了所有 C 语言的特性和库函数。同时,为了避免给读者造成负担, 我还省略了一些不必要的细节。 —————————— ① 《C 语言程序设计:现代方法(第 2 版·修订版)习题解答》由人民邮电出版社图灵公司出版,敬请读者关注。
第 2 版前言 3  具备简单易学的组织结构。根据多年教授 C 语言的经验,我强调循序渐进地展示 C 语言 特性的重要性。针对有一定难度的主题,我采用了螺旋式的介绍方法。也就是说,对于 较难的主题先进行简要介绍,然后在后续章节中再进行一次或多次介绍,每次逐渐增加 一些细节内容。本书的进度是经过深思熟虑的。每章都按照循序渐进的方式进行组织, 前后内容由浅入深,相互呼应。对于大多数学生来说,这种循序渐进的方法是最合适的: 既能避免产生厌倦,又能防止“信息过载”。  深入探讨语言特性。我的目标不是仅描述语言的每个特性,并展示应用该特性的几个简 单示例,而是尝试深入讲解每一个特性,并且探讨如何将其应用到实际问题中。  强调编码风格。对每位 C 程序员来说,采用统一的编码风格是非常重要的。但是,与指 定某种风格相比,我更愿意给出多种编码风格,让读者根据自己的喜好做出选择,因为 了解多种编码风格对阅读别人的程序是很有帮助的(程序员经常要花费大量时间阅读别 人的程序)。  避免依赖任何特定的计算机、编译器或操作系统。C 语言可以应用在许多平台上,所以 我尽量避免使编写的程序依赖于任何特定的计算机、编译器或操作系统。所有程序都经 过精心设计,可以移植到多种平台上去。  用图示来阐明关键概念。我在书中加入了尽可能多的图,因为我认为图对于理解 C 语言 的许多方面都至关重要。特别地,我尽可能地通过图来显示不同计算阶段的数据状态, 以此来动态地展示算法。 现代方法到底指的是什么 本书最重要的目标之一就是通过一种“现代方法”来介绍 C 语言。我试图通过以下这些途 径来实现这一目标。  正确看待 C 语言。我没有把 C 语言看作唯一值得学习的编程语言,而是把它作为众多实 用语言中的一种来介绍。我在书中提到了最适合用 C 语言编程的应用类型。此外,我还 展示了如何扬长避短地使用 C 语言。  强调 C 语言的标准版本。我尽可能少地关注 C89 标准之前的 C 语言,只是零星地提到 了经典(K&R)C 语言(Brian W. Kernighan 和 Dennis M. Ritchie 所著的《C 程序设计语 言》第 1 版中所描述的 1978 版 C 语言)。附录 D 列出了 C89 和经典 C 之间的主要差异。  揭穿神话。现今的编译器常常与过去的 C 语言基本假设不一致,我很乐于揭穿 C 语言的 某些神话,并挑战一些存在了很久的 C 语言信条(例如,指针的算术运算一定比数组下 标操作快)。我重新审查了 C 语言的旧惯例,保留了那些仍然有帮助的惯例。  强调软件工程。我把 C 语言视为一种成熟的软件工程工具,着眼于如何运用 C 语言来处 理大规模程序设计过程中产生的问题。本书强调程序要易读、可维护、可靠且容易移植, 尤其重视信息隐藏。  推迟介绍C语言的底层特性。虽然这些特性对于那些用C语言编写的系统来说非常有用, 但现在它们已经不那么适用了,因为 C 语言的应用比以前广泛得多。本书没有像诸多其 他 C 语言书那样把这部分内容放在前面介绍,而是推迟到第 20 章再讲述。  不再强调“手工优化”。许多书会指导读者编写一些技巧性较强的代码,以获得程序效 率的些许提高。如今优化的 C 语言编译器随处可见,这些编程技巧往往已经不必要了; 事实上,它们反而会降低程序的运行效率。
4 第 2 版前言 “问与答”部分 每章的末尾都有一个“问与答”部分,汇集了与本章内容相关的问题及其答案。“问与答” 部分包括以下内容。  常见问题。我尽力回答了某些频繁出现在我的课堂里、其他图书中及与 C 语言相关的新 闻组里的问题。  对一些难以理解的问题的进一步讨论和澄清。虽然具有多种编程语言经验的读者会满足 于简明扼要的说明和少量的示例,但是缺乏经验的读者需要更多的内容以帮助理解。  非主流的问题。某些问题所引出的技术问题并不是所有读者都感兴趣的。  某些对普通读者来说过于超前或深奥的内容。这类问题都用星号(*)做了标记。好学 且有一定编程经验的读者也许希望立刻深入研究这些问题,而另外一些读者则需要在首 次阅读时跳过这部分内容。提示:这类问题往往引用后续章节的内容。  C 语言编译器之间的常见差异。我讨论了某些特定编译器所提供的一些频繁使用的(非 标准)特性。 “问与答”部分中的某些问题与对应章中的具体内容直接相关,我用一个专门的图标 来 标记这些具体内容,以提示读者有附加信息可用。 其他特色 除了“问与答”部分,我还加入了许多有用的特色,其中很多都用简单而独特的图标做了 标记。  警告( )警示读者一些常见的缺陷。C 语言以其陷阱多而出名,要记录所有的陷阱非 常困难。我试着挑选出了一些最常见或最重要的缺陷供大家参考。  交叉引用( )提供一种类似超文本的能力来定位信息。多数引用指向后续章节中的内 容,也有一些引用指向先前的内容供读者回顾。  惯用法是 C 语言程序中常见的代码模式。它被标记出来以便于速查参考。  可移植性技巧给出了编写不依赖于特定计算机、编译器或操作系统的程序所需的提示。  附加说明包含一些严格来讲并不属于C语言的内容,但每位熟练的C程序员都应该知道。 (下面的“源代码”给出了附加说明的示例。)  附录提供有价值的参考资料信息。 程序 选择程序示例并不是件轻松的工作。如果程序过于简洁和做作,那么读者将无法体会如何 将这些特性应用于现实世界。如果程序过于真实,那么它的要点将很容易被埋没在过多的细节 中。我采取了折中方案。在首次介绍时,先通过小而简单的示例使概念清晰,然后再逐步建立 完整的程序。我没有使用过长的程序,因为根据我个人的经验,教师没有时间介绍这些内容, 学生也不会有耐心去阅读。但是,我并没有忽视编写大规模程序时会出现的问题,相关内容在 第 15 章和第 19 章中进行了详细的介绍。 这一版的程序有些小变化。在大多数情况下,main 函数的格式为 int main (void){...}。 这一改变既反映了业界的惯例,又能够与 C99 兼容:C99 要求每个函数都有一个显式的返回类型。
第 2 版前言 5 源 代 码 本书中所有程序的源代码都可以从 https://exl.ptpress.cn:8442/ex/l/609033ea 下载①。有关本书 的更新、校正和最新消息也可以在此获得。 读者 本书是为大学本科阶段的 C 语言课程编写的教材。具有其他高级语言或汇编语言的编程经 验会对阅读本书很有帮助,不过这些经验对于会用计算机的读者(我以前的一位编辑称他们为 “熟练的初学者”)来说并不是必需的。 因为本书内容齐备、自成一体,并且既可用于学习又可作为参考,所以它非常适合作为其 他一些课程的辅助读物,如数据结构、编译器设计、操作系统、计算机图形学、嵌入式系统及 其他要用 C 语言进行项目设计的课程。“问与答”部分以及对实际问题的强调,使得本书对于 培训班学员和自学 C 语言的人来说也很有吸引力。 组织结构 本书分为 4 个部分。  C 语言的基本特性。第 1~10 章包含的 C 语言内容足以帮助读者编写出使用数组和函数 的单文件程序。  C 语言的高级特性。第 11~20 章建立在前面各章内容的基础上,内容有一定的难度,深 入介绍了指针、字符串、预处理器、结构、联合、枚举以及 C 语言的底层特性。此外, 第 15 章和第 19 章提供了程序设计方面的指导。  C 语言标准库。第 21~28 章集中介绍 C 语言库——与编译器相关联的庞大函数集合。其 中一部分内容适合课堂讲解,但大部分材料更适合作为参考。  参考资料。附录 A 给出了 C 语言运算符的完整列表。附录 B 描述了 C1X 和 C99 之间的 主要差别。附录 C 描述了 C99 和 C89 之间的主要差别。附录 D 讨论了 C89 和经典 C 之 间的差异。附录 E 按字母顺序列出了 C89、C99 以及 C1X 标准库中的全部函数,并为每 个函数给出了详尽的说明。附录 F 列出了 ASCII 字符集。还有一个带注解的延伸阅读列 表为读者指明了其他的信息来源。 全面讲授 C 语言的课程应该按顺序覆盖前 20 章的内容,并根据需要增加第 21~28 章中的 一些内容(其中讨论了文件输入/输出的第 22 章最为重要)。短期课程可以忽略以下内容而不失 连贯性:8.3 节(C99 中的变长数组)、9.6 节(递归)、12.4 节(指针和多维数组)、14.5 节(其 他指令)、17.7 节(指向函数的指针)、17.8 节(受限指针)、17.9 节(弹性数组成员)、18.6 节 (内联函数)、第 19 章(程序设计)、20.2 节(结构中的位域)和 20.3 节(其他底层技术)。 —————————— ① 本书源代码也可以在图灵网站本书主页(ituring.cn/book/2873)免费注册下载。本书中文版勘误也可在此提交。 ——编者注
6 第 2 版前言 练习题和编程题 作为一本教材,拥有多样化的精选习题显然是非常必要的。这一版既有练习题(不需要写 出完整程序的简短习题)又有编程题(需要编写或修改完整程序的习题)。 有些练习题的答案不是显而易见的(有人称其为“刁钻问题”)。因为 C 语言程序经常包含 这类代码的大量案例,所以我认为有必要提供一些这样的练习,并用星号(*)做了标注。一定 要谨慎地对待有星号的习题:要么格外小心,认真考虑;要么干脆绕开它。 反馈 为了保证本书内容准确,我付出了极大的努力。然而,任何这种篇幅的书都不可避免地会 有一些错误。如果读者发现了错误,请通过 cbook@knking.com 这个电子邮箱与我联系。我也同 样期望听到读者的其他反馈,比如,你觉得哪些内容特别有用,哪些内容没什么用,希望添加 哪些内容等。 致谢 首先,我要感谢本书的编辑——Norton 出版社的 Fred McFarland 和 Aaron Javsicas。本书的 编辑工作最初由 Fred 负责,随后 Aaron 加入并付出了极大努力使本书得以完成。同时,还要感 谢副主编 Kim Yi、文字编辑 Mary Kelly、生产经理 Roy Tedoff 和编辑助理 Carly Fraser。 以下同事对这一版的部分或全部书稿进行了审阅,在此致以诚挚的谢意:Markus Bussmann (多伦多大学)、Jim Clarke(多伦多大学)、Karen Reid(多伦多大学)和 Peter Seebach (comp.lang.c.moderated 新闻组的主持人)。其中需要特别提到的是 Jim 和 Peter,他们的详细 审阅使这一版避免了许多错误。再次感谢第 1 版书稿的审稿人(按姓氏字母排序):Susan Anderson-Freed、Manuel E. Bermudez、Lisa J. Brown、Steven C. Cater、Patrick Harrison、Brian Harvey、Henry H. Leitner、Darrell Long、Arthur B. Maccabe、Carolyn Rosner 和 Patrick Terry。 我收到了第 1 版读者反馈的许多有用的意见,感谢每一位花时间提意见的读者。佐治亚州 立大学的学生和同事也向我反馈了不少有价值的意见。Ed Bullwinkel 和他的妻子 Nancy 阅读了 手稿的很多内容,在此我也要感谢他们。我还要特别感谢我的系主任 Yi Pan,他非常支持我的 这项工作。 感谢我的妻子 Susan Cole 一如既往地支持着我。还有我们的猫咪 Dennis、Pounce 和 Tex, 在完成本书的过程中,它们一直陪伴着我。有时,Pounce 和 Tex 的争吵使我在深夜写作时仍能 保持清醒。 最后,我还要感谢已故的 Alan J. Perlis①。他的警句出现在本书每一章的开始。20 世纪 70 年代中期我在耶鲁大学求学期间,曾有幸在 Alan 的指导下进行过短暂的学习。我想如果他知道 自己的警句出现在一本 C 语言书中,一定会非常高兴。 —————————— ① Alan J. Perlis(1922—1990)是计算机科学先驱,1966 年首届图灵奖得主。——编者注
目 录 第 1 章 C 语言概述 ........................................... 1 1.1 C 语言的历史 ............................................. 1 1.1.1 起源 ................................................. 1 1.1.2 标准化 ............................................. 1 1.1.3 基于 C 的语言 ................................ 2 1.2 C 语言的优缺点 ......................................... 3 1.2.1 C 语言的优点 ................................. 3 1.2.2 C 语言的缺点 ................................. 4 1.2.3 高效地使用 C 语言 ........................ 5 问与答 .................................................................. 5 第 2 章 C 语言基本概念 .................................. 7 2.1 编写一个简单的 C 程序 ............................ 7 2.1.1 编译和链接 ..................................... 8 2.1.2 集成开发环境 ................................. 9 2.2 简单程序的一般形式 ................................. 9 2.2.1 指令 ................................................. 9 2.2.2 函数 ................................................. 9 2.2.3 语句 ............................................... 10 2.2.4 显示字符串 ................................... 11 2.3 注释 ........................................................... 11 2.4 变量和赋值 ............................................... 13 2.4.1 类型 ............................................... 13 2.4.2 声明 ............................................... 13 2.4.3 赋值 ............................................... 14 2.4.4 显示变量的值 ............................... 15 2.4.5 初始化 ........................................... 16 2.4.6 显示表达式的值 ........................... 17 2.5 读入输入 ................................................... 17 2.6 定义常量的名字 ....................................... 18 2.7 标识符 ....................................................... 19 2.8 C 程序的书写规范 ................................... 21 问与答 ................................................................ 23 练习题 ................................................................ 25 编程题 ................................................................ 26 第 3 章 格式化输入/输出 ............................... 28 3.1 printf函数 ............................................. 28 3.1.1 转换说明 ....................................... 29 3.1.2 转义序列 ....................................... 31 3.2 scanf函数 ............................................... 31 3.2.1 scanf函数的工作方法 ................ 32 3.2.2 格式串中的普通字符 ................... 34 3.2.3 易混淆的 printf函数和 scanf函数 ................................... 34 问与答 ................................................................ 35 练习题 ................................................................ 37 编程题 ................................................................ 38 第 4 章 表达式 .................................................. 40 4.1 算术运算符 ............................................... 40 4.2 赋值运算符 ............................................... 44 4.2.1 简单赋值 ....................................... 44 4.2.2 左值 ............................................... 45 4.2.3 复合赋值 ....................................... 45 4.3 自增运算符和自减运算符 ....................... 46 4.4 表达式求值 ............................................... 47 4.5 表达式语句 ............................................... 50 问与答 ................................................................ 50 练习题 ................................................................ 52 编程题 ................................................................ 54 第 5章 选择语句 .............................................. 56 5.1 逻辑表达式 ............................................... 56 5.1.1 关系运算符 ................................... 56 5.1.2 判等运算符 ................................... 57 5.1.3 逻辑运算符 ................................... 57 5.2 if语句 ...................................................... 58 5.2.1 复合语句 ....................................... 59 5.2.2 else子句 ...................................... 59 5.2.3 级联式 if语句 ............................. 61 5.2.4 “悬空 else”的问题 .................. 63 5.2.5 条件表达式 ................................... 63 5.2.6 C89 中的布尔值 ........................... 64 5.2.7 C99 中的布尔值 ..................... 65 5.3 switch语句 ............................................. 66 问与答 ................................................................ 69
2 目 录 练习题 ................................................................ 72 编程题 ................................................................ 74 第 6 章 循环 ...................................................... 77 6.1 while语句 ............................................... 77 6.2 do语句 ...................................................... 80 6.3 for语句 ................................................... 82 6.3.1 for语句的惯用法 ........................ 83 6.3.2 在 for语句中省略表达式 ........... 83 6.3.3 C99 中的 for语句 ................. 84 6.3.4 逗号运算符 ................................... 84 6.4 退出循环 ................................................... 86 6.4.1 break语句 ................................... 87 6.4.2 continue语句 ............................. 87 6.4.3 goto语句 ..................................... 88 6.5 空语句 ....................................................... 90 问与答 ................................................................ 92 练习题 ................................................................ 94 编程题 ................................................................ 95 第 7章 基本类型.............................................. 97 7.1 整数类型 ................................................... 97 7.1.1 C99 中的整数类型 ................. 99 7.1.2 整型常量 ....................................... 99 7.1.3 C99 中的整型常量 ............... 100 7.1.4 整数溢出 ..................................... 100 7.1.5 读/写整数 .................................... 101 7.2 浮点类型 ................................................. 102 7.2.1 浮点常量 ..................................... 103 7.2.2 读/写浮点数 ................................ 103 7.3 字符类型 ................................................. 104 7.3.1 字符操作 ..................................... 104 7.3.2 有符号字符和无符号字符 ......... 105 7.3.3 算术类型 ..................................... 105 7.3.4 转义序列 ..................................... 106 7.3.5 字符处理函数 ............................. 107 7.3.6 用 scanf和 printf读/写 字符 ............................................. 108 7.3.7 用 getchar和 putchar读/写 字符 ............................................. 108 7.4 类型转换 ................................................. 110 7.4.1 常规算术转换 ............................. 111 7.4.2 赋值过程中的转换 ..................... 112 7.4.3 C99 中的隐式转换 ............... 113 7.5.2 类型定义和可移植性 ................. 116 7.6 sizeof运算符 ....................................... 117 问与答 .............................................................. 7.4.4 强制类型转换 ............................. 114 7.5 类型定义 ................................................. 115 7.5.1 类型定义的优点 ......................... 115 117 练习题 .............................................................. 120 编程题 .............................................................. 121 第 8 章 数组 .................................................... 124 8.1 一维数组 ................................................. 124 8.1.1 数组下标 ..................................... 124 8.1.2 数组初始化 ................................. 126 8.1.3 指示器 .................................. 127 8.1.4 对数组使用 sizeof运算符 ....... 129 8.2 多维数组 ................................................. 130 8.2.1 多维数组初始化 ......................... 131 8.2.2 常量数组 ..................................... 132 8.3 C99 中的变长数组 ........................... 134 问与答 .............................................................. 135 练习题 .............................................................. 136 编程题 .............................................................. 138 第 9 章 函数 .................................................... 141 9.1 函数的定义和调用 ................................. 141 9.1.1 函数定义 ..................................... 144 9.1.2 函数调用 ..................................... 145 9.2 函数声明 ................................................. 147 9.3 实际参数 ................................................. 149 9.3.1 实际参数的转换 ......................... 150 9.3.2 数组型实际参数 ......................... 151 9.3.3 变长数组形式参数 ............... 153 9.3.4 在数组参数声明中使用 static .................................. 154 9.3.5 复合字面量 ........................... 155 9.4 return语句 ........................................... 155 9.5 程序终止 ................................................. 156 9.6 递归 ......................................................... 157 9.7 泛型选择 ........................................... 161 问与答 .............................................................. 163 练习题 .............................................................. 166 编程题 .............................................................. 169 第 10 章 程序结构 ......................................... 171 10.1 局部变量 ............................................... 171 10.1.1 静态局部变量 ......................... 172 10.1.2 形式参数 ................................. 172 10.2 外部变量 ............................................... 172 10.2.1 示例:用外部变量实现栈 ..... 172 10.2.2 外部变量的利与弊 ................. 173
录 目 3 10.3 程序块 ................................................... 177 10.4 作用域 ................................................... 178 10.5 构建 C 程序 .......................................... 179 问与答 .............................................................. 185 练习题 .............................................................. 185 编程题 .............................................................. 186 第 11 章 指针 .................................................. 188 11.1 指针变量 ............................................... 188 11.2 取地址运算符和间接寻址运算符 ....... 189 11.2.1 取地址运算符 ......................... 189 11.2.2 间接寻址运算符 ..................... 190 11.3 指针赋值 ............................................... 191 11.4 指针作为参数 ....................................... 192 11.5 指针作为返回值 ................................... 195 问与答 .............................................................. 196 练习题 .............................................................. 198 编程题 .............................................................. 199 第 12 章 指针和数组 .................................... 201 12.1 指针的算术运算 ................................... 201 12.1.1 指针加上整数 ......................... 202 12.1.2 指针减去整数 ......................... 202 12.1.3 两个指针相减 ......................... 203 12.1.4 指针比较 ................................. 203 12.1.5 指向复合字面量的指针 ...... 203 12.2 指针用于数组处理 ............................... 204 12.3 用数组名作为指针 ............................... 206 12.3.1 数组型实际参数(改进版) ..... 207 12.3.2 用指针作为数组名 ................. 208 12.4 指针和多维数组 ................................... 209 12.4.1 处理多维数组的元素 ............. 209 12.4.2 处理多维数组的行 ................. 210 12.4.3 处理多维数组的列 ................. 210 12.4.4 用多维数组名作为指针 ......... 210 12.5 C99 中的指针和变长数组 ............. 211 问与答 .............................................................. 212 练习题 .............................................................. 213 编程题 .............................................................. 215 第 13 章 字符串 ............................................. 217 13.1 字面串 ................................................... 217 13.1.1 字面串中的转义序列 ............. 217 13.1.2 延续字面串 ............................. 218 13.1.3 如何存储字面串 ..................... 218 13.1.4 字面串的操作 ......................... 219 13.2 字符串变量 ........................................... 13.1.5 字面串与字符常量 ................. 219 220 13.2.1 初始化字符串变量 ................. 220 13.2.2 字符数组与字符指针 ............. 221 13.3 字符串的读和写 ................................... 222 13.3.1 用 printf函数和 puts函数 写字符串 ................................. 222 13.3.2 用 scanf函数读字符串 ......... 223 13.3.3 逐个字符读字符串 ................. 224 13.4 访问字符串中的字符 ........................... 225 13.5 使用 C 语言的字符串库 ....................... 226 13.5.1 strcpy函数 ........................... 226 13.5.2 strlen函数 ........................... 227 13.5.3 strcat函数 ........................... 228 13.5.4 strcmp函数 ........................... 229 13.6 字符串惯用法 ....................................... 231 13.6.1 搜索字符串的结尾 ................. 232 13.6.2 复制字符串 ............................. 233 13.7 字符串数组 ........................................... 235 问与答 .............................................................. 238 练习题 .............................................................. 241 编程题 .............................................................. 243 第 14 章 预处理器 ......................................... 246 14.1 预处理器的工作原理 ........................... 246 14.2 预处理指令 ........................................... 248 14.3 宏定义 ................................................... 248 14.3.1 简单的宏 ................................. 249 14.3.2 带参数的宏 ............................. 250 14.3.3 #运算符 ................................... 252 14.3.4 ##运算符 ................................. 253 14.3.5 宏的通用属性 ......................... 254 14.3.6 宏定义中的圆括号 ................. 254 14.3.7 创建较长的宏 ......................... 255 14.3.8 预定义宏 ................................. 256 14.3.9 C99 中新增的预定义宏 ... 257 14.3.10 空的宏参数 .................... 258 14.3.11 参数个数可变的宏 ......... 259 14.3.12 __func__标识符 ........... 260 14.4 条件编译 ............................................... 260 14.4.1 #if指令和#endif指令 ........ 260 14.4.2 defined运算符 ..................... 261 14.4.3 #ifdef指令和#ifndef 指令 ......................................... 261 14.4.4 #elif指令和#else指令 ...... 262 14.4.5 使用条件编译 ......................... 262 14.5 其他指令 ............................................... 263 14.5.1 #error指令 ........................... 263
4 目 录 14.5.2 #line指令 ............................. 264 14.5.3 #pragma指令 ......................... 265 14.5.4 _Pragma运算符 .............. 265 问与答 .............................................................. 266 练习题 .............................................................. 268 第 15 章 编写大型程序 ................................ 272 15.1 源文件 ................................................... 272 15.2 头文件 ................................................... 273 15.2.1 #include指令 ....................... 273 15.2.2 共享宏定义和类型定义 ......... 274 15.2.3 共享函数原型 ......................... 275 15.2.4 共享变量声明 ......................... 277 15.2.5 嵌套包含 ................................. 278 15.2.6 保护头文件 ............................. 278 15.2.7 头文件中的#error指令 ....... 279 15.3 把程序划分成多个文件 ....................... 279 15.4 构建多文件程序 ................................... 285 15.4.1 makefile................................... 285 15.4.2 链接期间的错误 ..................... 287 15.4.3 重新构建程序 ......................... 287 15.4.4 在程序外定义宏 ..................... 289 问与答 .............................................................. 289 练习题 .............................................................. 291 编程题 .............................................................. 292 第 16 章 结构、联合和枚举 ....................... 293 16.1 结构变量 ............................................... 293 16.1.1 结构变量的声明 ..................... 293 16.1.2 结构变量的初始化 ................. 295 16.1.3 指示器 .............................. 295 16.1.4 对结构的操作 ......................... 296 16.2 结构类型 ............................................... 296 16.2.1 结构标记的声明 ..................... 297 16.2.2 结构类型的定义 ..................... 298 16.2.3 结构作为参数和返回值 ......... 298 16.2.4 复合字面量 ...................... 299 16.2.5 匿名结构 .......................... 300 16.3 嵌套的数组和结构 ............................... 301 16.3.1 嵌套的结构 ............................. 301 16.3.2 结构数组 ................................. 302 16.3.3 结构数组的初始化 ................. 302 16.4 联合 ....................................................... 309 16.4.1 用联合来节省空间 ................. 310 16.4.2 用联合来构造混合的数据 结构 ........................................ 312 16.4.3 为联合添加“标记字段” ..... 312 16.4.4 匿名联合 ........................... 313 16.5 枚举 ....................................................... 314 16.5.1 枚举标记和类型名 ................. 315 16.5.2 枚举作为整数 ......................... 315 16.5.3 用枚举声明“标记字段” ..... 316 问与答 .............................................................. 316 练习题 .............................................................. 318 编程题 .............................................................. 323 第 17 章 指针的高级应用 ............................ 324 17.1 动态存储分配 ....................................... 324 17.1.1 内存分配函数 ......................... 325 17.1.2 空指针 ..................................... 325 17.2 动态分配字符串 ................................... 326 17.2.1 使用 malloc函数为字符串 分配内存 ................................. 326 17.2.2 在字符串函数中使用动态 存储分配 ................................. 327 17.2.3 动态分配字符串的数组 ......... 327 17.3 动态分配数组 ....................................... 329 17.3.1 使用 malloc函数为数组 分配存储空间 ......................... 329 17.3.2 calloc函数 ........................... 330 17.3.3 realloc函数 ......................... 330 17.4 释放存储空间 ....................................... 331 17.4.1 free函数 ............................... 332 17.4.2 “悬空指针”问题 ................. 332 17.5 链表 ....................................................... 332 17.5.1 声明结点类型 ......................... 333 17.5.2 创建结点 ................................. 333 17.5.3 ->运算符 ................................ 334 17.5.4 在链表的开始处插入结点 ..... 335 17.5.5 搜索链表 ................................. 337 17.5.6 从链表中删除结点 ................. 338 17.5.7 有序链表 ................................. 339 17.6 指向指针的指针 ................................... 344 17.7 指向函数的指针 ................................... 345 17.7.1 函数指针作为参数 ................. 345 17.7.2 qsort函数 ............................. 346 17.7.3 函数指针的其他用途 ............. 347 17.8 受限指针 ......................................... 350 17.9 弹性数组成员 ................................. 351 问与答 .............................................................. 352 练习题 .............................................................. 355 编程题 .............................................................. 358
目 录 5 第 18 章 声明 ................................................. 359 18.1 声明的语法 ........................................... 359 18.2 存储类型 ............................................... 360 18.2.1 变量的性质 ............................. 361 18.2.2 auto存储类型 ....................... 361 18.2.3 static存储类型 ................... 362 18.2.4 extern存储类型 ................... 363 18.2.5 register存储类型 ............... 363 18.2.6 函数的存储类型 ..................... 364 18.2.7 小结 ........................................ 365 18.3 类型限定符 ........................................... 366 18.4 声明符 ................................................... 366 18.4.1 解释复杂声明 ......................... 368 18.4.2 使用类型定义来简化声明 ..... 369 18.5 初始化器 ............................................... 369 18.6 内联函数 ........................................ 371 18.6.1 内联定义 ................................. 371 18.6.2 对内联函数的限制 ................. 372 18.6.3 在 GCC 中使用内联函数 ....... 372 18.7 函数指定符_Noreturn和头 <stdnoreturn.h> ......................... 373 18.8 静态断言 ......................................... 373 问与答 .............................................................. 374 练习题 .............................................................. 377 第 19 章 程序设计 ......................................... 379 19.1 模块 ....................................................... 379 19.1.1 内聚性与耦合性 ..................... 381 19.1.2 模块的类型 ............................. 381 19.2 信息隐藏 ............................................... 381 19.3 抽象数据类型 ....................................... 385 19.3.1 封装 ........................................ 385 19.3.2 不完整类型 ............................. 386 19.4 栈抽象数据类型 ................................... 386 19.4.1 为栈抽象数据类型定义 接口 ........................................ 386 19.4.2 用定长数组实现栈抽象 数据类型 ................................. 388 19.4.3 改变栈抽象数据类型中 数据项的类型 ......................... 389 19.4.4 用动态数组实现栈抽象 数据类型 ................................. 390 19.4.5 用链表实现栈抽象数据 类型 ........................................ 392 19.5 抽象数据类型的设计问题 ................... 394 19.5.1 命名惯例 ................................. 394 19.5.2 错误处理 ................................. 394 19.5.3 通用抽象数据类型 ................. 394 19.5.4 新语言中的抽象数据类型 ..... 395 问与答 .............................................................. 395 练习题 .............................................................. 396 编程题 .............................................................. 397 第 20 章 底层程序设计 ................................ 398 20.1 位运算符 ............................................... 398 20.1.1 移位运算符 ............................. 398 20.1.2 按位取反运算符、按位与 运算符、按位异或运算符 和按位或运算符 ..................... 399 20.1.3 用位运算符访问位 ................. 400 20.1.4 用位运算符访问位域 ............. 401 20.2 结构中的位域 ....................................... 403 20.3 其他底层技术 ....................................... 405 20.3.1 定义依赖机器的类型 ............. 405 20.3.2 用联合来提供数据的多个 视角 ......................................... 405 20.3.3 将指针作为地址使用 ............. 407 20.3.4 volatile类型限定符 ............ 409 20.4 对象的对齐 ..................................... 410 20.4.1 对齐运算符_Alignof ...... 410 20.4.2 对齐指定符_Alignas和头 <stdalign.h> ................. 410 问与答 .............................................................. 411 练习题 .............................................................. 411 编程题 .............................................................. 413 第 21 章 标准库 ............................................. 414 21.1 标准库的使用 ....................................... 414 21.1.1 对标准库中所用名字的 限制 ......................................... 415 21.1.2 使用宏隐藏的函数 ................. 415 21.2 C89 标准库概述 ................................... 416 21.3 C99 标准库更新 ...................................... 417 21.4 <stddef.h>:常用定义 ...................... 418 21.5 <stdbool.h>:布尔类型和值 ...... 419 21.6 C11 标准库更新 .............................. 419 21.7 <stdalign.h>:地址的对齐 ......... 420 21.8 <stdnoreturn.h>:宏noreturn的 定义 .................................................. 420 问与答 .............................................................. 420 练习题 .............................................................. 421 编程题 .............................................................. 422
6 目 录 第 22 章 输入/输出 ....................................... 423 22.1 流 ........................................................... 423 22.1.1 文件指针 ................................. 424 22.1.2 标准流和重定向 ..................... 424 22.1.3 文本文件与二进制文件 ......... 425 22.2 文件操作 ............................................... 426 22.2.1 打开文件 ................................. 426 22.2.2 模式 ........................................ 427 22.2.3 关闭文件 ................................. 428 22.2.4 为打开的流附加文件 ............. 428 22.2.5 从命令行获取文件名 ............. 429 22.2.6 临时文件 ................................. 430 22.2.7 文件缓冲 ................................. 431 22.2.8 其他文件操作 ......................... 432 22.3 格式化的输入/输出 .............................. 433 22.3.1 …printf函数 ......................... 433 22.3.2 …printf转换说明 ................. 433 22.3.3 C99 对…printf转换说明 的修改 .............................. 435 22.3.4 …printf转换说明示例 ......... 436 22.3.5 …scanf函数 ........................... 438 22.3.6 …scanf格式串 ....................... 438 22.3.7 …scanf转换说明 ................... 439 22.3.8 C99 对…scanf转换说明 的改变 .............................. 441 22.3.9 scanf示例 ............................. 441 22.3.10 检测文件末尾和错误条件 ... 442 22.4 字符的输入/输出 .................................. 444 22.4.1 输出函数 ................................. 444 22.4.2 输入函数 ................................. 444 22.5 行的输入/输出 ...................................... 446 22.5.1 输出函数 ................................. 446 22.5.2 输入函数 ................................. 447 22.6 块的输入/输出 ...................................... 447 22.7 文件定位 ............................................... 448 22.8 字符串的输入/输出 .............................. 451 22.8.1 输出函数 ................................. 451 22.8.2 输入函数 ................................. 452 问与答 .............................................................. 452 练习题 .............................................................. 455 编程题 .............................................................. 458 第 23 章 库对数值和字符数据的支持 ...... 462 23.1 <float.h>:浮点类型的特性............. 462 23.2 <limits.h>:整数类型的大小 .......... 464 23.3 <math.h>:数学计算(C89) ............ 465 23.3.1 错误 ......................................... 465 23.3.2 三角函数 ................................. 466 23.3.3 双曲函数 ................................. 466 23.3.4 指数函数和对数函数 ............. 467 23.3.5 幂函数 ..................................... 467 23.3.6 就近舍入函数、绝对值 函数和取余函数 ..................... 468 23.4 <math.h>:数学计算 ..................... 468 23.4.1 IEEE 浮点标准 ....................... 469 23.4.2 类型 ......................................... 470 23.4.3 宏 ............................................. 470 23.4.4 错误 ......................................... 470 23.4.5 函数 ......................................... 471 23.4.6 分类宏 ..................................... 471 23.4.7 三角函数 ................................. 472 23.4.8 双曲函数 ................................. 472 23.4.9 指数函数和对数函数 ............. 473 23.4.10 幂函数和绝对值函数 ........... 474 23.4.11 误差函数和伽马函数 ........... 474 23.4.12 就近舍入函数 ....................... 475 23.4.13 取余函数 ............................... 476 23.4.14 操作函数 ............................... 477 23.4.15 最大值函数、最小值函数 和正差函数 ........................... 477 23.4.16 浮点乘加 ............................... 478 23.4.17 比较宏 ................................... 478 23.5 <ctype.h>:字符处理 ........................ 479 23.5.1 字符分类函数 ......................... 479 23.5.2 字符大小写映射函数 ............. 481 23.6 <string.h>:字符串处理 .................. 482 23.6.1 复制函数 ................................. 482 23.6.2 拼接函数 ................................. 483 23.6.3 比较函数 ................................. 484 23.6.4 搜索函数 ................................. 485 23.6.5 其他函数 ................................. 487 问与答 .............................................................. 488 练习题 .............................................................. 488 编程题 .............................................................. 490 第 24 章 错误处理 ......................................... 491 24.1 <assert.h>:诊断 .............................. 491 24.2 <errno.h>:错误 ................................ 492 24.3 <signal.h>:信号处理 ...................... 494 24.3.1 信号宏 ..................................... 494 24.3.2 signal函数 ........................... 494 24.3.3 预定义的信号处理函数 ......... 495
目 录 7 24.3.4 raise函数 ............................. 496 24.4 <setjmp.h>:非局部跳转 .................. 497 问与答 .............................................................. 499 练习题 .............................................................. 500 第 25 章 国际化特性 .................................... 502 25.1 <locale.h>:本地化 .......................... 502 25.1.1 类项 ........................................ 503 25.1.2 setlocale函数 ..................... 503 25.1.3 localeconv函数 ................... 504 25.2 多字节字符和宽字符 ........................... 507 25.2.1 多字节字符 ............................. 507 25.2.2 宽字符..................................... 508 25.2.3 Unicode 和通用字符集 .......... 508 25.2.4 Unicode 编码 .......................... 509 25.2.5 多字节/宽字符转换函数 ........ 510 25.2.6 多字节/宽字符串转换函数 .... 511 25.3 双联符和三联符 ................................... 512 25.3.1 三联符..................................... 512 25.3.2 双联符..................................... 513 25.3.3 <iso646.h>:拼写替换 ........ 513 25.4 通用字符名 .................................... 514 25.5 <wchar.h>:扩展的多字节和宽 字符实用工具 ................................ 514 25.5.1 流的倾向性 ............................. 515 25.5.2 格式化宽字符输入/输出 函数 ........................................ 515 25.5.3 宽字符输入/输出函数 ............ 517 25.5.4 通用的宽字符串实用工具 ..... 518 25.5.5 宽字符时间转换函数 ............. 521 25.5.6 扩展的多字节/宽字符转换 实用工具 ................................. 521 25.6 <wctype.h>:宽字符分类和映射 实用工具 ........................................ 523 25.6.1 宽字符分类函数 ..................... 524 25.6.2 可扩展的宽字符分类函数 ..... 525 25.6.3 宽字符大小写映射函数 ......... 525 25.6.4 可扩展的宽字符大小写 映射函数 ................................. 525 25.7 <uchar.h>:改进的 Unicode 支持 ................................................. 526 25.7.1 带 u、U和 u8前缀的 字面串..................................... 526 25.7.2 可重启动的多字节/ 宽字符转换函数 ..................... 527 问与答 .............................................................. 528 练习题 .............................................................. 529 编程题 .............................................................. 530 第 26 章 其他库函数 .................................... 531 26.1 <stdarg.h>:可变参数 ...................... 531 26.1.1 调用带有可变参数列表的 函数 ......................................... 533 26.1.2 v…printf函数 ....................... 533 26.1.3 v…scanf函数 ................... 534 26.2 <stdlib.h>:通用的实用工具 .......... 534 26.2.1 数值转换函数 ......................... 535 26.2.2 伪随机序列生成函数 ............. 537 26.2.3 与环境的通信 ......................... 539 26.2.4 搜索和排序实用工具 ............. 540 26.2.5 整数算术运算函数 ................. 542 26.2.6 地址对齐的内存分配 ....... 542 26.3 <time.h>:日期和时间 ....................... 542 26.3.1 时间处理函数 ......................... 543 26.3.2 时间转换函数 ......................... 545 问与答 .............................................................. 550 练习题 .............................................................. 552 编程题 .............................................................. 553 第 27 章 C99 对数学计算的新增支持 ..... 554 27.1 <stdint.h>:整数类型 ................ 554 27.1.1 <stdint.h>类型 .................... 555 27.1.2 对指定宽度整数类型的 限制 ......................................... 555 27.1.3 对其他整数类型的限制 ......... 556 27.1.4 用于整型常量的宏 ................. 557 27.2 <inttypes.h>:整数类型的格式 转换 ................................................ 557 27.2.1 用于格式指定符的宏 ............. 557 27.2.2 用于最大宽度整数类型的 函数 ......................................... 558 27.3 复数 ................................................ 559 27.3.1 复数的定义 ............................. 559 27.3.2 复数的算术运算 ..................... 560 27.3.3 C99 中的复数类型 ................. 561 27.3.4 复数的运算 ............................. 561 27.3.5 复数类型的转换规则 ............. 562 27.4 <complex.h>:复数算术运算 ...... 562 27.4.1 <complex.h>宏 ..................... 562 27.4.2 CX_LIMITED_RANGE编译 提示 ......................................... 563 27.4.3 <complex.h>函数.................. 564 27.4.4 三角函数 ................................. 564 27.4.5 双曲函数 ................................. 565 27.4.6 指数函数和对数函数 ............. 565