Chap 15
一、头文件
- #include指令
告诉预处理器打开指定的文件,并把此文件的内容插入到当前文件中
• 格式1 #include <文件名>
用于属于c语言自身库的头文件。搜寻系统头文件所在的目录(或多个目录)
不要再包含自己编写的头文件时使用尖括号。
• 格式2 #include “文件名”
用于所有其他头文件,也包含任何自己编写的文件。
先搜索当前目录,然后搜寻系统头文件所在的目录(或多个目录)
• 格式3 #include 记号
记号 是任意预处理记号序列。预处理器会扫描这些记号,并替换遇到的宏。宏完成替换后,#include指令一定与前两种之一相匹配。此格式优点是可以用宏来定义文件名,而不用将文件名”硬拷贝“到指令里面去。如P250所示。 - 共享宏定义和类型定义
放头文件的好处:不把定义复制到需要它们的源文件中可以节约时间;程序易于修改,改变宏定义或类型定义只需编辑单独的头文件;不需要担心由于源文件包含相同宏或类型的不同定义而导致的矛盾。 - 共享函数原型
把函数的原型放进一个头文件中,在所有调用该函数的地方包含这个头文件。
除了在调用函数的源文件中包含f.h,还需在f.c中包含它,从而使编译器可以验证f.h中函数f的原型和f.c中函数定义相匹配。
任何含有函数f调用的文件可能会需要f.c中的其他一些函数,然而,仅用于文件f.c的函数不需要再头文件中声明。 - 共享变量声明
声明一个外部变量 extern int I;
extern 告诉编译器,变量i是在程序中的其它位置定义的(很可能是在不同的源文件中),因此不需要为i分配空间。
通常把共享变量的声明放在头文件中,需要访问特定变量的源文件可以包含相应的头文件。此外,含有变量定义的源文件需要包含含有相应变量声明的头文件,这样编译器就可以检查声明与定义是否匹配。
但 共享变量有重大的缺点。我们在19.2节学习如何设计不需要共享变量的程序。 - 保护头文件
避免多次包含。用 #ifndef 和 #endif 指令来封闭文件的内容。 - 头文件中的 #error 指令
用来检查不应该包含头文件的条件。
二、构建多文件程序
• 编译 对程序中的每个源文件分别进行编译(不需要编译头文件。编译包含头文件的源文件时会自动编译头文件的内容)。对于每个源文件,编译器会产生一个包含目标代码的文件(目标文件 object file)。
• 链接 链接器把上一步产生的目标文件和库函数的代码结合在一起生成可执行的程序。
链接器的一个职责是要解决编译器遗留的外部引用问题。(外部引用发生在一个文件中的函数调用另一个文件中调用的函数或访问另一个文件中定义的变量时。)
- makefile
这个文件包含构建程序的必要信息。
makefile不仅列出了作为程序的一部分的那些文件,还描述了文件之间的依赖关系。
见笔记或书。 - 链接期间的错误
常见:拼写错误、缺失文件、缺失库 - 重新构建程序
- 在程序外定义宏
Q&A
----我需要调用foo.c中的函数,所以包含了匹配的头文件foo.h,程序可以通过编译,但不能通过链接,为什么?
==在c语言中编译和链接是完全独立的。头文件存在是为了给编译器而不是链接器提供信息。如果希望调用foo.c中的函数,那么需要确保对foo.c进行了编译,还要确保链接器知道必须在foo.c的目标文件中搜索该函数。通常情况下,这意味着在程序的makefile或工程文件中命名foo.c。
Chap 19 程序设计
一、模块
- 模块化设计的过程:
应定义哪些模块,每个模块应提供哪些服务,各个模块之间的相互联系 - 模块的性质
高内聚性:模块的元素之间彼此紧密相关,可看作为了同一目标而相互合作。
低耦合性:模块间尽量相互独立。 - 模块的类型
数据池:相关的变量或者常量的集合,c语言中一般作为头文件。不建议在头文件中放变量,但建议放常量。
库:相关函数的集合
抽象对象:对隐藏的数据结构进行操作的函数的集合
抽象数据类型(ADT):将具体的数据实现方式隐藏起来的数据类型