IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++入门——位操作 -> 正文阅读

[C++知识库]C++入门——位操作

位操作

一些有关位操作的知识

  1. 常用的等式:-n = ~(n - 1) = ~n + 1

  2. 获取整数n的二进制中最后一个1:n & (-n)或者n & ~(n - 1)

  3. 去掉整数n的二进制中最后一个1:n & (n - 1)

结构声明中的冒号和数字

C语言的结构体可以实现位段(也称位域),它的定义形式是在一个定义的结构体成员后加上冒号,然后是该成员所占的位数。位段的结构体成员必须是int或unsigned int类型,不能是其他类型。位段在内存中的存储方式是由具体的编译器决定的

  1. 定义位段的长度不能大于存储单元的长度

  2. 一个位段如果不能放在一个存储单元里,那么它会把这个存储单元中剩余空间闲置,而从下一个存储单元开始存储下一个位段,即一个位段不能跨越两个存储单元,位段在一个存储单元中的存储是紧凑的

  3. 位段名缺省时称作无名位段,无名位段的存储空间通常不用,而位段长度为0位表示下一个位段存储在一个新的存储单元中。位段长度为0的时候,位段名必须缺省

  4. 一个结构体中既可以定义位段成员,也可以同时定义一般的结构体成员。这时,一般成员不和位段存储在同一个单元

举例:

#include <stdio.h>
typedef struct
{
    int a:2;
    int b:2;
    int c:1;
}test;

int main()
{
    test t;
    t.a = 1;
    t.b = 3;
    t.c = 1;
    printf("%d\n%d\n%d\n", t.a, t.b, t.c);
    return 0;
}

由于a占两位,而a被赋值为1,二进制就是01,因此%d输出的时候输出1;b也占两位,赋值为3,二进制是11,由于使用了%d输出,表示的是将这个b作为有符号int型来输出,这样的话二进制的11将会有一位被认为是符号位,并且两位的b也会被扩展为int类型,也就是4字节。

其实a也做了这种扩展,只是扩展符号位的时候,由于数字在计算机中存储都是补码形式,因此扩展符号位的时候正数用0填充高位,负数用1填充高位。因此,对于a来说,输出的时候被扩展为00000000 00000000 00000000 00000001,而b扩展为11111111 11111111 11111111 11111111,也就是-1。同理,t.c的输出也是-1

利用位运算计算数的绝对值

在计算机中,数字都是以补码的形式存在,求负数的绝对值,应该是不管符号位,所有位执行按位取反,末位加1操作即可

对于一个负数,将其右移31位后会变成0xffffffff,即-1;而对于一个正数而言,右移31位则为0x00000000,即0。所以,利用异或操作即可实现这一功能:

int MyAbs(int x)
{
    int y;
    y = x >> 31;
    return (x^y)-y;
}

求整型数的二进制表示中1的个数

方法一

判断数的二进制表示中每一位是否为1,如果为1,就在count上加1,而循环的次数就是n的位数,程序如下:

int func(unsigned int n)
{
    int count = 0;
    while(n)
    {
        count += n & 0x1u;
        n >>= 1;
    }
    return count;
}

但该方法在1比较稀疏时效率较低

方法二

当一个数被减1时,它最右边的那个值为1的位将会变成0,同时其右边的所有位都会变成1。因此,我们只需不断的执行x & (x - 1),就可以把二进制数中的最后一位1逐个去掉,当循环到x等于0的时候,循环的次数就是x对应的二进制数中1的个数,程序如下:

int func(int x)
{
    int count = 0;
    while(x)
    {
        count ++;
        x = x & (x - 1);
    }
    return count;
}

不使用sizeof,如何判断操作系统是16位,还是32位

如果没有强调不许用sizeof,如果在32位机器上,sizeof(int) = 4,如果在16位机器上,sizeof(int) = 2

方法一

一般而言,机器位数不同,其表示的数字的最大值也不同。在16位机器上,int可以表示-32768到+32767之间的数,也就是说,它能表示的最大值为32767,如果再加1,将会溢出为-32768。但是,在32位机器上,int能表示的最大值为2147483647,因此32767+1不会溢出。可以根据这个特性,判断系统是16位,还是32位

方法二

对类型为unsigned int的0取反,不同位数下的0值取反,其结果不一样。例如,在32位机器上,执行按位取反运算,结果为429496727295,。而在16位机器上,取反的结果值为65535,可以根据这个特性来判断系统是16位还是32位

