一些术语
数据对象:用于存储值的数据存储区域统称为数据对象。使用变量名就是标识对象的一种方法。
左值:标识特定数据对象的名称或表达式。
右值:能赋值给可修改左值的量。
多重赋值
int jane, tarzan, cheeta;
cheeta = tarzan = jane = 68;
多重赋值中赋值顺序是从右往左的。
- 和 +
作为加减法,- + 是二元运算符。
作为一元运算符,-代表负,C90标准才新加了+作为正号的表示。
除法 /
整数相除的结果只保留结果的整数部分。
浮点数相除的结果是浮点数。
整数浮点数混合相除会把整数先转成浮点型,再进行计算。
对于负数除法,结果的保留方式有两种。以结果为-3.8为例:
一种是四舍五入为-4,因为-4小于-3.8。
另一种直接丢去小数,也就是-3,成为**“趋零截断”**。
C99之前不同实现采用不同方法,C99后统一采用趋零截断。
sizeof运算符和size_t类型
需要注意的是,sizeof并不是一个函数,而是一个运算符。
sizeof返回size_t类型的值。
C头文件系统会使用typedef把size_t作为unsigned int或者unsigned long的别名。
C99新增了%zd说明用于size_t类型的转换说明。
递增 / 递减运算符 ++ / –
会比x = x + 1;或者x = x - 1这样的形式效率更高,但随着编译器更智能这样的优势可能会消失,因为可能编译器会自动把x = x + 1优化为x++。
前缀使用时(++x,–x),使用值之前递增 / 递减。
后缀使用时(x++,x–),使用值之后递增 / 递减。
自增自减运算符应小心使用,在一个变量多次出现在同个函数的多个参数或者一个表达式中,由于不同系统对参数运算的顺序不同,结果可能无法预料。如:
printf("%d %d\n", n, n++ * n--);//一个变量多次出现在同个函数的多个参数
x = n++ * n--; //一个变量多次出现在一个表达式中
类型转换
1.类型转换出现在表达式中,unsigned 或 signed的char和short都被自动转换为int,如有必要则自动转换为unsigned int(如果short和int大小相同,unsigned short比int大,则转换为unsigned int)。
2.涉及两种类型的运算,两个值分别抓换为两种类型的更高级别。
3.赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型,这个过程可能导致类型升级或降级。
4.被当作函数参数传递时,char和short被转换为int,float被转换为double。
待转换值和目标类型不匹配
1.目标类型无符号整型,待赋值的为整数,额外的位被忽略,也就是地位截断。
2.目标类型是有符号整数,待赋值为整数,结果因实现而异。
3.目标类型整型,待赋值浮点数,该行为未定义
4.浮点值转为整数,浮点值截断。(取整)
带参数的函数
实参和形参类型不符时,会自动进行转换。假如形参是int,传入char会自动升级成int,不会出错。如果传入float,参数不匹配自动转成double,会出现错误。
实参和形参
实参 actual argument 或 actual parameter
形参 formal argument 或 formal parameter
但C99规定,对于实参使用术语argument,对于形参使用术语parameter。
逻辑判断
所有非0值都是真,只有0是假。
构建等于的判断时可以把常量放在前面可以防止误把==输入成=。
if(n = 3); //不报错
if(n == 3);//不报错
if(3 = n); //报错
if(3 == n);//不报错
C99提供stdbool.h头文件,让bool成为_Bool的别名。
出口条件循环:do while
while循环和for循环都是入口条件循环,每次迭代前检查测试条件。
do while是出口条件循环,循环的每次迭代后检查测试条件。这保证至少执行循环体中的内容一次。
使用do while要避免一下形式:
do
{
询问用户是否继续
其他行为
} while(回答是yes);
这样即使用户回答No,也仍然会执行一次其他行为。
如何选择循环
入口条件循环和出口条件循环一般选择入口条件循环(依需求而定)。某些应用中要求一开始不满足条件就直接跳过,要选择入口条件循环。
一般而言,循环涉及初始化和更新变量用for循环比较合适,其他情况用while更好。
对于涉及索引计数的循环,用for循环更合适。
函数声明
如果在main后定义函数,则需要进行声明,如果在main前面定义了函数就不需要。但是,这并不是C的标准风格,main通常只提供整个程序的框架,最好把main放在所有函数前面。因此前置声明必不可少。
ctype.h系列的字符函数
ctype.h头文件包含了一系列专门处理字符的函数的原型。例如isalpha函数判断参数是否是一个字母。
逻辑运算符的顺序
C保证逻辑表达式的求值顺序是从左往右的。且程序从一个逻辑对象执行到另一个逻辑对象之前,所有的副作用都会生效。而且C保证一旦发现某个元素让整个表达式无效,便立即停止求值。
比如&&中,左边表达式为0,立刻判断逻辑值为0并退出。
||左边表达式为1,立刻判断逻辑值为1并退出。
switch语句
switch的圆括号中测试表达式的值应该是整数,case后只能是整数常量。
缓冲区
无缓冲输入,用户输入的字符程序可以立即使用该内容。以下代码:
int main(){
char ch;
while((ch = getchar()) != '#')
putchar(ch);
return 0;
}
输入Hello, there. I would[enter]
会显示以下内容:
HHeelllloo, tthheerree… II wwoouulldd[enter]
缓冲输入:用户输入的字符被收集存储在一个被称为缓冲区(buffer)的临时存储区,按下enter后,程序才可以使用用户输入的字符。
为什么要有缓冲区?
若干字符作为一个块进行传输比逐个发送这些字符节约时间。其次,如果打错字符,可以直接通过键盘修正错误,按下enter后传输正确的输入。
但在某些交互式程序中也需要无缓冲输入,比如游戏。
缓冲分为两类:完全缓冲I/O,行缓冲I/O。
完全缓冲输入指当缓冲区填满才刷新缓冲区,通常出现在文件输入中。缓冲区大小取决于系统,常见的大小是512字节和4096字节。
行缓冲I/O指的是出现换行符时刷新缓冲区。键盘输入通常是行缓冲输入,按下enter后才刷新缓冲区。
能否进行无缓冲输入取决于计算机系统。
文件操作
各操作系统对于文件操作的底层I/O都不一样,所以C通过标准I/O包来处理文件。具体的C实现负责处理不同系统的差异以便用户使用统一的界面。
例如,用if(ch == ‘\n’)检查换行符,即使某些系统用回车符和换行符的组合来标记行末尾,I/O函数会在两种表示法之间相互转换。
流
概念上看,C程序处理的是流(stream)而不是直接处理文件。流是一个实际输入或输出映射的理想化数据流。意味着不同属性和不同种类的输入,由属性统一的流表示。于是,打开文件的过程就是把流与文件相关联,而且读写都通过流来完成。
C把输入和输出设备视为存储设备上的普通文件,尤其是把键盘和显示设备视为每个C程序自动打开的文件。stdin流表示键盘输入,stdout流表示键盘输出,getchar(), putchar(), printf(), scanf()等标准I/O包成员处理这两个流。