一 操作符分类
算数操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用和结构成员
二 算数操作符
对除法操作符“/”来说,两边的操作数都是整数,执行的是整数除法,如果想计算出小数,除号的两端至少有一个操作数是浮点数
对于取模操作符“%”来说,两边的操作数都需要是整数,运算结果为除法之余数
三 移位操作符
移位操作符移动的是二进制位,且只能对于整数操作,并且移动位数只能是小于32位的正数
1.1 “>>”右移操作符 ”<<“左移操作符
内存中存储的是补码的二进制,所以在进行移位时,移动的是二进制位
1.1.1 << 左移操作符
使用了左移操作符后,10的二进制序列向左移动了一比特位,最左端的比特位超出存储空间并被抛弃,最右端空出的比特位由0补全。由此操作10变成了20,但在此操作过程中,储存10的变量并未发生改变,只是10的表达结果改变为20。左移的操作结果为整数数值*2
1.1.2 >>右移操作符
右移操作符分为算术右移和逻辑右移,常见的为算数右移。绝大多数的编译器采用的是算数右移
右移之后,最右端的比特位抛弃,与此同时,算数右移与逻辑右移便存在不同。
算数右移后,右边抛弃,左边补上原来的符号位。
逻辑右移后,右边抛弃,左边补上0
1.3 整数的二进制的表示形式有三种:原码,反码,补码
1、按照一个整数的正负,直接写出它的二进制序列,这就是该整数的原码
正数的原码反码补码是相同的
以10为例,10的二进制表示为1010,并且10是个整形,一个整型的存储空间为4个字节,共计32个比特位,那么10的二进制位也应由32个比特位表示
00000000 00000000 00000000 00001010,整数的正负有最高位表示,若整数是个正数,则最高位为0,负数的最高位为1。例如-10的二进制表示为10000000 00000000 00000000 00001010。
2、反码为原码的符号位不变,其余位按位取反。
- 例如-10的反码为11111111 11111111 11111111 11110101
3、补码为反码+1。例如-10的补码为11111111 11111111 11111111 11110110
四 位操作符
位操作符分为“&”按位与、“|”按位或、“^”按位异或,与移位操作符相似,它们的操作数必须是整数,也是对于存在内存中的二进制补码进行计算。
1.1 按位与 &
& 的计算法则为:按二进制位与,对应的二进制位,有0为0,两个同时为1,才为1。
例如
1.2 按位或 |
按位或的计算规则与 & 类似,同样是对于二进制补码操作,两个二进制位同时为0,才为0,有一个1,则为1。
例如:
1.3 按位异或 ^
按位异或的计算规则为,对应的两个二进制位,相同为0,相异为1。
例如:
按位异或的几个性质
- a ^ a = 0
- a ^ 0 = a
- 异或支持交换律
例如:
通过计算我们可以得知 3 ^ 3 ^ 5 = 5并且 3 ^ 5 ^ 3 = 5
五 赋值操作符 =
1.1 赋值操作符作为我们平时使用极为频繁的操作符,其功能十分简单,就是对于变量进行赋值。
赋值操作符可以连续使用:
赋值的运算规则为从右向左赋值,县级那个y+1赋给x,再将x赋给a。
虽然赋值操作符可以连续使用,但这样会使得代码难以调试,故而不建议这样使用。
1.2 复合赋值符
常见的复合赋值符有 += -= *= /* %= <<= >>= &= |= ^=
六 单目操作符
常见的单目操作符有以下这些
1.1 逻辑反操作符 !
作为我们日常使用中比较常见的单目操作符,它的很多性质我们已经有所了解,在此便不再赘述,其中值得一提的便是“布尔类型”。
布尔类型的概念,在c99中被首先引入,其本质就是用来判断真假的类型。
布尔类型的使用格式,在此我们以判断闰年为例
它的本质便是,判断为真时,return 1,判断为假时,return 0
1.2 前置、后置++ --
后置++的计算规则为:先使用后++
前置++ 的计算规则为:先++再使用
无论实在赋值运算中,还是在将变量作为函数参数进行使用时,前置++与后置++的作用原理是一致的。而前置--与后置--的作用原理与++相同,在此就不再赘述。
1.3 强制类型转换 ()
例如在对于整型变量进行除法运算时,若想使得计算结果为小数,除了将任意一个整数*1.0以外,我们还可以将其中一个整数通过强制类型转换,将其转换为浮点数,从而达到效果。
七 关系操作符
关系操作符相对简单易懂,没什么值得讲解的部分,但在使用中,要注意 == 与 = 的区分,以免出现错误。
八 逻辑操作符
1.1 简介逻辑操作符
在使用逻辑操作符时,要特别与位操作符区分。
在计算机语言中,0为假,非0为真。
对于&&而言,两者只有全部为真,才为真。
对于||而言,两者只要有一个为真,就为真。
1.2 逻辑操作符的使用
下面的代码,大家可以尝试判断以下最终的输出结果是多少
通过运行代码后,我们会发现,输出的结果为
由于赋值操作符的计算优先级较低,所以在程序运行时,会先计算后面的代码。而 a++ 是对于a先进行使用,使得&&左边为0,导致 a++&&++b 的计算结果为0,进而导致右半部分 && d++ 的计算结果也为0。
在此过程中,仅有a++成功运行,而++b,d++均由于&&的判断结果为0,未进行计算,故而最终的结果为上图结果。
若将a改为1,并且将&&改为||,下列代码的计算结果又是多少呢?
运行代码后我们发现
表达式中,仅有a的值发生了改变。
这是因为,对于||操作符而言,若左边的计算结果为真,则右边便不会进行计算了。
九 条件操作符
条件操作符也被称为三目操作符。
条件操作符的计算规则是:表达式1为真,表达式2计算,表达式3不计算;表达式1为假,表达式2不计算,表达式3计算.
通过条件操作符,我们便可以将比较两数的大小的代码,简化为下图所示
十 逗号表达式
exp1, exp2, exp3, …expn
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
下面我们通过2个例子来理解以下,逗号操作符的运行规则。
代码1:
通过计算可知,c的最终结果为13
代码2:
该代码最终的判断对象为:d是否 > 0
代码3:
对于上图代码,我们也可以通过逗号操作符进行简化
十一 下标引用、函数调用和结构成员
1.1下标引用操作符 [ ]
下标引用操作符常常与数组搭配使用。
1.2 函数调用操作符 ( )
函数调用操作符便是我们在平时使用函数时的 ( )
对于函数调用操作符而言,至少有一个操作数。
1.3.1 结构成员操作符 “.” 以及 ->
在c语言中,常见的变量类型为int、char、float、double、long等,这些类型为c语言内置类型。除此以外,c语言中还有许多别的类型,例如自定义类型、结构体、枚举、联合体等类型。
这次我们主要介绍结构体,结构体类型实际上属于自定义类型,也被称为聚合类型。
在我们日产生活中,常常需要处理的数据并不是单一的类型,而是多种不同的类型组合在一起,由此而产生的聚合类型,便为结构体。
这是一个简单的自定义的结构体类型
现在我们对其进行初始化并进行打印
1.3.2通过结构体成员操作符,我们也可以对于结构体成员进行修改
十二 表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
1.1.1 隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
1.1.2 整型提升的意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。
例1:
例2:
例2中的a,b要进行整形提升,但是c不需要整形提升 。a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假。但是c不发生整形提升,则表 达式 c==0xb6000000 的结果是真,所程序输出的结果是: c
1.2 算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。
下面的层次体系称为寻常算术转换。
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
警告:算术转换要合理,要不然会有一些潜在的问题
1.3 操作符的属性
复杂表达式的求值有三个影响的因素:
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序
两个相邻的操作符先执行哪个?
取决于他们的优先级。如果两者的优先级相同,取决于他的结合性。