文章目录
- Ⅰ操作符的分类
- Ⅱ 算术操作符
- Ⅲ 移位操作符
- ⒈左移操作符
- ⒉右移操作符
- Ⅳ 位操作符
- ⒈按位取
- ⒉按位与
- ⒊按位异或
- ⒋按位或
- ⒌位操作符练习题
- Ⅴ赋值操作符
- Ⅵ 单目操作符
Ⅰ操作符的分类
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
Ⅱ 算术操作符
操作符 | 名称 | 例子 | 结果 |
---|---|---|---|
+ | 加法运算符(双目) | 5 + 3 | 8 |
- | 减法运算符(双目) | 5 - 3 | 2 |
* | 乘法运算符(双目) | 5 * 3 | 15 |
/ | 除法运算符(双目) | 5 / 3,5.0 / 3.0 | 1,1.666667 |
% | 求余运算符(双目) | 5 % 3,5.0 % 3.0 | 2,出错 |
+ | 正号运算符(单目) | +5 | 5 |
- | 负号运算符(单目) | -5 | -5 |
这里有以下几点需要注意
- 对于整数间的除法采取直接舍弃小数部分的方式,而不是四舍五入.
- 对于浮点数间的除法则能获得一个相对逼近结果的值(如果除不尽或位数特别多的话)。
- 百分号(%)表示求余数的意思,但求余运算符要求两边的操作数都必须是整数,其结果也是整数。
Ⅲ 移位操作符
- C 语言中提供了可以将某个变量中所有的二进制位进行左移或右移的操作符:移位操作符。
- 将变量存储在内存中的二进制补码进行移动,左移一位为乘 2,右移一位为除 2。
操作符 | 名称 | 例子 | 结果 |
---|---|---|---|
<< | 左移操作符 | 6 << 1 | 12 |
>> | 右移操作符 | 6 >> 1 | 3 |
<< 左移操作符
>> 右移操作符
关于移位操作符的注意事项
- 移位操作符的操作数只能是整数。
- 整数在移位中,移动的是整数存储在内存中的二进制补码。
- 反码移动完了之后,如果想打印结果,打印出的结果是反码转成的原码。
- 移位操作并不会改变变量的值,如 a >> 1,并不会改变 a 的值,想改变 a 的值必须要 a = a >> 1 这么写。
⒈左移操作符
<< 左移操作符
- 左移操作符(<<)拥有两个操作数,但两者的意义是不一样的;
- 左边的操作数是即将被移位的数据,右边的操作数指定移动的位数。
- 移位规则:将左边移出的位数全部抛弃,右边用 0 填充空位。
举个栗子
- 注:一个整型应该有 32 个二进制位,但是这里为了方便画图就只使用 8 个比特位。
- 将一个二进制数 1100 1010 左移 2 位,就这样写:
11001010 << 2
- 结果就是 0010 1000,如下图所示。
- 这里演示的是负数的移位,想打印出来必须先将移位后的结果转换成原码才行。
⒉右移操作符
>> 右移操作符
- 右移操作符(>>)同样拥有两个操作数;
- 左边的操作数是即将被移位的数据,右边的操作数指定移动的位数。
右移操作符分为两种
- 逻辑移位:右边丢弃,左边补 0。
- 算术移位:右边丢弃,左边补原符号位。
1. 逻辑移位
- 右边丢弃,左边补 0。
- 一个二进制数 1100 1010 右移 2 位,就这样写:
11001010 >> 2
- 结果就是 00111010,如下图所示。
- 绝大部分机器都不用逻辑移位,因为就像这种,我给的是个负数,逻辑移位之后直接一下就变成正数了。
2. 算术移位
- 右边丢弃,左边补原符号位。
- 负数算术移位:将一个二进制数 1100 1010 右移 2 位,就这样写:
11001010 >> 2
- 结果就是 1111 1010,如下图所示。
- 正数算术移位:将一个二进制数 0011 0101 右移两位,就这样写:
00110101 >> 2
结果就是 0000 1101,如下图所示。
- 绝大部分机器用得都是算术移位,能够保证原来的数不管是正数还是负数,在移位之后都不会改变正负。
Ⅳ 位操作符
- 对整型的每一个二进制位进行操作。
操作符 | 含义 | 优先级 | 举例 | 说明 |
---|---|---|---|---|
~ | 按(二进制)位取反 | 高 | ~a | a 的二进制位上,所有的 0 变成 1,1 变成 0 |
& | 按(二进制)位与 | 中 | a & b | 只有 a 和 b 对应的二进制位上的值都为 1,结果才为 1;有 一个为 0,结果都是 0 |
^ | 按(二进制)位异或 | 低 | a ^ b | a 和 b 的对应二进制位上,相同为 0,相异为 1 |
| | 按(二进制)位或 | 最低 | a | b | a 和 b 的对应二进制位上,只要有一个为 1,结果为 1;只有 a 和 b 同时为 0,结果才为 0 |
⒈按位取
- 作用是将一个整数的二进制补码上的 1 变成 0, 0 变成 1。
举个栗子
- 将 -1 的补码按位取反,然后将结果打印出来
#include <stdio.h>
int main()
{
int a = -1;
// 10000000 00000000 00000000 00000001 :-1 的原码
// 11111111 11111111 11111111 11111110 :-1 的反码
// 11111111 11111111 11111111 11111111 :-1 的补码
// 00000000 00000000 00000000 00000000 :~a 的补码
// 00000000 00000000 00000000 00000000 :~a 的反码
// 00000000 00000000 00000000 00000000 :~a 的原码
printf("~a = %d\n", ~a);//打印的是~a的原码
return 0;
⒉按位与
- 对两个整数的二进制位补码进行按位与。
- 两个数的对应二进制位补码上,全 1 则 1。
举个栗子
- 将 3 的补码和 -5 的补码进行按位与( & )。
#include <stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a & b;
// 00000000 00000000 00000000 00000011 : 3 的补码
//& 11111111 11111111 11111111 11111011 :-5 的补码
//结果为 3
// 00000000 00000000 00000000 00000011 : 全 1 则 1
printf("%d\n", c);//将按位与后的补码结果转换成原码再进行打印
return 0;
}
⒊按位异或
- 只有当两个操作数对应的二进制(补码)位不同时,它的结果才为 1,否则为 0。
- 相同为 0,相异 为 1。
举个栗子
- 不能创建临时变量,交换两个变量的值。
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
printf("交换前:a = %d,b = %d\n", a, b);
a = a ^ b; // a = 3 ^ 5
b = a ^ b; // b = 3 ^ 5 ^ 5 --> b = 3
a = a ^ b; // a = 3 ^ 5 ^ 3 --> a = 5
printf("交换后:a = %d,b = %d\n", a, b);
return 0;
}
⒋按位或
- 对两个整数的对应二进制位补码进行按位或。
- 两个数的对应二进制位补码上,全 0 则 0。
举个栗子
- 将 3 的补码和 -5 的补码进行按位或( | )。
#include <stdio.h>
int main()
{
int a = 3;
int b = -5;
int c = a | b;
// 00000000 00000000 00000000 00000011 : 3 的补码
//| 11111111 11111111 11111111 11111011 :-5 的补码
// 11111111 11111111 11111111 11111011 : 全 0 则 0
// %d 是将上述结果转换成原码再进行打印
// 11111111 11111111 11111111 11111010 :反码
// 10000000 00000000 00000000 00000101 :原码为-5
printf("%d | %d = %d\n", a, b, c);
return 0;
}
⒌位操作符练习题
题目内容
- 编写代码实现:求一个整数存储在内存中的二进制中1的个数。
解题思路
- 定义一个变量 a 来存储输入的整数。
- 用变量 a & 1,这样就能获取到二进制位上最后一位的数字。
- 将 a & 1 的值赋给 n ,如果最后一位是1(n=1),则让计数器 +1;
- 获得了最后一位的数字之后,利用右移操作符将 a 的二进制位右移操 1 位,直到总共移了 32 位为止。
- 最后直接打印计数器的值即可。
代码实现
#include <stdio.h>
int main()
{
int a = 0;
int n = 0;
int count = 0;
int move = 32;
printf("请输入要统计的数:");
scanf("%d", &a);
int b = a;
while (move)
{
n = a & 1;
if (1 == n)
{
count++;
}
a = a >> 1;//将得到的最后一位去掉,移位了之后还必须将移位后的值赋给a
//不要像我一样想了半天
move--;//只能移动 32 位
}
printf("%d 二进制数中有 %d 个 1\n", b, count);
return 0;
}
结果演示
Ⅴ赋值操作符
- 前面讲过的那些操作符还可以和赋值号结合起来,使得代码更加简洁。
- 譬如说,a += b 等价于 a = a + b,其余复合赋值符同义。
复合赋值符
- +=、-=、*=、/=、%=。
- >>=、<<=。
- &=、|=、^=。
Ⅵ 单目操作符
- 只有一个操作数的操作符称为单目操作符。
操作符 | 名称 |
---|---|
! | 逻辑反操作 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
– | 前置、后置– |
++ | 前置、后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
1. “ ! ” 逻辑反
- 真变假,假变真(0 表示假,非 0 表示真)。
- 举例 !a:如果 a 为真,则 !a 为假;如果 a 为假,则 !a 为真。
2. “ & ” 取地址
- 取出一个变量在内存中的地址。
- 例如:取出变量 a 的地址,并打印出来。
- &a 取出的是 a 占的四个字节中得第一个字节的地址。
3. “ sizeof ” 求操作数的类型大小
- sizeof 是一个操作符,计算的是 变量/类型 所占内存空间的大小,单位是字节。
- sizeof (数组名) 计算的是整个数组的大小。
4. “ ++ ” 自增操作符
- 前置++:先让变量的值+1,再使用+1之后的变量的值。
- 后置++:先使用变量的值,再让变量的值+1。
自增操作符在单独使用的时候前置后置都没什么区别,如 a++,++a 意思都是 a = a + 1。
但是将 ++a 和 a++ 赋值给另一个变量的时候,两者的差异就产生了。
#include <stdio.h>
int main()
{
int a = 5;
int b = 0;
b = ++a;//先对自身进行++运算,再将变量a的值赋给b(此时变量a的值已经+1了)
printf("a = %d,b = %d\n", a, b);
a = 5;
b = a++;//先将变量a中保存的值赋给b,再对自身进行++运算
printf("a = %d,b = %d\n", a, b);
return 0;
}
5. “ - - ”自减操作符
- ++ 能够理解的话,-- 也是一回事。
6. “ * ” 间接访问操作符(解引用操作符)
- 当需要访问指针变量指向的数据时,可以使用解引用操作符 “ * ”。
- 解引用操作符跟定义指针变量一样都是使用星号 “ * ”,这属于符号的重用,即在不同的地方有不同的意义:
- 在定义时表示定义一个指针变量,在其他位置表示获取指针变量指向的变量的值。
- 直接访问:直接通过变量名来访问变量的值,称为直接访问;
- 间接访问:通过指针变量这样的形式来访问变量的值,称为间接访问。
#include <stdio.h>
int main()
{
int a = 10;
int* p = &a;
// a = 20; 这属于直接改动 a 里面的值
*p = 20;//通过p间接的改动a里面存放的值,*p 等价于 a
printf(" a = %d\n", a);// 20
printf("*p = %d\n", *p);// 20
return 0;
}
7.(类型)强制类型转换
- C 语言允许强制转换操作数的数据类型。做法就是在操作数的前面用小括号将目标数据括起来。
- 例如下方代码:
printf("整型输出:%d\n",1 + (int)1.8);
//将浮点型的 1.8 强制转换为整型.
- 并没有像想象中的那样进行四舍五入,因为 C 语言中,将小数强转成整型时,是直接丢弃小数部分的值。