C语言中的按位移动及其简单引用
C语言中的按位移动及其简单应用
在C语言中按位左移用”<<”表示,按位右移用”>>”表示。
按位左移和按位右移运算经常被用来替换乘二和除二运算,但是要注意,这两者之间并不完全等价。下面就分析一下:
首先明确,按位移动分为逻辑移动和算术移动,具体就是:逻辑左移、算术左移;逻辑右移、算术右移。
算术左移、算术右移、逻辑左移、逻辑右移的操作如下图:
可以看到,逻辑左移=算术左移:都是左移然后右边补零
算术右移和逻辑右移有所区别:逻辑右移是右移并且左边补0,而算术左移是右移并且左边补1(也就是补符号位)
在C语言中:左移采用的逻辑左移(和算术左移的效果相同)和算术右移。由于算术左移和逻辑左移的效果相同,所以我们可以认为,C语言中采用的是算术移动(对于有符号数而言)。
那么算术左移、算术右移和乘二、除二运算结果是否一致呢?
先来看左移:
先给出结论:无论是正数还是负数,只要结果不溢出(也就是不超过取值范围)结果就是乘2。
比如:
输出为:
对于结果溢出的(也就是超出取值范围的),比如:
输出为:
发现没,两个结果都不对,因为char是8位的,表示范围为-128~127,结果产生溢出了。那为什么会产生这样的结果呢?
a = 65. 二进制位:0100 0001
逻辑左移一位结果为: 1000 0010 ,我们发现,这个数的符号位变成了1,也就是负数了,就是-126。细心的人也会发现,理想结果130、实际结果 -126、27,这三者之间貌似有某种联系,的确有联系,这里就不深究了。
再来看看右移:
先给结论:首先,右移,并不会造成结果溢出的情况。对于正数,放心大胆地去右移吧,结果都相当于除二;但是对于负数,结果不一定相等,最好不要用右移代替除二。
比如:对于正数
输出为:
结果都是正确的。
但是对于负数,比如:
输出为:
可以看到对于偶数的结果是正确的。但是对于奇数,结果的绝对值比除二的结果的绝对值大1。
那么能否实现逻辑右移呢?
上面提到了C语言中采用的是算术右移,那么有没有办法实现逻辑右移呢?可以的。
我们需要注意一下无符号数,无符号数并没有符号位,所以对无符号数进行的位移都是逻辑位移。对于一个有符号数,如果我们想对他进行逻辑右移,那么我们可以先将该数转换成对应对的无符号数,然后再进行右移操作。
比如对于char a = -6。
二进制位 1111 1010
如果是算术右移,结果应该为: 1111 1101 也就是-3
如果是逻辑右移,结果应该为: 0111 110 也就是125
下面我们验证一下:
输出为:
正确
位移运算的简单应用
有这样一个题,要求是将一个int型的数据转换成二进制和16进制的字符串输出。
比如对于整型量456789
我们要输出:(空格不用了)
00000000 00000110 11111000 01010101
0x0006F855
对于求二进制,该数在内存中就是二进制的表示,我们只需要把每一位变成字符然后存到字符数组中即可,方法就是按位与和位移相结合,比如我们想得到第一个位(最高位),那么我们可以先按位与上10000000 00000000 00000000 00000000 然后将结果按位右移31位(需要注意,应该是采用逻辑右移,而不是算术右移,也就是前面要补0,而不是补1,方法是上面提到的,先将该数转换成无符号数),就得到了第一位的值,之后转换成字符即可。
对于求16进制,我们则需要以四个二进制位为一个单位,方法类似,思路就是想办法把当前要转换的四个二进制位移动到最低四位,因此我们可以用一个char来保存8位二进制,然后想办法把高四位置为0,把待转换的四位放到第四位。
比如说,我们现在想转换标红的部分
00000000 00000110 11111000 01010101
应该是转换成F,那么怎么转换呢?
首先我们先将该数左移16位,也就是4*4位,改数就变成了
11111000 01010101 00000000 00000000
然后我们在右移28位,也就是32-4位(特别注意,这里应该是逻辑右移,而不是算术右移,也就是要保证前面补0,而不是补1,方法就是上面提到的,先将该数转换成无符号数,然后再右移)
结果为:
00000000 00000000 00000000 00001111
最后在用char类型将数据的低8位截断,并转换成字符F即可。
源代码如下:
#include <stdio.h> #include <string.h> #include <stdlib.h> char *get_bin_string(int num) { unsigned int u_num = (unsigned int)num; char *buffer = (char *)malloc(); if(buffer == NULL) return NULL; buffer[] = '\0'; int i = ; for(;i < ;i++) { unsigned int temp = u_num &(<<( - i)); temp = temp>>(-i); buffer[i] = temp == ? '':''; } return buffer; } char *get_hex_string(int num) { unsigned int u_num = (unsigned int)num; char *buffer = (char *)malloc(); if(buffer == NULL) return NULL; //填写固定的部分 buffer[] = ''; buffer[] = 'x'; buffer[] = '\0'; char *temp = buffer + ; int i = ; for(;i < ;i++) { unsigned int af = u_num<<( * i); af = af >> ; temp[i] = (char)(u_num<<( * i)>>); temp[i] = temp[i] < ? temp[i] + :temp[i] + ; } return buffer; } int main() { int num = ; char *bin = get_bin_string(num); char *hex = get_hex_string(num); printf("二进制为:%s\n",bin); printf("十六进制为:%s\n",hex); free(hex); free(bin); return ; }
最后再强调一下,右移的时候一定要考虑清楚,我们是想要前面补0还是补1,也就是采用逻辑右移还是算术右移,如果是采用逻辑右移,要先将该数转换成无符号数。
就到这里了。
最后附上word文件和源代码文件
链接:http://pan.baidu.com/s/1nuYBXjj 密码:5huf
如果你觉得对你有用,请赞一个吧~~~
C语言中的按位移动及其简单引用的更多相关文章
- Python语言中的按位运算
(转)位操作是程序设计中对位模式或二进制数的一元和二元操作. 在许多古老的微处理器上, 位运算比加减运算略快, 通常位运算比乘除法运算要快很多. 在现代架构中, 情况并非如此:位运算的运算速度通常与加 ...
- C语言中的重要位运算
1. 常用的等式 :-n = ~(n-1) = ~n + 1. 2. 获取整数n的人进制形式中的最后1个,也就是只保留最后一个1,其余的全部置位0,如1000 0011 ---> 0000 0 ...
- java中对于二位数组的简单操作,进而可以递推复杂操作
1.程序 2.结果 3.内循环的次数 arr[x].length
- C语言:值传递,地址传递和引用传递(example:值交换)
于C语言中值传递.地址传递和引用传递的我个人理解. 通过一个例子:swap(交换两个整型变量的值)来表现! #include <stdio.h> void swap1(int* a,int ...
- C语言中两位ASCII码可以表示汉字
最近偶然有人问到这个相关字符编码的问题,所以百度了下参考了这两个资料,进行了简单分析. ******************************************************** ...
- C语言中的位运算和逻辑运算
这篇文章来自:http://blog.csdn.net/qp120291570/article/details/8708286 位运算 C语言中的位运算包括与(&),或(|),亦或(^),非( ...
- C/C++语言中的位运算
在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作. 一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率.C语 ...
- C语言中的左移与右移 <<, >> 位运算
这里参考了一篇很好的位运算,涉及到位运算可能会遇到的正负号问题,左右溢出怎么处理问题. 参考: 1. https://www.cnblogs.com/myblesh/articles/2431806. ...
- 【C语言】求两个数中不同的位的个数
//求两个数中不同的位的个数 #include <stdio.h> int count_different(int a, int b) { int count = 0; int c = a ...
随机推荐
- const 用法全面总结
C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,本人根据各方面查到的资料进行总结如下,期望对朋友们有所帮助. Const 是C++中常用的类型修饰符,常类型是指使用类 ...
- Spring Boot 2 实践记录之 使用 ConfigurationProperties 注解将配置属性匹配至配置类的属性
在 Spring Boot 2 实践记录之 条件装配 一文中,曾经使用 Condition 类的 ConditionContext 参数获取了配置文件中的配置属性.但那是因为 Spring 提供了将上 ...
- Replication--查看未分发命令和预估所需时间
当复制有延迟时,我们可以使用复制监视器来查看各订阅的未分发命令书和预估所需时间,如下图: 但是当分发和订阅数比较多的时候,依次查看比较费时,我们可以使用sys.sp_replmonitorsubscr ...
- NET项目发布到IIS上报错:HTTP 错误 403.14
NET项目发布到IIS上报错:HTTP 错误 404.0 - Not Found 原因:由于本机开发环境是Net4.5,所以虽然创建项目时选择的是net framework4.5的,但是webconf ...
- C# OleDbConnection对特定部分Excel的数据读取
最近在写winform程序,先来一个简单的. 读取特定部分Excel的数据读取,读取Excel第30行开始到H列的数据 using System;using System.Collections.Ge ...
- sqlServer 查询表中31到40的记录,考虑id不连续的情况
SQL 查询表中31到40的记录,考虑id不连续的情况 写出一条sql语句输出users表中31到40记录(数据库为SQL Server,以自动增长的ID作为主键,注意ID可能不是连续的)? -- ...
- 基于GitLab与Git Extensions搭建版本控制工具
1.背景 大家知道GitHub是现在非常流行的代码托管工具,但是如果有些项目不想开源的话,则需要付费,因此萌生了自己搭建一个Git的版本控制工具,供内网使用.GitLab则是个好的选择,但是GitLa ...
- openvswitch datapath 内核态流表创建过程(ovs_flow_cmd_new)
datapath流表更新的入口函数都定义在dp_flow_genl_ops中,流表创建的入口函数是ovs_flow_cmd_new函数,通过该函数,我们可以一窥流表相关信息的建立. 1.ovs_flo ...
- FFMpeg的一些基础配置
一 . CMakeLists.txt文件的使用 1.添加头文件的相对路径 : include_directories(include(这里面就是文件的名字)) 2.设置ffmpeg的库的路径(v7a或 ...
- 《Python黑帽子:黑客与渗透测试编程之道》 网络基础
TCP客户端: 示例中socket对象有两个参数,AF_INET参数表明使用IPv4地址或主机名 SOCK_STREAM参数表示是一个TCP客户端.访问的URL是百度. #coding=utf-8 i ...