操作符和表达式是C语言基础中非常重要的一环,而且C语言的操作符和表达式相较于其他语言会更加丰富,所以这部分的学习尤为重要,这章的内容属于重要但难度不大的类型。按照惯例,先来一个本章内容的思维导图。
1 内容概览
从思维导图中可以看到,本章的内容虽然不多,但是非常琐碎,所以需要一点一点学习。
2.1 操作符
2.1.1 算术操作符
算术操作符是最长用的操作符,即:+ - * / %
,其中的除法运算比较特殊,因为牵扯到能否整除的问题,所以有了额外的运算符:取余,即%
,来看这样的一个例子。
#include <stdio.h> // 函数指针与回调函数 int main() { int a1 = 10; double a2 = 10; int b = 3; printf("%d\n", a1 / b); printf("%f\n", a2 / b); printf("%d\n", a1 % b); system("pause"); return 0; }
打印输出如下:
可以看到除法运算中,当被除数和除数都是整数时,执行整除运算,其他时候会执行浮点数除法,而求余运算也是在被除数和除数都是整数时取得的余数。
2.1.2 移位操作符
移位操作符一般情况下分为左移位操作符<<
和右移位操作符>>
,来看下面的例子。
#include <stdio.h> // 函数指针与回调函数 int main() { short a1 = 5; short a2 = -5; printf("%d\n", a1 << 2); printf("%d\n", a2 << 2); //printf("%d\n", a1 % b); system("pause"); return 0; }
打印输出如下:
书上的表达比较严谨。无符号值执行的是逻辑移位,而有符号值是逻辑移位还是算术移位与具体的编译器有关。其实在刚才的结果中也可以看到,在VS2015编译器中执行的是算术移位,所谓的算术移位是指在移位的过程中,符号位(最高位)始终保持不变,其他位会有变化,逻辑移位就很简单了,直接进行移位操作,不够的地方补零即可。负数的移位操作稍微繁琐些,具体的可以参考这篇文章。
2.1.3 位操作符
位操作符在很多偏向于底层的开发任务中尤为重要,因为意味着我们可以对某数的某个二进制位进行操作。在C语言中常见的位操作符有三个,它们分别是:& | ^
分别对应着位与运算,或运算,异或运算,其实异或运算也很好理解,就是所谓的相同出0,不同出1。来看个例子。
#include <stdio.h> // 函数指针与回调函数 int main() { short a1 = 0x03; printf("%d\n", a1&0x02); printf("%d\n", a1|0x04); printf("%d\n", a1 ^ 0x0c); system("pause"); return 0; }
打印输出如下:
a1用二进制形式表示为(假设占8位内存):a1 = 0000 0011
,所以与0x02(0000 0011)
的与运算结果为2,或者也可以理解为最后一位的1与0进行与运算之后变为了0,而其与0x04(0000 0100)
执行或运算的时候,有1出1,所以后三位全变为1
,也就得到了最终的结果7
,最后一个是a1与0x0C(0000 1100)
进行异或运算,相同出0,不同出1,由于其低四位全都不同,因而得到了最终结果15(0000 1111)
。
当然书上还有这样的例子,读者可自行分析。
#include <stdio.h> // 函数指针与回调函数 int main() { unsigned short a1 = 0x03; int bit_number = 1; //将指定的位设置为1 a1 |= 1 << bit_number; printf("%d\n", a1); //将指定的位设置为0 a1 &= ~(1 << bit_number); printf("%d\n", a1); system("pause"); return 0; }
其实不难的。
2.1.4 赋值
赋值操作符非常简单,就是数学上的等号:=
,同样,也可以采用下面的方法对变量进行赋值。
#include <stdio.h> // 函数指针与回调函数 int main() { short a1 ,b1, c1; a1 = b1 = c1 = 10; printf("a1的值为:%d\n", a1); printf("b1的值为:%d\n", b1); printf("c1的值为:%d\n", c1); system("pause"); return 0; }
最终得到的结果都为10。当然这种“连续赋值”的形式在变量定义的时候进行赋值是不被允许的。
比方这种形式:
#include <stdio.h> // 函数指针与回调函数 int main() { short a1 = b1 = c1 = 10; printf("a1的值为:%d\n", a1); printf("b1的值为:%d\n", b1); printf("c1的值为:%d\n", c1); system("pause"); return 0; }
原因很简单,因为赋值运算符的结合顺序是自右向左的,而首先对变量c1进行赋值的时候,该变量尚未定义,所以会报错。
当然还有复合赋值符,这种运算符非常常见,因为在运算比较复杂的时候可以大大简化代码量,且不易出错。
比方说a = a + 10
;可以用a+=10
;进行表达,其他类似的运算也是如此。
2.1.5 单目操作符
单目操作符意思是只接受一个操作数,C语言中有不少这样的操作符,包括:逻辑非!
,变量按位取反~
,变量自加++
,变量自减--
,变量前添加负号-
,正号+
,取地址运算符&
,间接访问操作符*
,求变量字节数操作符sizeof()
。这些操作符都比较好理解,也很常用,就不一一介绍了,其中最容易混淆的是i++
和++i
这两种运算,也是考试中最常考的内容之一。下面着重介绍。
其中前者是先对表达式取值为i,然后再对i自身加1,而后者是先对自身加一,然后再对表达式取值。
也就是说,对于i自身的值来说,两种运算对变量自身的值并无影响,只是表达式的值不一样而已。一起来看下面的例子。
#include <stdio.h> // 函数指针与回调函数 int main() { int i = 10; printf("i的值为:%d\n", i); printf("i++的值为:%d\n", i++); i = 10; printf("++i的值为:%d\n", ++i); system("pause"); return 0; }
打印输出如下:
2.1.6 关系操作符
常见的关系操作符有6种,分别是:“>,>=,<,<=,==,!=
”,含义很简单,其中的大于小于和数学上的没什么区别,只是数学上的等号在C语言中是双等号,数学上的≠在C语言中是!=
。一般关系运算符的优先级比较低,这也符合我们正常的编程逻辑。也就是算术操作符优先级高于关系操作符。
2.1.7 逻辑操作符
逻辑操作符包含两个,分别是&&
和||
,即条件与和条件或,这个也很好理解,条件与是两者都满足才是真,其他时候均为假,而条件或是只有一个为真即可,结果为真。
2.1.8 条件操作符
条件操作符接受三个操作数。它们也会控制表达式的求值顺序。有的教材中条件操作符成为三目运算符,毕竟本来C语言中得三目运算符并不算多。条件操作符可以说是条件语句在特殊形式下的简化形式。而且非常常用。比方说下面这个问题(来自力扣):
- 各位相加 给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。
某解法如下:
int addDigits(int num){ int sum = 0; while(num > 0) { sum += num % 10; num /= 10; } return sum < 10 ? sum : addDigits(sum); }
每次都将该数各个位上的值进行相加,如果小于10则直接返回,否则执行递归。
2.1.9 逗号操作符
逗号表达式并不是非常常见,这些表达式自左向右逐个进行求值,整个逗号表达式的值就是最后那个表达式的值。其形式为:
表达式1,表达式2,表达式3,……,表达式N
。
2.1.10 下标引用、函数调用和结构成员
这些内容将在后续的章节讨论,下标引用与数组和指针有关,函数调用和函数有关,结构成员和结构体有关。
2.2 布尔值
在标准C中并没有bool数据类型,一般是这样用的,在C语言中,所有非零数值皆为真,零为假。
2.3 左值和右值
左值和右值很好理解,左值是想赋值的部分,而右值是读取数值的部分,比方说:
a = b + 15;
则a为左值,b + 15为右值。不可以调换位置。
2.4 表达式求值
在表达式求值中有一些值得注意的问题,比方说,隐式类型转换,操作符的优先级,等等问题,这是在实践中经常会遇到的问题。
隐式类型转换很好理解,因为在运算中有时候数据类型不统一,或者左值和右值的数据类型不统一都会出现这个问题。但一般为了保证程序的正常运行和结果不出错,隐式类型转换总会将数值转换为我们期望的类型。
操作符的优先级只需要熟悉就行,一般来说,算术操作符>关系操作符>逻辑操作符,用多了自然就可以记住。
3 总结
本章的内容很基础,也比较简单,只需要多加实践即可!
----------------------------------------------------END----------------------------------------------------