目录
一. 操作符分类
• 算术操作符: + 、- 、* 、/ 、%
• 移位操作符: << >>
• 位操作符: & 、| 、^
• 赋值操作符: = 、+= 、 -= 、 *= 、 /= 、%= 、>= 、&= 、|= 、^=
• 单⽬操作符: !、++、--、~ 、sizeof(类型)
• 关系操作符: > 、>= 、< 、<= 、 == 、 !=
• 逻辑操作符: && 、|| 、!
• 逗号表达式: ,
• 下标引⽤: []
• 函数调⽤: ()
• 结构成员访问操作符: . 、->
二. 算数操作符
算数操作符就是用来进行运算的操作符
+ ------加
- ------减
* ------乘
/ ------除
% ------取余
int a = 10;
int b = 5;
printf("%d\n", a + b);//15
printf("%d\n", a - b);//5
printf("%d\n", a * b);//50
printf("%d\n", a / b);//2
printf("%d\n", a % b);//0
//10/5=2,余数为0;所以a%b=0
三. 逻辑操作符
逻辑操作符是用来连接表达式并且判断语句的真假(在C语言中0表示假,非0表示真)
逻辑操作符分为:
&& —— 逻辑 “与”|| —— 逻辑 ”或“
! —— 逻辑 ”非“
1.&&--逻辑与
&& 可以理解为并且的意思,(当两个语句都为真时才为真,否则为假),例如:
int main()
{
int a = 10;
int b = 5;
if (a > 0 && b > 0)//如果a>0并且b>0,那么就打印hehe
{
printf("hehe\n");
}
return 0;
}
2.||---逻辑或
|| 可以理解为或者的意思,(只要有一个语句为真,就为真,只有2个语句都为假时,才为假),例如:
int main()
{
int a = 10;
int b = -1;
if (a > 0 || b > 0)如果a>0或者b>0,那么打印hehe
{
printf("hehe\n");
}
return 0;
}
3. !----逻辑非
逻辑取反,(真为假,假为真)例如:
int main()
{
int a = 0;
if (!a)//如果a为假,那么!a就为真,打印hehe
{
printf("hehe");
}
return 0;
}
四. 移位操作符
在讲解移位操作符之前给大家科普一些内容:进制的概念,原码、反码、补码的概念。
1.进制的概念
在生活中,我们最常见的就是10进制(满10进1),但是我们经常还能听到2进制、8进制、16进制 这样的讲法,2进制、8进制、10进制、16进制、这些都是数值的不同表⽰形式。我们看看数值15的各种进制的表⽰形式:
15的2进制:1111
15的8进制:17
15的10进制:15
15的16进制:F
在我们的计算机当中,计算机是使用二进制作为数字编码系统,所有数据都是以二进制形式存储和传输的。接下来我们重点讲解一下2进制:
10进制中满10进1 ,10进制的数字每⼀位都是0~9的数字组成, 其实⼆进制也是⼀样的 ,2进制中满2进1 ,2进制的数字每⼀位都是0~1的数字组成 ,例如 1001 就是⼆进制的数字。
1.1 2进制转10进制
2进制该如何转换成10进制呢,例如上面提到的15的2进制是1111
1111转换成10进制15
从右边往左边进行计算就是:
1*2的3次方+1*2的2次方+1*2的1次方+1*2的0次方1*8+1*4+1*2+1*1=15
如果是1100呢?其实是一样的:
1*2的3次方+1*2的2次方+0*2的1次方+0*2的0次方
1*8+1*4+0*2+0*1=12
相信大家都学会了2进制如何转换成10进制了,那么反过来10进制怎么转换成2进制呢?
15转换成2进制也是如此:
15/2=7余数为1
7/2=3余数为1
3/2=1余数为1
1/2=0余数为1
从下往上得到的余数就是1111,所以1111就是15的2进制表示方式
1.2 2进制转换成8进制
8进制的数字每⼀位是0~7组成的,从2进制序列中右边低位开始向左每3个2进制位会换算⼀ 个8进制位,剩余不够3个2进制位的直接换算。
因为0~7的数字,各⾃写成2进制,最多有3个2进制位就⾜够了
如:2进制的 01 101 011,换成8进制:0153,0开头的数字,表示该数字为8进制
011
0*2的2次方+1*2的1次方+1*2的0次方
0*4+1*2+1*1=3
101
1*2的2次方+0*2的1次方+1*2的0次方
1*4+0*2+1*1=5
01
0*2的1次方+1*2的0次方
0*2+1*1=1
1.3 2进制转换成16进制
16进制的数字每⼀位是0~9, a~f(10-15) 组成的,f 的⼆进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进 制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。
因为0~9, a~f (10-15) 的数字,各⾃写成2进制,最多有4个2进制位就⾜够了
例如2进制的 0110 1011,换成16进制:0x6b,0x开头的数字表示该数字为16进制
1011
1*2的3次方+0*2的2次方+1*2的1次方+1*2的0次方
1*8+0*4+1*2+1*1=11(b)
0110
0*2的3次方+1*2的2次方+1*2的1次方+0*2的0次方
0*8+1*4+1*2+0*1=6
2.原码、反码、补码
整数分为有符号整数(signed int )和无符号整数(unsigned int ),而整数的2进制表⽰⽅法有三种,即原码、反码和补码。有符号整数的三种表⽰⽅法均有符号位和数值位两部分,2进制序列中,最⾼位的1位是被当做符号位,剩余的都是数值位。 符号位都是⽤0表⽰“正”,⽤1表⽰“负”。
正整数的原、反、补码都相同
负整数的三种表⽰⽅法各不相同
原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码
补码:反码+1就得到补码。
补码得到原码也是可以使⽤:取反,+1的操作。
在计算机中,对于整形计算是用补码进行计算的,计算完成后以原码的形式呈现
例如:
int main()
{
int a = 8;
int b = -4;
int c = a + b;
printf("%d", c);
return 0;
}
b的2进制原码:10000000 00000000 00000000 00000100
b的2进制反码:11111111 11111111 11111111 11111011
b的2进制补码:11111111 11111111 11111111 11111100
a的2进制补码:00000000 00000000 00000000 00001000(正整数原码,反码,补码相同)
相加后得到:100000000 00000000 00000000 00000100(满2进1)我们保留32位(整形是4个字节=32bit位)忽略溢出的最高位,即得到:00000000 00000000 00000000 00000100
发现符号位为0,正整数的原码,反码,补码相同,所以直接转换成10进制就是4
那么为什么整形数据存放在内存中其实存放的是补码呢?
计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀ 处理;同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算 过程是相同的,不需要额外的硬件电路。所以在计算的时候,计算机是使用补码进行计算的,再以原码的形式打印出来。
3. << 左移操作符
左移操作符的移位规则:左边抛弃、右边补0(移位移的也是补码)
例如:
int main()
{
int num = 10;
int n = num<<1;// <<右边数字是多少,就移动多少位
printf("n= %d\n", n);
printf("num= %d\n", num);
return 0;
}
如果想让num的值一起发生改变,将代码中的 int n=num<<1改成 int n=num<<=1即可,使左移操作符变成一种赋值操作符。
4. >> 右移操作符
右移操作符的移位规则分为2种:
1. 逻辑右移:左边⽤0填充,右边丢弃
2. 算术右移:左边⽤原该值的符号位填充,右边丢弃
使用哪种移位规则,取决于编译器,大部分编译器采用的都是算数右移
int main()
{
int num = -10;
int n = num >> 1;
printf("n= %d\n", n);
printf("num= %d\n", num);
return 0;
}
逻辑右移一位演示:
算数右移一位演示:
左移操作符和右移操作符的特点:通过上面例子我们可以发现 一个数左移一位后的值等于原来的值*2,一个数右移后的值等于原来的值/2。
注意:对于移位运算符,不要移动负数位,这个是标准未定义的,移位操作符的操作数只能是整数,并且移动的范围应该在0-32之间,因为整形的2进制位最多为32位bit。
五.位操作符:&、|、^、~
位操作符中的位指的是2进制位,也是对2进制中的补码进行操作的。
1.&---按位与
与运算的规则: 2个操作数补码中的2进制位一 一对应,有0则为0,全部为1则为1
例如:
int main()
{
int a = 10;
int b = 8;
int c = a & b;
printf("%d", c);
return 0;
}
与运算过程:
最后a&b的结果为:
2.|-----按位或
或运算的规则: 2个操作数补码中的2进制位一 一对应,有1则为1,全部为0则为0
例如:
int main()
{
int a = -10;
int b = 8;
int c = a | b;
printf("%d", c);
return 0;
}
或运算过程:
最后a | b的结果为:
3. ^---按位异或
异或运算的规则: 2个操作数补码中的2进制位一 一对应,相同则为0,不同则为1
例如:
int main()
{
int a = 10;
int b = 5;
int c = a ^ b;
printf("%d", c);
return 0;
}
异或运算过程:
最后a ^ b的结果为:
按位异或的特点:
1.任何数与0异或都等于那个数本身
例如:
int main()
{
int a = 10;
int b = 0;
int c = a ^ b;
printf("%d", c);
return 0;
}
异或过程:
最后的值为:
2.任何数与本身异或的值为0
例如:
int main()
{
int a = 10;
int b = 10;
int c = a ^ b;
printf("%d", c);
return 0;
}
异或过程:
运行结果:
4. ~ ---按位取反
取反运算的规则: 操作数中的补码,1变成0,0变成1
例如:
int main()
{
int a = 10;
printf("%d", ~a);
return 0;
}
取反过程:
运算结果:
六. 逗号表达式
逗号表达式,就是⽤逗号隔开的多个表达式。
逗号表达式,从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。
例如:
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);
printf("%d", c);
return 0;
}
运行结果:
注意:逗号表达式不能只看最后一个表达式,前面的表达式可能会影响到后面表达式的值
逗号表达式可以减少一些不必要的代码:
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
//...
a = get_val();
count_val(a);
}
使⽤逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
这样在写代码的过程中是不是显得更加方便了呢
七. 下标访问[ ]、函数调⽤( )
7.1 下标引用操作符[ ]
在我们访问数组中的元素时,就会用到下标引用操作符
操作数:⼀个数组名 + ⼀个索引值(下标)
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("arr[5]=%d\n", arr[5]);
printf("arr[8]=%d\n", arr[8]);
return 0;
}
[ ]的两个操作数是数组名和下标
在访问数组中元素时,要确保下标是在有效范围内,不然会造成访问越界的现象:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("arr[10]=%d\n", arr[10]);
return 0;
}
7.2 函数调⽤操作符( )
当我们调用函数的时候,就会用到函数调⽤操作符
接受⼀个或者多个操作数:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数。
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = Add(10, 20);
printf("%d", a);
return 0;
}
调用Add函数和printf 库函数 ()就是函数调用操作符,函数调用操作符的操作数是不确定的,但是最少有一个操作数,就是函数名。
八. 结构成员访问操作符
结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个操作数。
例如:
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
int main()
{
struct Stu s1 = { "zhangsan", 20 };
struct Stu s2 = { .age = 20, .name = "lisi" };
printf("%s %d\n", s1.name, s1.age);
printf("%s %d\n", s2.name, s2.age);
return 0;
}
九. 操作符的属性:优先级、结合性
C语⾔的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。
1. 优先级
优先级指的是: 如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。
3 + 4 * 5;
表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * ),由于乘法 的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。
2 结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )
5 * 6 / 2;
上⾯⽰例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 , 再计算 / 2 。
运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列)
• 圆括号( () )
• ⾃增运算符( ++ ),⾃减运算符( -- )
• 单⽬运算符( + 和 - )
• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( < 、 > 等)
• 赋值运算符( = )
其他操作符在使⽤的时候可以查看下⾯表格
以上就是本期操作符的全部内容,如有错误欢迎在评论区指出~,后续会给大家更新一些有关位操作符的题目,让大家更深入体会 位操作符 的用法。