第五章 操作符
操作符在C语言中的使用是十分经典且典型的,因为在C语言的引导下,后世很多编程语言也都和C语言有着极其相似的操作符使用方法,因此学好这一章也会为日后学习更多的编程语言打下基础。
第1节
操作符
算术操作符
+ - * / %
这是C语言中最为常用的几个操作符,使用也十分简单,除了%
操作符外,其它操作符可以用于整数和浮点数。%
操作符,的两个操作数必须是整数,返回的是整除后的余数。
移位操作数
左移操作数:<<
左移规则:将数字的在内存中的二进制表示左移一位,右边补0, 等同于将变量中的数本身乘以2并且值重新赋给变量。1
2
3
4
5
6
7
8
9
10
11#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
int main()//主函数,函数入口
{
int num = 10;
//未移位前:00000000000000000000000000001010
printf("%d\n", num << 1);//打印20
//左移一位后:00000000000000000000000000010100
system("pause");
}
右移操作数:>>
右移规则:右移规则分为两种。
1、逻辑移位:左边用0填充,右边丢弃
2、算数移位:左边用符号位填充,右边丢弃。1
2
3
4
5
6
7
8
9
10#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
int main()//主函数,函数入口
{
int num = -1;
//右移位前:11111111111111111111111111111111
printf("%d\n", num >> 1);//打印-1 //右移移位后:11111111111111111111111111111111
system("pause");
}
从结果可见,我们右移后在左边用了符号位1来填充,可见编译器默认使用了算数移位。
对移位运算符来说,有一点是十分致命的,就是永远不要移动负数位,这个标准是未定义的,会发生无法意想的错误。
位操作符
位操作符主要针对的是两个数的二进制表示中各位上的数字进行运算。
按位与:&
,都为1则为1,否则为0。
按位或:|
,一个为1就为1,否则为0。
按位异或:^
,不同则为1,相同为0。
按位取反:~
,1变0,0变1。
位操作符两边的操作数都必须是整数。 通过位操作符,我们可以实现一些针对于整数的二进制表示的算法,例如我们可以实现计算一个整数二进制表示中有多少个1
。
方法1:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
int BitOneCount(int num)
{
int count = 0;
while (num != 0)
{
if (num % 2 == 1)
{
count++;
}
num /= 2;
}
return count;
}
int main()//主函数,函数入口
{
printf("%d\n", BitOneCount(3));//打印2
system("pause");
}
这一种方法是较为简单的算法,写起来也十分直观,但是有一个致命的缺点就是它对负数的计算就并不是那么准确了,因此我们通过改进得出了一下这个方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
int BitOneCount(int num)
{
int count = 0;
for (int i = 0; i < 32; i++)//一个整形最多有32位,因此我们需要从第一位开始进行32个循环逐步计数
{
if (num >> i & 1 != 0)//右移验证最低位是否是1,是1则计数器+1
{
count++;
}
}
return count;
}
int main()//主函数,函数入口
{
printf("%d\n", BitOneCount(-1));//打印32
system("pause");
}
以上的这种算法利用位运算符直接对二进制数进行计算,即适用于正数,也适用于负数,是十分推荐使用的算法。
赋值操作符
赋值预算符:=
,我们之前已经多次使用了,这里不在介绍了,不过基于普通的赋值运算符,C语言中给我们提供了更多的方便我们使用的复合赋值符。这里举其中一两个例子。
a += 1
=> a = a + 1
;
a -= 1
=> a = a - 1
;
以此类推,有很多的运算符都可与=
号结合方便我们的使用。
单目操作符
单目操作符是指只有一个操作数的操作符。
逻辑反操作:!
,这个操作符可以将语句的逻辑取反,在C语言中,0是表示假,非0表示真,因此在使用这个操作符时,可以将逻辑取到与之相反的一方。
负值/正值:-/+
,与加减法不同,负值个操作符可以将数值编程他的相反数。
取地址:&
,取地址操作符可以取到一个变量在内存中存储的地址。
sizeof:sizeof()
,可以取到变量在内存中所占的空间大小,单位是字节。
自增/自减操作符:++/--
,自增自减操作符写在操作数的前或后有着不同的效果,在日常使用中机器容易出错,因此在很多情况下不推荐使用。
解引用操作符:*
,这个操作符可以通过指针找到指针所指变量中的数据,并将其返回,在指针使用中非常常见。
强制类型转换操作符:(类型)
,这个操作符可以将C语言中的类型进行强制转换,比如说double a = (double)1;
,有非常大的使用空间。
关系操作符
关系操作符主要常用于判断,在C语言中有以下关系操作符。
> >= < <= == !=
逻辑操作符
逻辑操作符多用于同时需要多个条件或多个条件只要一个满足即可的情况。
逻辑与:&&
,逻辑或:||
。
1 && 1 == 1
,1 && 0 == 0
,1 || 0 == 1
,0 || 0 == 0
。
条件操作符(三目操作符)
这是C语言中唯一的一个三目操作符,具体使用如下。
exp 1 ? exp2 : exp3
:如果exp1为真则执行exp2,否则执行exp3。有时使用三目运算符比使用if语句要更加方便。
逗号表达式
exp1, exp2, exp3...expn
:如果一个表达式中出现了逗号,则表达式从左向右一次执行,但整个表达式的结果是最后一个表达式的结果。
下标引用,函数调用和结构成员
下标引用:数组名[]
,下标引用在数组中经常使用。
函数调用:函数名()
,在函数调用时会经常使用。
结构体成员:./->
,前者在结构体变量中可以取到结构的成员,后者在结构体指针中可以取到结构体成员,这两个操作符在结构体操作中都十分常用。
表达式求值
隐式转换
在C语言中整形算数运算总是至少以缺省整形的精度进行运算的,为了获得这个精度,表达式中的字符和短整型的操作数在使用前都会先隐式转换为整形才会进行运算,这个也叫做整形提升。
那么是如何进行整形提升的呢?
当char
中的值为有符号的负数时,char a = -1
原本的char只有8个符号位为11111111
,在提升后会在高位补符号位变为11111111111111111111111111111111
。而如果对于没有符号位的char a = 1
,原本的char
为00000001
,提升后会在高位补符号位变为00000000000000000000000000000001
。1
2
3
4
5
6
7
8
9
10
11
12
13
14#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
int main()//主函数,函数入口
{
unsigned char a = 255;
printf("%d\n", (a << 1) >> 1);
//计算前先整形提升为00 00 00 ff
//左移后变为00 00 01 fe
//右移后又变为00 00 00 ff
//正因为整形提升的存在才不会造成溢出
system("pause");
}
算术转换
在表达式的计算中,如果某个操作符量的操作数为不同类型,那么其中一个操作数就会转换为另外一个操作数的类型来方便运算,并且转换总是从低级向高级进行转换。int -> unsigned int -> long int -> unsigned long int -> float -> double -> long double
int
以下的类型都会自动先转换为int,之前的隐式类型转换已经说过了。
不过算数转换也要合理不然会造成数据丢失。
float f = 3.14; int num = f
一些常见问题
1、操作符的求值有三个影响的因素:(1)操作符的优先级;(2)操作符的结合性;(3)是否控制求值顺序。
2、有一些表达式的运算在标准中未进行定义,因此在不同环境下的运行结果不尽相同,需要多多注意。