对标题和序号稍加修改。
5.1 循环简介
本章仍从一个程序开始。
#include<stdio.h>
int main(void)
{
int val=1;
while(val<11)
{
printf("%d ",val);
val=val+1;
}
printf("\n");
return 0;
}
运行后输出结果:
1 2 3 4 5 6 7 8 9 10
程序用while循环打印了1~10,减少了printf()
的语句数。简单解释while循环的原理:程序第1次遇到while时,会检查()
入口条件是否为真,为真就执行语句块(block)的内容,执行完块中最后一句会返回入口重复检查条件,为真就执行语句块并往复,为假就结束循环继续执行块后语句。
5.2 基本运算符
运算符(operator)表示算数运算。基本运算符:=
、+
、-
、*
和/
。
5.2.1 赋值运算符 =
基本格式:变量名=待赋值
,结合性自右向左。无效格式:2021=year;
、int a=b=c=5;
。
数据对象
赋值表达式语句的目的时把值储存到内存位置上。用于储存值的数据存储区域统称为数据对象(data object),对象就是存储数据的实体。=
左侧必须引用一个存储位置,最简单的方法就是使用变量名,变量名是标识对象的方法之一。
左值、右值
左值(lvalue)是用于标识数据对象的名称或表达式(见5.4.1),是存储位置的标签。早期C中,左值表达:
- 指定一个对象,引用内存中的地址。
- 作为可修改的值用在
=
左侧。
因为用const
定义的只读变量不符合第二点,所以引入可修改的左值(modifiable lvalue)标识可修改的对象,也称为对象定位置(object locator value)。
右值(rvalue)可以是常量、变量或表达式,指的是能赋值给可修改左值的量,也称为表达式的值(value of an expression),其本身并不是左值。
运算对象
运算对象(operand)即运算符操作的对象,可以是常量、变量或两者的组合。
5.2.2 加法运算符 +
加法运算符(addition operator)使其两侧的值相加。运算对象可以是变量也可以是常量。
income=salary+bribes;
income是可修改的左值,salary和bribes亦是,但salary+bribes是右值。
5.2.3 减法运算 -
减法运算符(subtraction operator)使其左侧数减右侧数。
value=224.00-24.00; //把200.00赋给value
5.2.4 符号运算符 -和+
-
还可用于标明或改变一个值的代数符号。示例:
int rocky=-12;
int smokey=-12;//把12赋给smokey
+
(C90新增)不会改变运算对象的值或符号。
dozen=+12;
早期C不允许以上示例。
-
和+
是一元运算符(unary operator),只需一个运算对象。=
、+
、-
、*
和/
是二元运算符(binary operator),需要两个运算对象。
例如,-(12+8)
中,+
的运算对象是12和8,-
的运算对象是(12+8)
。
5.2.5 乘法运算符 *
cm=2.54*inch;
2.54乘以inch的值再赋给cm。
打印5~10及其平方的示例程序:
#include<stdio.h>
int main(void)
{
int num=5;
while (num<11)
{
printf("%4d %6d\n",num,num*num);
num=num+1;
}
return 0;
}
运行后输出结果:
5 25
6 36
7 49
8 64
9 81
10 100
5.2.6 除法运算符 /
格式为被除数/除数
,例如,four=120/3.0
。然而整数除法与浮点数除法不同。浮点数除法结果是浮点数。整数除法是没有小数的部分,这被称为截断(truncation)。示例程序:
#include<stdio.h>
int main(void)
{
printf("integer division: 5/4 is %d\n",5/4);
printf("integer division: 6/3 is %d\n",6/3);
printf("integer division: 7/4 is %d\n",7/4);
printf("floating division: 7./4. is %.2f\n",7./4.);
printf("mixed division: 7./4 is %.2f\n",7./4);
return 0;
}
运行后输出结果:
integer division: 5/4 is 1
integer division: 6/3 is 2
integer division: 7/4 is 1
floating division: 7./4. is 1.75
mixed division: 7./4 is 1.75
整数会截断计算结果的整个小数部分且不会四舍五入。混合类型除法中,编译器会把两个运算对象自动转换成想用的类型。
早期使用“舍入过程采用小于或等于浮点数的最大整数”的方法实现负数的除法,例如将-3.8四舍五入为-4。而C99规定了一种趋零截断方法,将-3.8转换成-3。
5.2.7 运算符优先级
C语言通过优先级来解决运算符操作顺序问题。每个运算符都有自己的优先级。如果两个运算符优先级相同且处理同一个运算对象时按结合性的顺序进行。下表为基本运算符的优先级(从高到低)和结合性。
运算符 | 结合性 |
---|---|
() | 从左往右 |
+ -(一元) | 从右往左 |
* / | 从左往右 |
+ -(二元) | 从左往右 |
= | 从右往左 |
例如,25.0+60.0*6/2
的运算顺序是:60.0*6
=>/2
=>25.0+
。
5.2.8 优先级和求值顺序
当运算符共享一个运算对象时,优先级决定求值顺序。例如,6*12+5*20
,先进行两个乘法运算再进行加法运算。
结合性适用于共享同一运算对象且优先级相同的运算符。例如,12/3*2
,按结合性从左往右进行运算,先算12/3
结果为4在乘以2,即8。
5.3 其他运算符
5.3.1 求模运算符 %
求模运算符(modulus operator)用于整数运算符。返回值是左侧整数除以右侧整数的余数(remainder)。例如5%2
的值就是1。还可以通过a-(a/b)*b
来求a%b
。
C99中,负数求模中如果第1个运算对象是负数,求模结果就是负数,如果第1个运算对象是正数结果就是正数。示例:
11/5得2,11%5得1
11/-5得-2,11%-5得1
-11/-5得2,11%-5得-1
-11/5得-2,-11%5得-1
5.3.2 递增递减运算符
递增运算符(increment operator)和递减运算符(decrement operator)让运算对象递增或递减1,运算对象是可修改的左值,且各有两种形式:前缀和后缀。两种形式的行为发生时间不同。前缀形式的返回值是运算对象递增(减)后的值,后缀形式的返回值是当前运算对象的值,然后运算对象再递增(减)。示例程序。
#include<stdio.h>
int main(void)
{
int val=10;
printf("%d ",val++);
printf("%d ",++val);
printf("%d ",val--);
printf("%d ",--val);
return 0;
}
运行后输出结果:
10 12 12 10
递增递减运算符的优点
- 结构紧凑,程序简洁。
- 可读性高。
- 运行效率高。
5.4 表达式和语句
5.4.1 表达式
表达式(expression)由运算符和运算对象组成,且每个表达式都有一个值。最简单的表达式是一个单独的运算对象。较复杂的表达式由子表达式(subexpression)组成。
5.4.2 语句
语句(statement)是程序的基本构建块。大部分语句以;
结尾。最简单的语句是空语句(null statement):;
表达式语句:表达式;
。例如8;
和3+4;
,但是这些不算真正的语句,真正的语句可以改变值或调用函数,例如x++;
或x=sqrt(y);
。
一条语句相当于一条完整的计算机指令。但不是所有的指令都是语句。例如x=6+(y=5)
中y=5
是一条指令但不是语句。所以;
用于识别此情况下的简单语句。
声明,去掉;
后并不是表达式,没有值。
int value/*不是表达式,没有值*/
5.4.3 副作用和序列点
副作用(side effect)是对数据对象或文件的修改。副作用的发生在序列点(sequence point)之前。;
标记了一个序列点,完整表达式(full expression)的结束也是一个序列点。
完整表达式指该表达式不是更大表达式的子表达式。
5.4.4 复合语句(块)
复合语句(compound statement)是用{}
包裹的一条或多条语句,也称为块(block)。整个复合语句被视为一条语句。
5.5 类型转换
5.5.1 自动类型转换规则
- 升级(promotion):当类型转换出现在表达式时,无论是unsigned还是signed的char和short都会转换成int,如有必要会被转换成unsigned int(如果short与int大小相同,unsigned short转换成unsigned int)。K&C时的C,float被转换成double(目前不是)。
- 涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
- 类型的级别从高到底:long double、double、float、unsigned long long 、long long、unsigned long、long、unsigned int、int。short和char被升级为int或unsigned int。
- 降级(demotion):转换成更低级别的类型,发生截断。在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型,这一过程可能会有升级或降级。
- 作为参数传递时,char和short升级成int,float升级成double,函数原型会覆盖自动升级(第9章详解)。
- 待赋值与目标类型不匹配时:目标类型是无符号整型且待赋值是整数,忽略额外的位;目标类型是有符号整型且待赋值是整数,结果因实现而异;目标类型是整型且待赋值是浮点型,该行为未定义。
- 浮点型转换成整型会发生截断。
示例程序:
#include<stdio.h>
int main(void)
{
char ch;
int i;
float fl;
fl=i=ch='C'; /*ASCII码值67*/
printf("ch=%c, i=%d, fl=%2.2f\n",ch,i,fl);
ch=ch+1;
i=fl+2*ch;
fl=2.0*ch+i;
printf("ch=%c, i=%d, fl=%2.2f\n",ch,i,fl);
ch=1107;
printf("Now ch=%c\n",ch);
ch=80.89;
printf("Now ch=%c\n",ch);
return 0;
}
运行后输出结果:
ch=C, i=67, fl=67.00
ch=D, i=203, fl=339.00
Now ch=S
Now ch=P
5.5.2 强制类型转换
强制类型转换(cast),即在某个量前加(目标类型)
。强制类型转换运算符(cat operator):(type)
。
5.6 带参数的函数
见第9章9.1。