C语言中的位运算

前言:我个人认为,C语言的两个比较有特色的点,(1)在于存在指针。(2)可以执行位运算

因为这两种特色,c能够对底层数据,作出修改和使用,这也是人们又把C称为底层语言的原因

今天主要讲讲C中的位运算

位运算的符号(&,|,^,~,<<,>>)

位运算符解释说明优先级(按大小分为4个等级)结合方向运算规则说明
&(双目)按位与3置左向右全1为1,有0为0
|(双目)按位或5置左向右有1为1,全0为0
^(双目)按位异或4置左向右相异则1,相同则0
~(单目)按位取反1置右向左1变0,0变1
<<左移2置左向右数据左移
>>右移2置左向右数据右移

先看按位或运算 |

unsigned char aa=10;
char bb = -10;
char
 cc=aa|bb;
printf("看看cc的值是多少%d\n",cc);

先理论计算一下 cc的值 。unsigned char aa 的二进制值; 0000 1010

                                                                                                             |(按位或)

                                                             char bb的二进制值; 1000 1010

                                                      char cc 最终得到的值为; 1000 1010 =-10(10进制)

看看输出结果:

啥??为什么是-2,这又引出另外一个问题,负整数在内存中都是以补码的形式存储的。

要弄清楚补码,就得先知道反码

如 bb 

原码(注意不是源码,而是原码,即按二进制+最高位的符号位)反码反码规则补码补码规则

1000 1010

(bb即-10)

1111 0101反码(即符号位不动,其他数据Bit,按位取反)1111 0110在反码的基础上,加上1
0000 1010
(aa即10)
0000 10100000 1010
aa|bb的结果1111 1110
-2(1000 0010)1111 11011111 1110

因为表格有点问题:故最后三行,不能将补码,写在合适的位置。注意看最后一行-2的补码,和aa|bb的结果。于是问题就解决了。

总结的知识点

1)aa|bb的结果可以断定,按位或运算时,如果参与运算的数有有符号数。则符号位也要参与运算。也就是说,所有bit位都参与按位或运算

2)我查看了,部分资料描述道,正整数的补码和反码都是原码。感觉很啰嗦,而且这样记的话,也很容易出错。我们只需记住,正整数是是按原码存储在内存中的。

3)我们研究下,aa|bb的结果是如何被程序判定为-2的。cc=aa|bb=1111 1110,char cc声明时被定义为有符号数。故此时程序便去查看最高bit位 =1。于是判定为负数,1111 1110就是该数字的补码 。我们将这个补码转换为原码

首先符号位不动 ,数据位-1 =(1111 1110 -0000 0001)=1111 1101 ——》数据段取反=1000 0010 =-2

4)再看3)中我标绿的部分,请大家把 char cc声明修改为 unsigned char试试输出结果又会有什么不同。     

             

再看结果:           

也就是说,如果我们把按位或运算的值赋值给某个值,程序会根据这个值的类型来判断结果

5)根据第四条,我们又修改代码 printf("看看cc的值是多少%d\n",(aa|bb));    看看结果:

结论:当采取这种方式时,程序会自己创建一个临时变量来存储aa|bb的值,这个临时变量的默认是有符号型。

说明:本人使用的是codeblock 中的 GUN GCC编译器,而且都是默认设置。如果运算结果是和编译器及设置存在关系,请大佬指证一下。

此时又有同学提出问题,你这算的都是同一个类型的数据,如果不同数据类型,如char|float该怎么办?我们试一试 

unsigned char aa=10;
float bb = -10;
char cc=(aa|bb);
printf("看看aa|bb的值是多少%d\n",(aa|bb));
printf("看看aa|bb的值是多少%d\n",(aa|bb));

不过此时我还没打算放弃;我们将计算结果强行显示转换一下。

char cc = (char)(aa|bb);

结果如下图:也是不行。

于是我们只能修改为 float cc=(aa|bb); 看结果

结果依然是错误的。最后说结论:位运算只能是int (包括long short unsigned )char

 两种类型。

