[C.C++] c语言位运算符

1060 0
Honkers 2025-3-14 00:35:14 | 显示全部楼层 |阅读模式

目录

1.位移运算符介绍

2.原码、反码、补码

3.运算符讲解

<< 左移动运算符

>> 右移运算符

& 按位与

| 按位或

^ 按位异或

~ 取反

4.位运算宏举例

获取低16位

获取某一位

2的n次幂

对位截取

参数说明

宏的工作原理

示例


1.位移运算符介绍

位运算符用来对二进制位进行操作,C语言中提供了如上表所示的位运算符。

位运算符中,除 ~ (取反)以外,其余均为双目运算符。

  • & 按位与
  • | 按位或
  • ^ 按位异或
  • ~取反
  • >>右移
  • <<左移

整数在内存中存储的形式是补码的二进制

位运算符是对补码进行运算

2.原码、反码、补码

整数的二进制表示:有3种(原码、反码、补码)

原码:直接根据数值写出的二进制序列就是原码(32位)

反码:原码的符号位不变,其他位按位取反就是反码

补码:反码加1,就是补码

对于正整数的原码、反码、补码都相同;负数是存放在二进制的补码中,负整数的原码、反码、补码都不相同。

 例如:1(正整数的原码、反码、补码都相同)(最高位是符号位,0 表示正数)

  1. 原码:0000000 00000000 00000000 00000001
  2. 反码:0000000 00000000 00000000 00000001
  3. 补码:0000000 00000000 00000000 00000001
复制代码

例如:-1(负整数的原码、反码、补码都不相同)(最高位为是符号位,1表示负数)

  1. 原码:10000000 00000000 00000000 00000001
  2. 反码:11111111 11111111 11111111 11111110(按位取反,符号位不变)
  3. 补码:11111111 11111111 11111111 11111111(反码加1)
复制代码

3.运算符讲解

<< 左移动运算符

规则:左移运算是将一个二进制位的操作数按指定移动的位数向左移动,移出位被丢弃,右边移出的空位一律补0。

简单说就是:左边丢弃,右边补0

举例说明

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int a = -5;
  5. int b = a << 1;
  6. printf("%d\n", a); // -5
  7. printf("%d\n", b); // -10
  8. return 0;
  9. }
复制代码

过程说明

先写出 -5 的补码

  1. 原码:10000000 00000000 00000000 00000101 (最高位为1)
  2. 反码:11111111 11111111 11111111 11111010 (按位取反,符号位不变)
  3. 补码:11111111 11111111 11111111 11111011 (反码加1)
复制代码

补码向左移动一位,左边去掉,右边补0,得到

  1. 11111111 11111111 11111111 11110110
复制代码

反推原码进行打印(%d为打印形式,因此对补码按int解读,首位为符号位)

  1. 补码:11111111 11111111 11111111 11110110
  2. 反码:11111111 11111111 11111111 11110101(补码 -1 得到反码)
  3. 原码:10000000 00000000 00000000 00001010(按位取反得到原码)
复制代码

此时得到的原码打印就是-10了

左移操作符通常用于对数值进行快速的乘以2的幂次方的操作,因为每向左移动一位就相当于乘以2。

也常用于在底层编程中操作位字段。

  1. int a = 5; // 二进制表示为 0000 0101
  2. int n = 2;
  3. int result = a << n; // 结果是 20,二进制表示为 0001 0100
  4. int a = 7; // 二进制表示为 0000 0111
  5. int result = a << 3; // 结果是 56,因为 7 * 2^3 = 56
  6. // 二进制表示:0000 0111 -> 0011 1000
复制代码

>> 右移运算符

右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位一律补0。然后符号位与原来保持一致。

举例说明

  1. int main()
  2. {
  3. char a = -126;
  4. char b = a >> 1;
  5. printf("%d\n", a); // -126
  6. printf("%d\n", b); // -63
  7. return 0;
  8. }
复制代码

-126的补码

  1. 原码:1111 1110
  2. 反码:1000 0001
  3. 补码:1000 0010
复制代码

右移1位

  1. 补码:1000 0010
  2. 右移一位后的补码:0100 0001
  3. 符号位与原来保持一致:1100 0001
  4. 反码:1100 0000
  5. 原码:1011 1111
复制代码

1011 1111就是-63

这个操作通常用于对整数进行快速的除以2的幂次方的操作,或者用于在处理位字段时调整位的位置。

  1. #include <stdio.h>
  2. int main()
  3. {
  4. // 7 >> 1 就是 7 / 2
  5. printf("%d\n", 7 >> 1); // 3
  6. return 0;
  7. }
复制代码

& 按位与

规则:两个二进制操作数对应位同为 1 ,结果位才为 1 ,其余情况为 0 。

示例代码

  1. #include <stdio.h>
  2. int main()
  3. {
  4. unsigned char a = 252;
  5. unsigned char b = 143;
  6. char c = a & b;
  7. printf("%d\n", c); // -116
  8. return 0;
  9. }
复制代码

解释

  1. a的补码: 1111 1100
  2. b的补码: 1000 1111
  3. 按&运算------------
  4. c的补码: 1000 1100
复制代码

注意c类型为char,是有符号的,首位看作符号,获取原码

  1. c的补码: 1000 1100
  2. c的反码:1000 1011
  3. c的原码:1111 0100
复制代码

111 0100对应116.所以打印-116

| 按位或

规则:两个二进制操作数对应位只要有一个为 1 ,结果 位 就为 1 ,其余情况为 0 

示例代码

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int a = 5;
  5. int b = -2;
  6. int c = a | b;
  7. printf("%d\n", c); // -1
  8. return 0;
  9. }