大端与小端

采用小端模式的CPU对操作数的存放方式是从低字节到高字节,而大端模式对操作数的存放方式是从高字节到低字节。

80x86机是小端模式,单片机一般为大端模式。Intel处理器一般都是小端模式。

如何判断计算机处理器是大端还是小端

方法一

联合体union的存放顺序是所有成员都从低地址开始存放。

程序如下:

int checkCPU()
{
    {
        union w
        {
            int a;
            char b;
        }c;
        c.a = 1;
        return (c.b == 1);
    }
}
方法二

还可以通过指针地址来判断,由于在32位计算机系统中,short占两个字节,char占1个字节,所以可以采用如下方式判断:

int checkCPU()
{
    unsigned short usData = 0x1122;
    unsigned char *pucData = (unsigned char*)&usData;
    return (*pucData == 0x22);
}

不用除法操作符实现两个正整数的除法

方法一

可以根据除法运算的原理进行减法操作,用除数循环减被除数,减一次结果加1,知道刚好减为0或余数小于被除数为止

int div(int a, int b)
{
    int result = 0;
    if (b == 0)
    {
        printf("除数不能为0\n");
        return result;
    }
    while (a >= b)
    {
        result++;
        a = a - b;
    }
    return result;
}

当a很大,b很小时,这个算法的效率很低

方法二

翻倍比较法。对于方法一,如果每次采用将比较熟翻倍的比较方法,则算法效率能够得到极大优化

int MyDiv(int a, int b)
{
    int k = 0;
    int c = b;
    int res = 0;
    if (b == 0)
    {
        printf("除数不能为0\n");
        return 0;
    }
    if (a < b)
    {
        return 0;
    }
    for (; a >= c; c <<= 1; k ++)
    {
        if (a - c < b)
        {
            return 1 << k;
        }
    }
    return MyDiv(a - (c >> 1), b) + (1 << (k - 1));
}

方法三

采用移位操作实现:

int div(const int x, const int y)
{
    int left_num = x;
    int result = 0;
    while (left_num >= y)
    {
        int multi = 1;
        while (y * multi <= (left_num >> 1)
        {
            multi = multi << 1;
        }
        result += multi;
        left_num -= y * multi;
    }
    return result;
}

只用逻辑运算实现加法

对于二进制加法而言,1 + 1 = 01 + 0 = 10 + 1 = 10 + 0 = 0,与位运算中的异或类似,而且0 + 0的进位是0,1 + 0的进位是0,只有1 + 1的进位有效,这又与&运算相似,即只有1 & 1 = 1,又由于进位是进位到高一位,与<<运算相似。同时,num1和num2相&后,如果结果为0,那么不存在进位,运算完成。用递归实现:

int add(int num1, int num2)
{
    if (0 == num2)
    {
        return num1;
    }
    int sumTemp = num1 ^ num2;
    int carray = (num1 & num2) << 1;
    return add(sumTemp, carry);
}

非递归实现:

int add(int num1, int num2)
{
    int sum = 0;
    int num3 = 0;
    int num4 = 0;
    while ((num1 & num2) > 0)
    {
        num3 = num1 ^ num2;
        num4 = num1 & num2;
        num1 = num3;
        num2 = num4 << 1;
    }
    sum = num1 ^ num2;
    return sum;
}

用逻辑运算实现乘法

对于二进制数乘法:1011 * 1010,可以将表达式拆分为两个运算,1011*00101011*1000的和,而对于二进制的运算,左移1位,等价于乘以0010,左移3位,等价于乘以1000,所以,两者的乘积为101101011000的和

所以,乘法可以通过一些列移位和加法完成。最后一个1可通过b&~(b-1)求得,可通过b&(b-1)去掉,为了高效地得到左移的位数,可以提前计算一个map:

int multiply(int a, int b)
{
    bool neg = (b < 0);
    if (neg)
    {
        b = -b;
    }

    int sum = 0;
    map<int, int> bit_map;
    for (int i = 0; i < 32; i++)
    {
        bit_map.insert(pair<int, int>(1 << i, i));
    }
    while (b > 0)
    {
        int last_bit = bit_map[b & ~(b - 1)];
        sum += (a << last_bit);
        b &= b - 1;
    }

    if (neg)
    {
        sum = -sum;
    }
    return sum;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-15 15:59:28  更:2021-07-15 16:00:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/20 19:08:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码