【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题

简介: 【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题

法一:取模与取余

分析:

       数据在内存中以补码形式存储

      题目要求我们求一个数在内存中二进制中1的个数,从这里可以想到,我们需要定义一个变量count来计数,再得到二进制的每一位,并且再判断它是否为1,这道题就差不多解决了。但问题就是如何得到二进制的每一位?我们知道二进制的每一位要么是0要么是1,因此求二进制中1的个数,只需%2,看得到的余数是不是1,如果是1,count++,之后/2使得这个二进制去掉最后一位,如此循环往复,直到该数字为0,count的值就是1的个数。

#include <stdio.h>
int main()
{
  int num = 10;//00....01001
  scanf("%d", &num);
  int count = 0; //计数
  while (num)
  {
    if (num % 2 == 1)
      count++;
    num = num / 2;
  }
  printf("二进制中1的个数 = %d\n", count);
  return 0;
}

运行结果:

从运行结果来看,-1的输出是错的,因此,这种实现方法肯定有问题。

那是为什么呢?

现在,我们来找找问题:

输入-1时,由于num为负数,所以num/2的值为向下取整的结果,即-1/2=-1,此时num的值仍为-1,因此while循环不会结束,导致程序出现死循环。同时,由于num的值一直为负数,所以num % 2 的结果始终为0,导致count的值一直为0。

因此这个问题是由于-1是负数,那我们把num类型定为无符号整型,会整型会怎样呢?

     修改后:

#include <stdio.h>
int main()
{
    unsigned int num = 10;
    scanf("%u", &num);
    int count = 0; //计数
    while (num)
    {
        if (num % 2 == 1)
            count++;
        num = num / 2;
    }
    printf("二进制中1的个数 = %d\n", count);
    return 0;
}

运行结果:

答案正确。

这是因为将输入的无符号整数 num 强制转换为有符号整数 int 类型,导致输入的负数被解释为一个很大的正整数。当输入-1时,它被当作非常大的正整数(4294967295)来处理,然后计算其补码二进制表示中 1 的个数,最终输出结果为 32。

法二:按位与和移位操作符

    这里先上代码,再分析

int main()
{
  int num = -1;
    scanf("%d", &num);
  int i = 0;
  int count = 0; //计数
  for (i = 0; i < 32; i++)
  {
    if (num & (1 << i))
      count++;
  }
  printf("⼆进制中1的个数 = %d\n", count);
  return 0;
}

   我们以-1来分析:

  • 数据在计算机中的存储形式是补码,而程序打印数据是以二进制的原码形式转换成十进制
  • -1的原码:10000000000000000000000000000001
    -1的反码:11111111111111111111111111111110
    -1的补码:11111111111111111111111111111111
  • (1 << i)   :1向左移动 i 位

当 i 为0时,结果为0000......01;

当 i 为1时,1向左移动一位,最左边丢掉一位,右边补一个0,结果为0000.....10;

当 i 为2时,1左移两位,丢掉最左边两位,在最右边补两个0,结果为0000.......100

因此(1<<i)产生的效果就是让数字1存储在内存中唯一的二进制位 1 移动 i 位

  • &按位与运算符:对应二进制位有0,则0
  • num & (1 << i):当 i 为0 时,1111....11& 0000....01结果为0000.....01,即1,为真,count++;

       当 i 为1时,1111......11&0000....10结果为0000....10,即2,为真,count++;

       .

       .

       .

       最终结果为32

  法三:利用算法去掉二进制中最右边的1

       一个数字与上这个数字减一的数,该数二进制最右边的1必然会消除掉,以此类推,从右往左,每一次进行按位与操作,都会取消掉一个1,直到该数字变为0,跳出循环,就得到了该数字二进制中1的个数。

以3(0000...0011)为例:

num:             0000...0011 —>       3

num - 1:          0000...0010 —>       2  

num = 3 & 2:   0000...0010  —>      2

num - 1:          0000...0001  —>      1

num = 2 & 1:   0000...0000 —>        0

跳出循环

#include <stdio.h>
int main()
{
  int num = -1;
    scanf("%d",&num);
  int i = 0;
  int count = 0; //计数
  while (num)
  {
    count++;
    num = num & (num - 1);
  }
  printf("⼆进制中1的个数 = %d\n", count);
  return 0;
}

       已经到这里了,来道课外练习巩固吧!

