2018-2019-1 20189221 《从问题到程序》第 4 周学习总结
第五章 C程序结构
实数类型和整数类型
- 实数类型共有三个,类型名分别是: **float, double, long double **
- 字符类型 :C 语言还有signed char 和unsigned char 两个字符类型,普通char 类型等价于这两者之一,
- 整数类型:基本的整数类型有三个:int, short int, long int
类型选择原则,这也是在大多数 C 程序里的 合理选择:
- 如果没有特殊需要,浮点数总采用 double 类型,因为它的精度和表示范围能满足一般要求(float的精度常常不够,long double可能降低效率)。
- 如果没有特殊需要,整数总采用int 类型,因为它是每个 C 系统里的 基本类型,必定能得到硬件的基本支持,其效率不会低于- 任何其他整数类型。
- 如果没有特殊需要,字符总采用char类型。
- 尽量少用各种unsigned类型,除非服务于某些特殊目的。
函数的作用是使人可以把一段计算抽象出来,封装(包装)起来,使之成为程序中的一个独立实体。
函数抽象的好处:
- 重复出现的程序片段被一个唯一的函数定义和一些形式简单的函数调用所取代,这样有可能使程序变得更简短而清晰。
- 由于整个程序里同样的计算片段仅描述一次,需要改造这部分计算时,就只要修改一个地方:改变函数的定义。程序的其他地方可能完全不需要修改。
- 函数定义和使用形成对程序复杂性的一种分解,使人在程序设计中可以孤立地考虑函数本身的定义与函数的使用问题,有可能提高程序开发的效率。
- 把具有独立逻辑意义的适当计算片段定义为函数后,函数可以看成是在更高层次上的程序基本操作。一层一层的函数定义可以使人可以站在一个个抽象层次上去看待和把握程序的意义,这对于开发大的软件系统是非常重要的。
C 标准库函数完成一些 常用的基本功能,包括基本输入和输出、文件操作、存储管理,以及其他一些常用功能函数,如数学函数、数据值的类型转换函数等。
-
字符分类函数
-
随机数生成函数
每个 C 程序里总有一个名为 main 的特殊函数,主函数是程序入口,定了整个程序执行的起点。程序里不能调用主函数,它将在程序开始执行时被自动调用。
-
函数调用
-
如果一段计算可以定义为函数,那么就应该将它定义为函数。
程序中可能有重复出现的相同或相似的计算片段,程序中具有逻辑独立性的片段(分解程序复杂性,使之更容易理解和把握) -
从外部看,一个函数实现了某种功能。只需知道它的名字和类型特征等。调用函数时遵从这些规定,提供数目和类型适当的实参,正确接受返回值,就能得到预期的计算结果。
-
任何 return 语句的执行将导致本函数的本次执行结束。
-
函数调用的形式是函数名后跟圆括号括起、逗号分隔的若干表达式,这些表达式称为实际参数,简称实参。调用函数时必须提供一组个数正确、类型合适的实参。
-
函数调用与参数值的传递 :
-
函数原型说明:
原型说明中参数表里的参数名可缺(可以只写类型)。即使在这里写参数名,所用名字也不必与函数定义用的名字一致。原型说明里的参数名可以起提示作用,也提倡给出有意义的名字,这将有利于函数的正确使用。
以函数原型作为媒介,保证函数定义和使用之间的一致性。
写原型时必须给出完整的类型特征描述
不写函数f的原型说明可能引起各种问题,如: -
如果编译程序后来遇到了 f 的定义,其返回值类型与它所做的假设不一致,编译程序有可能给出“函数重新定义”的错误信息。
-
假设f 在其他源文件定义,或为库函数,而且f 的类型返回类型与默认假设不符,那么编译不会发现错误,连接时也不检查,产生的可执行程序在执行时则可能出错。
-
如果函数调用的实参(个数或类型)与函数定义不一致,编译时不会发现错误,也不会自动生成类型转换,连接时也不检查。 终形成可执行程序里的语义错误。
我们应坚持的正确原则是: -
如果使用库函数,那么就必须在源文件前部用#include 命令包含必要的头文件(xxx.h 文件)。
-
对所有未能在使用前给出定义的函数(无论它是定义在本文件后面,还是在其他源文件里),都应给出正确完整的原型说明。
-
把原型说明写在源文件前面(不要写在函数内部),以使函数的定义点和所有使用点都能“看到” 同一个原型说明。
一个 C 语言源文件由一系列外部定义和外部说明构成:C 函数只能在外部定义(不允许在函数内定义函数),所有函数都定义在表层
- 以外部定义形式定义的变量称为外部变量或全局变量,常把外部变量定义写在源文件里所有函数定义之前
- 变量前给出有关的外部变量说明,下面要使用在其他地方定义的整型外部变量member和length:
extern int number, length;
定义和说明这两个术语的意义不同。定义要求建立被定义对象,而说明仅指明有这个东西存在,被说明的东西必须在其他地方建立(定义),否则这个说明就是无效的、没有意义的。对于变量,定义与说明的差异表现得非常明显。程序执行到变量定义处时将建立被定义的变量,为其分配存储值的位置。而变量说明只是通告“有这样一个变量存在,其性质是……”,这里并不实际建立变量,也没有存储安排问题。
外部变量可以在整个程序里使用,这也就意味着在整个程序内不能有重名的外部变量。否则目标文件连接时就会出现问题。由于连接时还要用到 C 语言的程序库,因此在编写程序时还要注意,所定义程序对象的名字不要与程序库里的名字冲突。
作用域与生存期
- 变量定义都有一个确定的作用范围,这个范围称为该变量定义的作用域,变量的作用域由变量定义的位置确定
- 变量在程序执行中存在的那段时期称为这个变量的存在期。
main也是普通函数,其内部定义也是局部的,同样不能在函数体之外使用。
自动变量:自动建立和撤消。复合语句开始执行时建立定义的所有变量只存在到该复合语句结束。复合语句结束时,内部定义的所有变量都撤消。
C 语言规定:当内层复合语句出现同名变量定义时,外层同名定义将被内层定义遮蔽。
同样的,外部定义的全局变量被函数里面的同名局部变量定义所遮蔽。
C 语言程序的加工分为三步:预处理、编译和连接
文件包含命令
- #include <文件名>: 预处理程序直接到某些指定目录中查找所需文件,目录指定方式由具体系统确定,通常指定几个系统目录。
- **#include "文件名" :预处理程序先在源文件所在目录中查找,找不到时再到指定目录查找。
文件包含命令的处理过程是:首先查找所需文件,找到后就用该文件的内容取代当前文件里这个包含命令行。替换进来的文件里仍可有预处理命令,它们也将被处理。
带参宏定义
带参数宏定义的形式是:
#define 宏名字(参数列表) 替代正文
宏定义和调用并不考虑类型问题
C 还提供了预处理命令#undef,取消已有的宏定义
#undef 宏名字
可以从这个命令出现的位置开始取消对宏名字已有的定义。
条件编译的预处理命令有四个:#if和#elif, #ifdef和 #ifndef
- 在可能的情况下,我们将尽量使用枚举常量
- 需要非整数值的常量,那就应该考虑用const
- 在这两种方式都不合适,而用宏定义又能带来特别的方便时,才应考虑用宏定义
为面向复杂的系统程序设计,C 语言特别提供了对二进制位的操作。
字位“否定” ~ 字位“或” |
字位“与” & 字位“异或” ^
- “~”:字位“否定”
- “|”:字位“或”
- “&”:字位“与”
- “^”:字位“异或”