复制代码
  1. 5 的补码:00000000 00000000 00000000 00000101
  2. -2 的补码:11111111 11111111 11111111 11111110
  3. 5 | -2 的补码:11111111 11111111 11111111 11111111
复制代码

补码,需要原码才能打印

  1. 5 | -2 的补码:11111111 11111111 11111111 11111111
  2. 5 | -2 的反码:11111111 11111111 11111111 11111110
  3. 5 | -2 的原码:10000000 00000000 00000000 00000001
复制代码

^ 按位异或

规则:两个二进制操作数对应 位 相同为 0 ,不同为 1

示例

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int a = 5;
  5. int b = -2;
  6. int c = a ^ b;
  7. printf("%d\n", c); // -5
  8. return 0;
  9. }
复制代码

过程

  1. ​ 5 的补码:00000000 00000000 00000000 00000101
  2. -2 的补码:11111111 11111111 11111111 11111110
  3. 5 ^ -2 的补码:11111111 11111111 11111111 11111011
复制代码

补码,需要原码才能打印

  1. 5 ^ -2 的补码:11111111 11111111 11111111 11111011
  2. 5 ^ -2 的反码:11111111 11111111 11111111 11111010
  3. 5 ^ -2 的原码:10000000 00000000 00000000 00000101
复制代码

~ 取反

规则:一个二进制操作数,对应位为 0 ,结果位为 1 ;对应位为 1 ,结果位为 0
作用是将每位二进制取反

示例

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int a = 5;
  5. int c = ~a;
  6. printf("%d\n", c); // -6
  7. return 0;
  8. }
复制代码

过程

  1. ​ 5 的补码:00000000 00000000 00000000 00000101
  2. 取反:11111111 11111111 11111111 11111010
  3. 反码:11111111 11111111 11111111 11111001
  4. 原码:10000000 00000000 00000000 00000110
复制代码

4.位运算宏举例

获取低16位

// 定义一个宏,提取低16位赋值给uint16_t  
#define EXTRACT_LOW_16_BITS(value) ((uint16_t)((value) & 0xFFFF)) 
 

  1. uint32_t idx = 0x12345678; // 假设这是你的原始32位值
  2. uint16_t index; 
  3. // 使用宏来提取低16位  
  4. index = EXTRACT_LOW_16_BITS(idx); 
  5. // 此时index将包含0x5678
  6. // 你可以安全地将这个值作为uint16_t类型使用
复制代码

// 定义一个宏,提取低8位赋值给uint8_t  

#define EXTRACT_LOW_8_BITS(value) ((uint8_t)((value) & 0xFF))

获取某一位

// 获取第bitPos位的bit值,0,1,2....等

#define GET_BIT(num, bitPos) ((num >> bitPos) & 0x1)  
// 通过右移操作将目标位移动到最低位
// 然后使用位与操作(&)与1进行运算,以获取最低位的值  

2的n次幂

#define Power_Two(n) ((uint32)(0x1UL << (n)))

作用是计算并返回2的n次幂,其中n是函数的参数,表示幂次

0x1UL << (n) 是一个C或C++语言中的表达式,它结合了字面量、类型转换和左移操作符

  • 0x1:这是一个十六进制字面量,它等于十进制的 1
  • UL:这是两个字符的组合,分别代表 unsigned long 类型的字面量后缀。在这个上下文中,UL 主要用于确保字面量具有足够的位宽来容纳左移操作的结果。
  • <<:这是左移操作符。
  • 0x1UL << (n) 的意思是:将无符号长整型字面量 1(在二进制中表示为 0000 0001,这里省略了前导零以简化说明)向左移动 n 位。
  • 在它原来所在的位置和更高位之间填充了 0。
  • 如果 n 是 2,则 0x1UL << (n) 的结果是 4(在二进制中表示为 0000 0100)
     

对位截取

#define GET_BITS_U32(x, hi, lo) (((x) >> (lo)) & ((1U << ((hi) - (lo) + 1)) - 1))

这是一个宏定义,用于从一个无符号32位整数(x)中提取从低位(lo)到高位(hi,包括)之间的位。这个宏利用了位操作和位掩码的概念来实现其功能。下面是对这个宏的详细解释:

参数说明

  • x:待操作的无符号32位整数。
  • hi:需要提取的最高位的索引(从0开始计数)。
  • lo:需要提取的最低位的索引(从0开始计数)。

宏的工作原理

  1. 右移操作:((x) >> (lo)):这部分将x向右移动lo位。这样做的目的是将目标位段(从lo到hi)移动到最低位(即0到(hi-lo+1))的位置。
  2. 生成掩码:((1U << ((hi) - (lo) + 1)) - 1)
    • 1U 是一个无符号整数常量,值为1。1U << ((hi) - (lo) + 1) 将1向左移动(hi - lo + 1)位。这样做是为了创建一个掩码,这个掩码在第(hi - lo + 1)位上为1,其余位都是0。
    • 然后通过-1操作,第(hi - lo + 1)位上为1变为0,第0位到第(hi - lo)位为1。
  3. 按位与操作:(((x) >> (lo)) & ((1U << ((hi) - (lo) + 1)) - 1)):最后,将上述两步的结果进行按位与操作。得到从lo位到hi位的截取。

示例

假设x = 0b 1101 0010 1011 0011 0011 0011 0011 0011(二进制表示),hi = 10,lo = 4。

  • 首先,(x >> lo) 得到 0b 0000 1101 0010 1011 0011 0011 0011 0011。
  • 然后,(1U << ((hi) - (lo) + 1)) - 1 生成掩码 0b 0111 1111。
  • 最后,两者按位与得到 0b 011 0011,这就是从x中提取的从第4位到第10位(包括)的值。

end

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4010
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行