课外练习1:用位运算判断一个数是否是2的次方数

       先自己思考动动手,再来看把!

#include <stdio.h>
int isPowerOfTwo(int n) 
{
  if (n <= 0) 
  {
    return 0;
  }
  return (n & (n - 1)) == 0;
}
int main()
{
  int num;
  scanf("%d", &num);
  if (isPowerOfTwo(num))
  {
    printf("%d 是2的次方数。\n", num);
  }
  else 
  {
    printf("%d 不是2的次方数。\n", num);
  }
  return 0;
}

简单分析:一个数如果是2的次方数,则它的二进制表示中只有一位是1,例如:1、2、4、8、16等。

课外练习2:编写代码将13二进制序列的第5位修改为1,然后再改回0

13的2进制序列: 00000000000000000000000000001101

将第5位置为1后:00000000000000000000000000011101

将第5位再置为0:00000000000000000000000000001101

同样的,还是要先自己练习了,再来看!

二进制序列的第n位修改为1的公式:a = a | (1 << n - 1)

分析:以a = a | (1 << n - 1)为例

         设 a = 2         // 0000...0010

  • (1 << i)   :1向左移动 i 位

       当 n为1时,结果为0000......01;

       当 n 为2时,1向左移动一位,最左边丢掉一位,右边补一个0,结果为0000.....10;

       当 n 为3时,1左移两位,丢掉最左边两位,在最右边补两个0,结果为0000.......100

       因此(1<<n - 1)产生的效果就是让数字1存储在内存中唯一的二进制位 1 移动 n - 1 位

  • | 按位或运算符:对应二进制位有1,则1
  • a |(1 << n - 1):设n为3时,0000....010 | 0000....100结果为 0000....110,这样就把第i位改为1了

以此类推,二进制序列的第n位修改为0的公式:a = a & ~ (1 << n - 1)

代码:

#include <stdio.h>
int main()
{
  int a = 13;
  a = a | (1 << 4);
  printf("a = %d\n", a);
  a = a & ~(1 << 4);
  printf("a = %d\n", a);
  return 0;
}

运行结果:

只有一点小小归纳,希望能帮到大家!

如果大家发现知识点错误的话,请帮忙指出,十分感谢!!

也请大家帮忙点赞、评论,这将督促我前行,大家一起加油!!!


目录
相关文章
|
5月前
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
1877 0
|
2月前
|
弹性计算 定位技术 数据中心
阿里云服务器配置选择方法:付费类型、地域及CPU内存配置全解析
阿里云服务器怎么选?2025最新指南:就近选择地域,降低延迟;长期使用选包年包月,短期灵活选按量付费;企业选2核4G5M仅199元/年,个人选2核2G3M低至99元/年,高性价比爆款推荐,轻松上云。
191 11
|
5月前
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
561 1
|
11月前
|
存储 安全 iOS开发
内存卡怎么格式化?6个格式化方法供你选
随着使用时间的增加,内存卡可能会因为数据积累、兼容性或是文件系统损坏等原因需要进行格式化。那么怎样正确格式化内存卡呢?格式化内存卡的时候需要注意什么呢?本文会给大家提供详细的步骤,帮助大家轻松完成格式化内存卡的操作。
|
5月前
|
存储 弹性计算 固态存储
阿里云服务器配置费用整理,支持一万人CPU内存、公网带宽和存储IO性能全解析
要支撑1万人在线流量,需选择阿里云企业级ECS服务器,如通用型g系列、高主频型hf系列或通用算力型u1实例,配置如16核64G及以上,搭配高带宽与SSD/ESSD云盘,费用约数千元每月。
510 0
|
6月前
|
存储 Windows
内存卡坏了还能修吗?4种常见修复方法
内存卡出现“无法保存”或“存储异常”等问题时,不一定是硬件损坏,可能是系统错误或文件系统异常导致。本文介绍几种亲测有效的修复方法:1) 更换读卡设备排除接触问题;2) 格式化修复文件系统(需先备份数据);3) 使用DiskGenius检测坏道;4) 借助厂商工具深度修复。同时提供日常保养建议,如避免高温环境、养成数据备份习惯,延长内存卡使用寿命。通过这些方法,多数问题可轻松解决,无需更换硬件。
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
937 159
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
329 6
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
1027 1