当前位置:网站首页>【C语言】符号的深度理解
【C语言】符号的深度理解
2022-07-04 20:33:00 【程序猿教你打篮球】
You wangt somenthing, go get it!
目录
1、按位运算符
1.1 按位或 ( | ) 和 按位与 ( & )
上期我们讲到过逻辑或和逻辑与,他们得到的结果是真假值,但我们一定要区分清楚,按位运算符 "|" 和 "&" 与逻辑运算符 "||" "&&" 是完全两个概念。
按位,简明之意,按数值二进制位来进行运算,都是在数据补码的基础上进行。
按位或 "|" : 两个数值的二进制补码对应位进行运算,对应位有 1 则为 1 ,否则为 0。
按位与 "&":两个数值的二进制补码对应位进行运算,对应位都为 1 则为 1, 否则为 0。
这里我们举例说明:
1 | 2 :
1 的二进制补码:0000 0000 ... 0000 0001
2 的二进制补码:0000 0000 ... 0000 0010
------按位或结果: 0000 0000 ... 0000 0011 -> 对应十进制:3
1 & 2:
1 的二进制补码:0000 0000 ... 0000 0001
2 的二进制补码:0000 0000 ... 0000 0010
------按位与结果: 0000 0000 ... 0000 0000 -> 对应十进制:0
其实有很多大学老师或者是书上都有可能把按位或,按位与,以及后面我们要讲的按位异或,他们会把每位二进制运算后的结果称为真或者假,其实这样的说法是不够严谨的,真假是逻辑判断,而按位运算得到的结果是数值,而且在C语言中0表示假,非0为真,所以我是不推荐这种说法。
1.2 按位异或 ( ^ )
按位或 "^" : 两个数值的二进制补码对应位进行运算,相同为 0 , 不同为 1。
这里我们举例说明:
1 ^ 3 :
1 的二进制补码:0000 0000 ... 0000 0001
3 的二进制补码:0000 0000 ... 0000 0011
---按位异或结果: 0000 0000 ... 0000 0010 -> 对应十进制:2
5 ^ 0 :
5 的二进制补码:0000 0000 ... 0000 0101
0 的二进制补码:0000 0000 ... 0000 0000
---按位异或结果: 0000 0000 ... 0000 0101 -> 对应十进制:5
结论:任何数异或0都等于它本身
这里有一道笔试题:不创建临时变量,实现两个数的交换。
//很多小伙伴直接想出来的做法:
int main()
{
int a = 10;
int b = 20;
printf("a = %d, b = %d\n", a, b);
a = a + b;
b = a - b;
a = a - b;
printf("a = %d, b = %d\n", a, b);
return 0;
}
但是我们仔细研究下这段代码,他有没有什么隐藏的问题呢?
一个整型,占四个字节,也就是 32 个比特位,这里进行加法运算,就会产生进位,万一我们是两个很大的数相加呢?他们的和超过了整型最大存储范围,那么在计算机里面就会发生截断!为了避免发生这种现象,我们可以采取异或的方法来实现这道题:
最后还有一个很简单的按位取反操作符:~
用途:对一个数的二进制按位取反(包括它的符号位)
注意:以上的位运算符, 他们的操作数必须是整数!
1.3 一个关于整型提升的问题
有这样一串代码,问:为什么一个char类型大小可以求出来是4字节?
无论任何位运算符,都是要计算机进行计算的,而计算机中CPU具有运算能力,但计算的数据都是放在内存中的。所以,做任何运算,都必须将数据从内存拿到CPU的寄存器中。而寄存器默认的操作数宽度是32位,可是,char类型数据只有1个字节,也就是8位,不满足32位怎么办,这就需要整型提升了!(详细整型提升大家可以查阅资料哦)
如果是一个有符号数的话:高位补符号位
如果是一个无符号数的话:高位补0
2、移位操作符
2.1 左移<< 右移>>操作符
<< 左移运算符是一个双目运算符,功能是把左边的运算数的各个二进制位向左移动指定位数。
>> 右移运算符是一个双目运算符,功能是把右边的运算数的各个二进制位向右移动指定位数。
注意:
<< 左移:最低位丢弃,最高位补零
>> 右移:
- 无符号数:最低位丢弃,最高位补零 [逻辑右移]
- 有符号数:最低位丢弃,最高位补符号位 [算数右移]
以上在补码中进行运算
警告:移位运算符,请不要移动负数位,这是标准未定义的!
左移我们好说,主要是右移我们需要细讲一下:
明显看到,这是在无符号数下进行右移,第一个小伙伴都不会感到惊讶, 可是第二个就有点不理解了,我们来解释下:
这里有一个问题,当 -1 准备放入变量 b 的时候我们需要看-1的类型吗?
答案是不需要!内存中放的都是二进制补码,本质上是把 -1 的补码放入变量 b 当中,第二,右移操作符属于计算,需要在CPU中进行,所以需要先把内存中 -1 的补码拿到CPU寄存器中运算,按照我们的规则,右移中,无符号数低位丢弃高位补零,所以 -1 右移完成之后就变成了 0111 1111 ... 1111 1111,接着我们以 %d 有符号整型打印,就会把他当作有符号数看待,最高位是 0 所以被认为是正数,转化成十进制也就是如上打印的值。
第二个我们来看下有符号数右移:
这个相信大家就很好理解了,第一个高位补符号位也就是补 0,低位丢弃,所以结果是 0,第二个高位补符号位也就是补 1,低位丢弃,值仍然不变,还是 -1。
注意:a>>1 并不会改变 a 变量的值,就好比如 a + 1。这样写才会改变:a = a >> 1;
2.2 习题练习
学完了上期的逻辑操作符,和本期的移位操作符,我们来练练手:
请你设计一个宏可以指定数据第几个比特位更改为 1 ,并设计一个函数将各个比特位打印出来。
//参考
#define SETBIT(a, num) ((a) |= (1 << (num - 1)) )
void PrintBit(int a)
{
int num = 31;
while (num >= 0)
{
if ((a & (1 << num)))
printf("1");
else
printf("0");
--num;
}
printf("\n");
}
int main()
{
int a = 0;
SETBIT(a, 5);
PrintBit(a);
return 0;
}
3、++ 和 -- 的操作
3.1 基本操作
其实这节知识点理解起来是很简单的,只不过总有些学校喜欢出一些很拉跨的题目:
int i = 3; 问:(++i) + (++i) + (++i) 的值是多少?
我的建议是,看到这类题,直接空着,你也可以在下面添一句,“ 你礼貌吗?”
这种表达式,在任何编译器下算出来的结果是不一样的!
对于这种问题没必要去争论谁对谁错, 如果有人想跟你杠的话,那么你直接告诉他,你真的超级高水平。
好了,言归正传,我们来说一下 ++ 和 -- 的基本理解:
- 前置++ -- : 先自增(减),再使用
- 后置++ -- : 先使用,再自增(减) 如果没有变量接收,那么直接自增。
例子:
基本使用就是这么多,接下来我们从汇编角度来深度理解一下:
3.2 从汇编角度深入理解 a++
既然我们知道,后置++ 是先使用后++,如果我们单纯的就 ++ 一下呢,他这个值被使用到了哪里去了呢?
int main()
{
int a = 0xDD;
int b = a++; //有b接收,那么a的先使用是将a的值(内容),放到b中
int c = 0xEE;
c++; //没有接收方,那么"先使用",如何理解?
return 0;
}
vs2019编译器反汇编:
结论:后置++ 完整的含义是先使用,在自增,如果没有变量接收,那么直接自增。
注意:在不同的编译器可能处理过程不同,不过这是一个基本的研究过程,比单纯的理论学习更严谨。
Calm, mature and precipitated
边栏推荐
- 杰理之AD 系列 MIDI 功能说明【篇】
- PS竖排英文和数字文字怎么改变方向(变竖直显示)
- [buuctf.reverse] 151_ [FlareOn6]DnsChess
- Huawei ENSP simulator configures ACL access control list
- MySQL - database query - use of aggregate function, aggregate query, grouping query
- HMS Core 机器学习服务
- Actual combat simulation │ JWT login authentication
- 华为ensp模拟器 DNS服务器的配置
- 华为ensp模拟器 配置ACL访问控制列表
- Y56. Chapter III kubernetes from entry to proficiency -- business image version upgrade and rollback (29)
猜你喜欢
JS卡牌样式倒计时天数
Advantages of RFID warehouse management system solution
admas零件名重复
Some suggestions for interface design
网件r7000梅林系统虚拟内存创建失败,提示USB磁盘读写速度不满足要求解决办法,有需要创建虚拟内存吗??
NetWare r7000 Merlin system virtual memory creation failed, prompting that the USB disk reading and writing speed does not meet the requirements. Solution, is it necessary to create virtual memory??
华为ensp模拟器实现通信安全(交换机)
Huawei ENSP simulator layer 3 switch
The concept and application of hash table
福昕PDF编辑器v10.1.8绿色版
随机推荐
网件r7000梅林系统虚拟内存创建失败,提示USB磁盘读写速度不满足要求解决办法,有需要创建虚拟内存吗??
杰理之AD 系列 MIDI 功能说明【篇】
heatmap.js图片热点热力图插件
五子棋 上班摸鱼工具 可局域网/人机
杰理之AD 系列 MIDI 功能说明【篇】
c语言函数形参自增自减情况分析
Huawei simulator ENSP common commands
Why does invariant mode improve performance
minidom 模塊寫入和解析 XML
[micro service SCG] use of predict
杰理之AD 系列 MIDI 功能说明【篇】
福昕PDF编辑器v10.1.8绿色版
[server data recovery] a case of RAID5 data recovery stored in a brand of server
HMS Core 统一扫码服务
Procurement in software development
Jerry's ad series MIDI function description [chapter]
华为ensp模拟器 实现多个路由器的设备可以相互访问
Explication détaillée du mécanisme de distribution des événements d'entrée multimodes
IIC (STM32)
D3.js+Three.js数据可视化3d地球js特效