vlambda博客
学习文章列表

重温C语言(9)之位运算

  • 1 运算符

  • 2 二进制

    • 原码,反码, 补码

  • 3 按位逻辑运算符

    • (1)二进制反码或按位取反: ~

    • (2)按位与: &

    • (3)按位或:|

    • (4)按位异或: ^

  • 4 移位运算符

    • (1)左移:<<

    • (2)右移:>>

    • 编程:十进制转换为二进制

  • 5 位字段

  • 6 对齐特性

1 运算符

~,&,|,^,<<,>>,&=,|=,^=,>>=,<<=

2 二进制

以2为基底表示的数字为二进制。

二进制1111可表示为:

12^3+12^2+12^1+12^0

字节表示储存系统字符集所需的大小,1字节包含8位。

二进制无符号整数的范围是 0 ~ 255;
有符号整数的范围是 -127 ~ 127,因为八位的第一位储存符号(1负 0正),只剩下7位表示数字本身。也会产生 +0 和 -0 两个0.

原码,反码, 补码

计算机中存储的数据都是以二进制码存储的,程序对正数和负数都要进行计算,因此增加了符号位来区分正数和负数。计算机以二进制补码来表示有符号位。

计算机中的运算器只有加法运算器,所以计算机只能做加法,减一个数的运算也是通过加法实现相当于加上一个这个数的相反数。

  • 正数:原码=反码=补码

  • 负数:原码(符号位是1)=反码(除符号位,其他位取反)=补码(反码+1)

  • 0:原码=反码=补码=0

以8位二进制为例:
一个二进制和按位取反的结果相加,和必定是 11111111 即 -1, 例如十进制2, 00000010 反码是 11111101 它两相加(00000010 + 11111101 = 11111111)结果是 -1, 即 数+相反数=-1 得出 数+相反数+1=0 因而补码是 相反数+1 的结果。

以 2 - 2 举例: 即 2 + (-2),2在程序中的补码是00000010,-2 在程序中的原码是 10000010 反码是 11111101 补码(计算机中存储的数值)是 11111110 可以得出 00000010 + 11111110 = 00000000

以 6 - 8 举例: 即 6 + (-8),6在程序中的补码是00000110,-8 在程序中的原码是 10001000 反码是 11110111 补码(计算机中存储的数值)是 11111000 可以得出 00000110 + 11111000 = 11111110, 11111110 是 -2 的补码。

以此类推,总的来说补码让计算机可以进行减法运算。

重温C语言(9)之位运算

3 按位逻辑运算符

(1)二进制反码或按位取反: ~

-(10011010) 的结果是 (01100101), 取反就是0变1,1变0.


 unsigned char a = 2; // 00000010 a = ~a; printf("%d", a); // 11111101 即输出 253
 
   
   
 
 char a = 2; // 00000010  printf("%d", ~a); //输出 -3,因为~a取反是11111101第一个是符号位,计算机判断负数,取补码 10000011 即 -3

(2)按位与: &

(10010011)&(00111101) 结果是 00010001, 按位与就是两个对比位都是1才是1,否则是0.

 
   
   
 
 unsigned char a = 2; // 00000010 unsigned char b = 1; // 00000001 printf("%d", a & b); // 00000000

使用掩码,就是按位与运算的过程。

(3)按位或:|

(10010011)|(00111101) 结果是 10111111, 按位与就是两个对比位只要有一位是1结果就是是1,否则是0.

(4)按位异或: ^

(10010011)^(00111101) 结果是 10101110, 按位与就是两个对比位只要不同结果就是是1,否则是0.

重温C语言(9)之位运算

4 移位运算符

(1)左移:<<

(10010011) << 2 的结果是 01001100, 左移运算即把每一位向左移动指定位数,移出左端的值丢失,右端空出的位置补0.
一个数左移2位相当于该数乘以2的2次幂的结果。

(2)右移:>>

对无符号类型 (10010011) >> 2 的结果是 00100100;
对有符号位 (10010011) >> 2 的结果是 00100100 或 11100100, 根据系统的不同补充位可能是0也可能是符号位的副本;
右移运算即把每一位向右移动指定位数,移出右端的值丢失,对有符号值左端空出的位置根据系统的不同补0或符号位的副本,对无符号值用0填充。

对于非负数,一个数右移2位相当于该数除以2的2次幂的结果。

编程:十进制转换为二进制

 
   
   
 
#include "limits.h"
// 这个方法将整数转换为二进制字符串char * itobs(int n, char * ps){ int i; const static int size = CHAR_BIT * sizeof(int); for (i = size - 1; i >= 0; i--, n >>= 1) { ps[i] = (01 & n) + '0'; } ps[size] = '\0'; return ps;}

 
   
   
 
 printf("sizeof(int) = %ld\n", sizeof(int)); char bit_str[CHAR_BIT * sizeof(int) + 1]; int number = 11; itobs(number, bit_str); printf("%d 的二进制是:\n", number); for (int i = 0; bit_str[i]; ) { putchar(bit_str[i++]); }

输出:

 
   
   
 
sizeof(int) = 411 的二进制是:00000000000000000000000000001011
-11 的二进制是:11111111111111111111111111110101

重温C语言(9)之位运算

5 位字段

 
   
   
 
struct { unsigned int a: 1; unsigned int b: 1; unsigned int c: 1; unsigned int d: 1;} prnt;

prnt 包含 4 个 1位的字段,a,b,c,d是位字段,它们都确定了字段的宽度是1, 因为只有1位,所以只能赋值 0或1。
位字段是一个signedint或unsignedint类型变量中的一组相邻的位。可以通过位字段来规定字段的大小。

6 对齐特性

_Alignof运算符给出一个类型的对齐要求,在关键字_Alignof后面的圆括号中写上类型名即可:

size_t f_align = _Alignof(float);

使用_Alignas说明符指定一个变量或类型的对齐值。但是,不应该要求该值小于基本对齐值。

_Alignas(float) char c1;
_Alignas(8) char c2;

C11在stdlib.h库还添加了一个新的内存分配函数,用于对齐动态分配的内存。该函数的原型如下:

void * aligned_alloc(size_t alignment, size_t size);

第1个参数代表指定的对齐,第2个参数是所需的字节数,其值应是第1个参数的倍数。与其他内存分配函数一样,要使用free()函数释放之前分配的内存。