那么问题又来了,当参与位运算的两个数分别为char和int类型,编译器该怎么转换

        其中运算中(包含算术运算位运算,其中位运算,准确的来说是双目位运算)的类型自动转换遵循如下规则:

        1:char型、short型转换为int型(包括unsigned char, unsigned short),float型转换为double型;

        2:相同类型的操作数作算术运算,其结果为同一类型,即5/2 = 2而不是2.5;

        3:不同类型的操作数经规则1转换后仍为不同类型,则其中级别低的类型自动转换为级别高的类型后再进行运算;级别高低如下:

        char < short < int < unsigned int < long < unsigned long < float < double

        其中,需要注意的是int型数据和unsigned int运算时,int会先转换为unsigned int,再参与运算。(c语言中整形常量默认为int型,比如-2,实型常量为double型,比如3.2)
 

总结:隐式转换的优先级遵守以下两个规则:

1)短类型(指的是所占字节短)优先级<长类型(指的是所占字节长)

2)有符号类型<有符号类型

3)整形中 int(有符号+有符号)<long

总结:遇到数据隐式转换时,先用规则1,来评判。规则1不能解决时,采用规则2来解决

第3)点非常有必要单独拿出来讲讲,C99或C11标准都没有强制规定各类数据所占的字节数。

总体上遵守以下规则即可:

char < short <= int<= long <= float < double;

各类数据所占字节数;与编译器的位数直接相关,

常用类型延伸类型16bit平台(编译器)32Bit平台(编译器)64Bit平台(编译器)

char

unsigned char

111
shortunsigned short int222

int

unsigned int244
long

long int

unsigned int

448
long long(两个)long long int888
float/444
double/888

移位运算:

移位运算的两个大前提;

** 目前C中的移位运算只针对int(整形)和char(字符型,本质上也是整形)对double和float则不能执行移位运算。(多说一句,对于int数组和char数组只能对数组中单个元素执行移位操作)

**目前C规范只对unsigned类型,作出了强制规定。对有符号数则没有强制规定移位操作需要遵守的规范,目前各个编译器采取了不同的方案,具体看下文介绍。

我看过很多资料和书籍,很多是直接开始讲移位运算。很多都没有讲清楚两个前提,

1)“所谓左移<<,右移>>”是建立在书写习惯的基础上的。不提这一点前提的话,直接讲左移和右移都是很不负责任的讲法。

首先看我们的书写习惯

十进制数字 10  我们是先写1(高位),再写0(低位)。

如果用unsigned char类型存储

二进制表示为 :0000 1010 ,注意看我们这也是把高位写在左边  ,低位写在右边。

当执行左移时,其实就是高位侧移出。

当执行右移时,其实就是低位侧移出。

2)

另外还有一个概念,算术移位和逻辑移位

*逻辑移位:右移时,移除的地位丢弃,左边空出来高位的位置0填充。左移时移除的高位丢弃,空出的低位用0填充。

unsigned char num_1= 0x80;
for(int i=0;i<15;i++)
{   if(i<7)
    {
         num_1=num_1>>1;  //右移一位,
         printf("看看num_1左移一位后的值%x\n",num_1);
/*测试按位或*/
    }
   if(i>7)
   {
         num_1=num_1<<1;  //左移一位,
         printf("看看num_1左移一位后的值%x\n",num_1);
   }
};

查看结果:

算术移位:右移时,移除丢弃,空出高位用符号位填充。左移时符号位不动,只移动数据段,移出丢弃,空出低位填充0。

当char num_1=10,为正数时,执行算术移位时的操作,

0000 1010 >>3位=xxx 0 0001;

xxx代表空出来的高位,看前面的规则,使用符号位填充,符号位是0 则=000 0 0001;此时细心的同学发现了。当有符号数=正数时,执行算术右移时和逻辑移位好像没有区别。

0000 0001<<3 = 0000 1xxx,符号位不动,空出低位使用0填充=0000 1000

我们再看 0000 0001执行逻辑移位时:

0000 0001<<7 (执行逻辑移位)=1000 0000 则变成负数了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值