5.1 循环
5.2 基本运算符
5.2.1 赋值运算符 =
bmw = 2002意思是把值 2002 赋给变量 bmw- 左值、可修改的左值、数据对象、右值 P106
- C语言可以多重赋值,方向为从右向左,其他语言大概率不支持
- 例如
cheeta = tarzan = jane = 68
5.2.2 ~ 5.2.6 运算符 + - * /
- 一元运算符与二元运算符,+ - 同时可以做一元运算符,表示正负
rocky=-12;smoky=-rocky。+ 做一元运算符只能用于dozen = +12;
-
C语言原生没有幂函数,可以用乘法表示
-
整数截断:对于整数除法“4/3”,答案不会包含小数,会直接把小数部分给舍弃,称为截断;浮点数之间的除法会保留小数;对于混合使用浮点数和整数的运算,即混合类型,会将整数转化为浮点数在进行计算,一般情况下需要尽量避免。
-
负数的整数之间的除法,例如当答案为“-3.8”,截断为 “-3”,称之为趋零截断
5.2.7 运算优先级
乘除法优先级大于加减,当全部优先级相同时从左往右进行,有括号先括号
对于上面未规定的运算顺序,例如y = 6 * 12 + 5 * 20;,前后两个乘法运算的优先级相同,但中间多了一个优先级不同的运算,不符合上面的第二条。标准中对该运算的顺序没有规定,由不同的硬件来决定
5.3 其他运算符
C语言运算符有40多个,比较常用的还有四个
5.3.1 sizeof 运算符和 size_t 类型
size_t 类型本质上是 unsigned int 或 unsigned long 的别名,在C语言头文件中使用 typedef 定义,具体哪种类型,由系统来定,转换说明使用%zd
sizeof 的运算对象为具体的数据对象或者类型,如果运算对象是类型,则必须有括号,具体数据对象可以没有括号,但是最好加上括号。
sizeof 运算结果是对象在内存中占用的字节数,对字符串,结果是占用的所有内存的大小而不是字符串的实际长度
5.3.2 求模运算符 %
求模运算符只能用于整数,即求余数。
求模运算更多用于判断语句中,每值某个数的整数倍则如何的判断
-
负数求模:趋零截断的原则下用
a % b = a - (a/b)*b计算 -
几个例子:
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.3 递增运算符 ++
递增运算符会将其运算对象递增一,有两种模式:前缀模式、后缀模式。顾名思义,放在变量的前面或者后面 递增运算的一个极大的优势是可以将代码写的更加优美,例如
即把变量递增过程放入循环条件中
这种方式将判断循环的两个条件:循环条件以及参数改变 放在了一个地方,避免忘记
但一定程度上降低了程序可读性
前缀模式与后缀模式的区别
示例:
结果是a11 = 1 ; _11b = 2
相同点是对应的变量值,a 和 b 都递增了,区别是在表达式中使用的 a++ 没有改变,仍然是 1, 而 ++b 则表示已经递增过的值。
由此,在判断表达式中如果使用递增符号,则需要注意,例如a++ < 9和++a < 9循环次数相差 1
由于不同的递增方式会对代码产生不同的影响,因此最明智的方式是不在表达式中使用
例如,使用
++i;b = i;而不是b = ++i;
5.3.4 递减运算符:–
与递增运算类似
5.3.5 优先级
递增递减运算符优先级仅次于括号
(x*y)++ 的使用是无效的,++仅仅会作用于可修改的左值
5.3.6 不要自作聪明
一次性使用太多递增运算符,容易导致自己糊涂以及发生错误 C语言中并没有对不同项的运算规定顺序 例如:
|
|
可以先计算前面的num 再计算num++,得到(5,25),也可以先计算num++,再计算前面的num (6,30)。也有可能先计算中间num,再计算num++,最后计算最前面的num,有(6,25).
|
|
|
|
也都有可能出现问题,应当予以避免 于是,规定以下情况不使用递加或递减,可以使程序更好运行
- 变量出现在一个函数的多个参数中
- 变量多次出现在一个表达式中
5.4 表达式和语句
5.4.1 表达式
表达式由运算符和运算对象组成
一些表达式由子表达式组成,a*(b+c/d)/20中,c/d可以认为是子表达式
每个表达式都有一个值,包括赋值和比较。q = 2*5,作为一个表达式时,值为10,等于赋值的值(参考连续赋值)。不等式a>2,当为真时,值为1,否则为0。
不建议使用但是不错的表达式:6 + (c = 3 + 8),值为17,后面视为子表达式
5.4.2 语句
一条语句表示一条完整的计算机命令,大部分语句以分号结尾.legs=4不是语句,可能是语句的一部分,而legs=4;表示语句。
上面的几条也都是语句,但是在程序中什么都不做,没有意义
声明不是表达式语句
-
表达式语句由:赋值表达式语句和函数表达式语句
-
迭代语句(while语句没有括号时)
-
复合语句
副作用(side effect)
C语言中,每个语句的主要目的是求值,比如a=100; printf("sdfasg"),每个语句的目的(C语言角度)都是求值,比如上面的第一条语句返回的值是 100,第二条语句返回打印的字符数。
而其产生的其他效果,比如给 a 赋予了一个值,以及打印了一些字符,便被称为副作用。
序列点(sequence point)
序列点是程序执行点,所有的副作用都应该在该点之前完成。通常来说一个分号是一个序列点。C标准中还规定了一个完整表达式的结束也是一个序列点。
完整表达式:一个表达式语句不是另一个表达式的子表达式时,包含循环条件和表达式语句
该语句中,在a++<10之后虽然没有分号,但是已经构成了一个完整表达式,所以后面打印的值应当是递增之后的。
5.4.3 复合语句(compund statement)
复合语句是用花括号括起来的一条或多条语句,也称为块(block)
对于一些函数,循环来说,例如while,复合语句的效果类似于一条语句。
5.5 类型转换
-
表达式中,short 和 char (无论有无符号)会被转换成 int 。如果 short 和 int 一样大(某些系统中是这样的),unsigned short 会被转换成 unsigned int。该转换称为升级(promotion)
-
涉及到两种不同的类型,会被转换成两者的高级别形式。比如 int 和 long int 相加,都转换成 long int 再相加
-
类型的级别从高到底为:long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。
上面没有short和char,是因为已经被升级了
当long 和 int 相同,unsigned int 比 long 级别高
-
赋值表达式中,右值的计算结果会被转换成左值的类型,该过程可能出现**降级(demotion)**或升级
-
函数作为参数传递时,char 、short 被转换成 int,float 被转换成 double。
通常类型升级不会出现太多问题,但是降级会,因为较低的类型放不下整个数字
4中转换成目标类型的过程中:
- 目标是无符号整型,待赋值的值是整数。忽略额外位数,比如8位的unsigned char 有8位,整数对256求模
- 目标是有符号整型,待赋为整数,结果需进一步讨论
- 目标是整型,待赋为浮点数,该行为是未定义的(这好像和下面相悖)
把浮点数转换成整数类型,会发生趋零截断
5.5.1 强制类型转换运算符
上面讨论的类型转换都是自动完成的,然而有时候需要精确的类型转换,此时需要**强制类型转换(cast)**例如
在对应的值前加上括号和类型,但是由于趋零截断,上面两个计算的结果是不同的。
一般不应该混合使用类型,很多语言甚至不允许
5.6 带参数的函数
构造带参数的函数时,在其函数头的括号中加上所包含的参数的声明,例如void pound(int n),不带参数的函数该部分是void。
通过声明参数,会创建一个形参(术语:parameter),形式参数(formal argument、formal parameter)。
而当使用该函数,pound(m)中,m是实际使用的参数,称为实参(argument),实际参数(actual argument、actural parameter)。
示例:
|
|
在函数头中声明的变量名是函数私有的,仅仅在声明的函数中使用,对函数之外函数比如主函数,没有影响
上面使用 pound(ch) 时,由于pound函数的参数类型是 int ,与 char 不匹配,故根据前面的转换方法进行