5. 基本运算符
一、基本运算符
1.1 赋值运算符:=
-
在C语言中,=并不意味着“相等”,而是一个赋值运算符。
-
赋值表达式语句的目的是把值储存到内存位置上,用于储存值的数据存储区域统称为数据对象。
-
左值:用于标识或定位存储位置(标识特定数据对象的名称或表达式,即实际的数据存储)。
-
可修改的左值:用于标识可修改的对象,赋值运算符的左侧应该是可修改的左值。
-
右值:能赋值给可修改左值的量,且本身不是左值(是字面常量,无法被赋值)。
-
运算对象:被称为“项”,是运算符操作的对象。
int ex,why,zee; //ex,why,zee是可修改的左值 const int TWO = 2; //TWO是不可改变的左值 why = 42; zee = why; ex = TWO * (why + zee); //42和(why + zee)是右值
1.2 加法运算符:+
- 加法运算符用于加法运算,使其两侧的值相加。
- 相加的值(运算对象)可以是变量,也可以是常量。
1.3 减法运算符:-
-
减法运算符用于减法运算,使其左侧的数减去右侧的数。
-
+和-运算符都被称为二元运算符,即这些运算符需要 两个运算对象才能完成操作。
1.4 符号运算符:-和+
-
-作为一元运算符,改变其右侧值的符号。
例如,对rocky = –12; smokey = –rocky; ,可得smokey = 12 。
-
+作为一元+运算符,它不会改变运算对象的值或符号,只能这样使用:dozen = +12 。
-
一元运算符只需要一个运算对象。
1.5 乘法运算符:*
-
符号*表示乘法。
-
C没有平方函数,可以使用乘法式来计算平方。
-
指数增长可以应用while循环。
/* squares.c -- 计算1~20的平方 */ #include <stdio.h> int main(void) { int num = 1; while (num < 21) { printf("%4d %6d\n", num, num * num); num = num + 1; } return 0; }
1.6 除法运算符:/
-
C使用符号/来表示除法。/左侧的值是被除数,右侧的值是除数。
-
整数除法和浮点数除法不同。浮点数除法的结果是浮点数;而整数除法的结果是整数,没有小数部分的数。
-
在C语言中,整数除法结果的小数部分被丢弃的过程被称为截断。
整数除法会截断计算结果的小数部分(丢弃整个小数部分),不会四舍五入结果。
-
负整数的运算也是直接丢弃小数部分,但是是“趋零截断”。如,-3.8会被截断为 -3。
-
混合整数和浮点数计算的结果是浮点数。
但计算机不能真正用浮点数除以整数,编译器会把两个运算对象转换成相同的类型。
1.7 运算符优先级和求值顺序
-
加减乘除的运算的优先级顺序跟数学运算优先级顺序一样。
-
当运算符共享一个运算对象时,优先级决定了求值顺序。
-
表<运算符优先级(从高到低)>
运算符 结合律 ( ) 从左到右 + -(一元) 从右到左 * / 从左到右 + -(二元) 从左到右 = 从右到左
二、其他运算符
2.1 sizeof 运算符和size_t 类型
-
sizeof 是C语言的内置运算符,以字节为单位给出指定类型的大小。C99和C11提供 %zd 转换说明匹配 sizeof 的返回类型,一些不支持C99和C11的编译器可用 %u 或 %lu 代替 %zd 转换说明匹配 sizeof 的返回类型。
-
运算对象是类型时,圆括号不能少,如 sizeof(char);但是对于特定量,可有可无,如 sizeof name。
2.2 求模运算符:%
-
求模运算符用于整数运算,不能用于浮点数。
-
求模运算符给出其左侧整数除以右侧整数的余数,如13 % 5 = 3。
2.3 递增运算符:++
-
递增运算符执行简单的任务,将其运算对象递增 1。
-
该运算符以两种方式出现(两种模式递增行为发生的时间不同):
第一种,++count,前缀形式的递增运算符;
第二种,count++,后缀形式的递增运算符。
#include<stdio.h> int main(void) { int a = 1, b = 1; int a_post = a++, b_pre = ++b; printf("%d %d %d %d\n", a, a_post, b, b_pre); //a = 2, a_post = 1, b = 2, b_pre = 2 //前后缀区别:和b都递增了1,但是,a_post是a递增之前的值,而b_pre是b递增之后的值。 return 0; }
2.4 递减运算符:--
-
递减运算符执行简单的任务,将其运算对象递减 1。
-
该运算符以两种方式出现(两种模式递增行为发生的时间不同):
第一种,--count,前缀形式的递减运算符;
第二种,count--,后缀形式的递减运算符 。
-
类比递增运算符
2.5 递增与递减运算符补充
-
递增和递减运算符只能影响一个变量(即只能影响一个可修改的左值)。
-
优点:第一,紧凑结构的代码让程序更为简洁,可读性更高,让程序看起来很美观;
第二,通常它生成的机器语言代码效率更高,因为它和实际的机器语言指令很相似。
-
优先级:递增运算符和递减运算符都有很高的结合优先级,只有圆括号的优先级比它们高。
-
不要对变量使用递增或递减运算符的两种情况:
如果一个变量出现在一个函数的多个参数中,如果一个变量多次出现在一个表达式中。
三、表达式与语句
3.1 表达式
-
表达式由运算符和运算对象组成。
-
最简单的表达式是一个单独的运算对象,以此为基础可以建立复杂的表达式。如 4、、x = ++q % 3、q > 3
-
一些表达式由子表达式组成。如,c/d是a*(b + c/d)/20的子表达式。
3.1.1 表达式的值
-
C 表达式的一个最重要的特性是,每个表达式都有一个值。
-
有赋值运算符(=)的表达式的值与赋值运算符左侧变量的值相同。
-
关系表达式的值不是0就是1。如果条件为真,表达式的值为1;如果条件为假,表达式的值为0。
-
| 表达式 | 值 |
|---|---|
| - 4 + 6 | 2 |
| c = 3 + 8 | 11 |
| 5 > 3 | 1 |
| 5 < 3 | 0 |
| 6 +(c = 3 + 8) | 17 |
3.2 语句
-
语句是C程序的基本构建块。一条语句相当于一条完整的计算机指令。
-
在C中,大部分语句都以分号结尾。因此, legs = 4 是一个表达式,而 legs = 4; 是一个语句。
-
最简单的语句是空语句: ; //空语句
-
C把末尾加上一个分号的表达式都看作是一条语句,如, 8; 、3 + 4; 也没有问题,但是,这些语句在程序中什么也不做,不算是真正有用的语句。
-
虽然一条有用的语句相当于一条完整的指令, 但并不是所有的指令都是语句。如,x = 6 + (y = 5); 语句中的子表达式 y = 5 是一条完整的指令,但是它只是语句的一部分。
-
因为一条完整的指令不一定是一条语句,所以分号用于识别在这种情况下的语句(即简单语句)。
// addemup.c -- 几种常见的语句 #include <stdio.h> int main(void) // 计算前20个整数的和 { int count, sum; // 声明[1] count = 0; // f表达式语句 sum = 0; // 表达式语句 while (count++ < 20) // 迭代语句 { sum = sum + count; printf("sum = %d\n", sum); // 函数表达式语句 } return 0; /* 跳转语句 */ }3.1.2 声明的特殊性
-
声明创建了名称和类型,并为其分配内存位置,但声明不是表达式语句。
-
即,如果删除声明后面的分号,剩下的部分不是一个表达式,也没有值。
如,int port // 不是表达式,没有值
3.2.3 赋值表达式语句
- 赋值表达式语句在程序中很常用,它为变量分配一个值,是表达式语句的一个示例。
- 赋值表达式语句的结构是,一个变量名,后面是一个赋值运算符,再跟着一个表达式,最后以分号结尾。
- 注意,在while循环中有一个赋值表达式语句
3.2.4 迭代语句
- while语句是一种迭代语句,有时也被称为结构化语句,因为它的结构比简单的赋值表达式语句复杂。
3.2.5 副作用和序列点
-
副作用:对数据对象或文件的修改。
-
序列点:程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。
-
完整表达式:指这个表达式不是另一个更大表达式的子表达式。如,表达式语句中的表达式和 while循环中的作为测试条件的表达式,都是完整表达式。
-
举例代码:while (guests++ < 10) printf("%d \n", guests);
表达式guests++ < 10是一个完整的表达式,因为它是while循环的测试条件,所以该表达式的结束就是一个序列点。因此,C 保证了在程序转至执行 printf( ) 之前发生副作用(即递增guests)。同时,使用后缀形式保证了guests在完成与10的比较后才进行递增。
-
3.3 复合语句(块)
-
复合语句:用花括号括起来的一条或多条语句, 复合语句也称为块。
/* 程序段 1 */ /* 程序段 2 */ index = 0; index = 0; while (index++ < 10) while (index++ < 10){ sam = 10 * index + 2; sam = 10 * index + 2; printf("sam = %d\n", sam); printf("sam = %d\n", sam); } /*程序段1,while循环中只有一条赋值表达式语句。没有花括号,while语句从while这行运行至下一个分号。循环 结束后,printf()函数只会被调用一次。 程序段2,花括号确保两条语句都是while循环的一部分,每执行一次循环就调用一次printf()函数。根据while语句的结构,整个复合语句被视为一条语句。
四、类型转换
4.1 类型转换的基本规则
-
当类型转换出现在表达式时,无论是unsigned 还是signed 的char 和short 都会被自动转换成int ,如有必要会被转换成unsigned int。
(如果short与int的大小相同,unsigned short 就比int 大,unsigned short 会被转换成 unsigned int)
-
类型级别从高至低依次是:long double、double、float、unsignedlong long、long long、unsignedlong、long、unsigned int、int。
例外的情况是,当 long 和 int 的大小相同时,unsigned int 比 long 的级别高。之所以 short 和 char 类型没有列出,是因为它们已经被升级到 int 或unsigned int 。
-
涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
-
在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这过程可能导致类型升级或降级。
-
升级,是指从较小类型转换为较大类型;降级,是指把一种类型转换成更低级别的类型。
-
当作为函数参数传递时,char 和short 被转换成int,float 被转换成 double。
-
函数原型会覆盖自动升级。
4.2 待赋值的值与目标类型不匹配的规则
- 1.目标类型是无符号整型,且待赋的值是整数时,额外的位将被忽略。 例如,如果目标类型是 8 位unsigned char,待赋的值是原始值求模。
- 2.如果目标类型是一个有符号整型,且待赋的值是整数,结果因实现而异。
- 3.如果目标类型是一个整型,且待赋的值是浮点数,该行为是未定义的。
/* convert.c -- 自动类型转换 */
#include <stdio.h>
int main(void)
{
char ch;
int i;
float fl;
fl = i = ch = 'C';
printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); // 第9行
ch = ch + 1;
i = fl + 2 * ch; // 第11行
fl = 2.0 * ch + i;
printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); //第13行
ch = 1107;
printf("Now ch = %c\n", ch); // 第15行
ch = 80.89;
printf("Now ch = %c\n", ch); // 第17行
return 0;
}
/*运行convert.c后输出如下:
ch = C, i = 67, fl = 67.00
ch = D, i = 203, fl = 339.00
Now ch = S
Now ch = P
程序的分析如下(此系统,char是8位,int是32位):
第8-9行:字符'C'被作为1字节的ASCII值储存在ch中。整数变量i接受由'C'转换的整数,即按4字节储存67。
最后,fl接受由67转换的浮点数67.00。
第10-13行:字符变量'C'被转换成整数67,然后加1。计算结果是4字节整数68,被截断成1字节储存在ch中。
根据%c转换说明打印时,68被解 释成'D'的ASCII码。
第11-13行:ch的值被转换成4字节的整数(68),然后2乘以ch。为和fl相加,乘积整数(136)被转换成浮点数。
计算结果(203.00f)被转换成int类型,并储存在i中。
第12-13行:ch的值('D',或68)被转换成浮点数,然后2乘以ch。为了做加法,i的值(203)被转换为浮点类型。
计算结果(339.00)被储存在fl中。
第14-15行:演示了类型降级的示例。把ch设置为一个超出其类型范围的值,忽略额外的位后,最终ch的值是字符S的
ASCII码。或者,更确切地说,ch的值是1107 % 265 ,即83。
第17-18行:演示了另一个类型降级的示例。把ch设置为一个浮点 数,发生截断后,ch的值是字符P的ASCII码。
4.3 强制类型转换运算符
-
强制类型转换:在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。
-
强制类型转换运算符:由圆括号和它括起来的类型名构成,其通用形式是: (type) ,再用实际需要的类型(如,long)替换type即可。
-
举例:mice = 1.6 + 1.7; 使用自动类型转换。首先,1.6和1.7相加得3.3。
然后,为了匹配 int 类型的变量,3.3被类型转换截断为整数3。
mice = (int)1.6 + (int)1.7; 1.6和1.7在相加之前都 被转换成整数1,所以把1+1的和赋给变量mice。
五、C的一些运算符总结
-
赋值运算符: = 将其右侧的值赋给左侧的变量
-
算术运算符:
+ 将其左侧的值与右侧的值相加 ; - 将其左侧的值减去右侧的值 ;
- 作为一元运算符,改变其右侧值的符号 ; * 将其左侧的值乘以右侧的值 ;
/ 将其左侧的值除以右侧的值,如果两数都是整数,计算结果将被截断;
% 当其左侧的值除以右侧的值时,取其余数(只能应用于整数) ;
++ 对其右侧的值加1(前缀模式),或对其左侧的值加1(后缀模式);
-- 对其右侧的值减1(前缀模式 ),或对其左侧的值减1(后缀模式)。
-
其他运算符:sizeof 获得其右侧运算对象的大小(以字节为单位),运算对象可以是一个被圆括号括起来的类型说明符,如sizeof(float),或者是一个具体的变量名、数组名等,如sizeof foo (类型名) 强制类型转换运算符将其右侧的值转换成圆括号中指定的类型,如(float)9把整数9转换成浮点数9.0。

浙公网安备 33010602011771号