https://mp.weixin.qq.com/s/Srz_K3YVLWwH7LBQeHPVZg
[TOC]
1 运算符
~
,&
,|
,^
,<<
,>>
,&=
,|=
,^=
,>>=
,<<=
2 二进制
以2为基底表示的数字为二进制。
二进制1111
可表示为:
1*2^3 + 1*2^2 + 1*2^1 + 1*2^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
的补码。
以此类推,总的来说补码让计算机可以进行减法运算。
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.
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) = 4
11 的二进制是:
00000000000000000000000000001011
-11 的二进制是:
11111111111111111111111111110101
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);
如果 f_align 的值是4,就代表float 类型对象的对其要求是4,即存储float类型值需要相邻地址的字节数是4.
使用_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()函数释放之前分配的内存。
网友评论