源码补码反码
int main(int argc, const char * argv[])
{
// int占4个字节 1个字节8位
int num = 12;
/*
// 12的二进制
12在内存中存储的是它的补码
00000000 00000000 00000000 00001100
正数的特点:(三码合一) 正数的原码就是TA的反码就是TA的补码
-12
二进制的最高位我们称之为符号位
如果符号位是0代表是一个正数,
如果符号位是1代表是一个负数
10000000 00000000 00000000 00001100 (-12的原码)
11111111 11111111 11111111 11110011(反码, 符号位不变其它位取反)
11111111 11111111 11111111 11110011
+00000000 00000000 00000000 00000001
_____________________________________________
11111111 11111111 11111111 11110100(补码 , 反码+1)
结论:无论正数负数在内存中存储的都是补码
11111111 11111111 11111111 11110101 (补码)
-00000000 00000000 00000000 00000001 (-1)
_____________________________________________
11111111 11111111 11111111 11110100 (反码)
10000000 00000000 00000000 00001011
*/
printf("%d\n", 0b11111111111111111111111111110101);
return 0;
}
位运算
/*
位运算都是针对二进制的
&
|
^
~
<<
>>
*/
int main(int argc, const char * argv[])
{
/*
& 按位与
特点:只有对应的两位都是1才返回1 否则返回0
口诀: 一假则假
规律:任何数按位与上1结果还是那个数
1001
& 0101
_______
0001
1001
&1111
______
1001
*/
/*
| 按位或
特点:只要对应的两位其中一位是1就返回1
口诀:一真则真
1001
| 0101
________
1101
*/
/*
^ 按位异或
特点:对应的两位不相同返回1 相同返回0
1001
^ 0101
_______
1100
// 多个整数按位异或的结果和顺序无关
1001
^ 0101
_______
1100
1100
^ 0110
_______
1010
1001
^ 0110
_______
1111
1111
^ 0101
_______
1010
// 相同整数按位异或结果是0
1001
^ 1001
_______
0000
// 任何整数按位异或上0结果不变
1001
^ 0000
_______
1001
// 任何整数按位异或上另一个整数两次结果还是那个数
1001
^ 1001
____________
0000
0000
^0101
______
0101
*/
// int result = 9 & 5;
// int result = 9 | 5;
// int result = 9 ^ 5;
// 多个整数按位异或的结果和顺序无关
// int result2 = 9 ^ 5 ^ 6;
// int result2 = 9 ^ 6 ^ 5;
// 相同整数按位异或结果是0
// int result3 = 9 ^ 9;
// 任何整数按位异或上0结果不变
// int result4 = 9 ^ 0 ;
// 任何整数按位异或上另一个整数两次结果还是那个数
// int result5 = 9 ^ 9 ^ 5;
// int result6 = 9 ^ 5 ^ 9;
// printf("result = %d\n", result6);
/*
~ 按位取反
特点: 0变1 1变0
0000 0000 0000 0000 0000 0000 0000 1001
~1111 1111 1111 1111 1111 1111 1111 0110 (补码)
0000 0000 0000 0000 0000 0000 0000 0001
______________________________________________
1111 1111 1111 1111 1111 1111 1111 0101 (反码)
1000 0000 0000 0000 0000 0000 0000 1010
*/
// int result = ~9;
//// printf("result = %d\n", result);
// printf("%d\n",0b11111111111111111111111111110110);
return 0;
}
左移右移
int main(int argc, const char * argv[]) {
/*
<< 左移
a << n 把整数a的二进制位往左边移n位
移出的位砍掉,低位补0, 发现左移会把原有的数值变大
9 << 1 = 18 9 * 2(1) = 18
9 << 2 = 36 9 * 2(2) = 26
9 << n = 9 * 2(n)
左移的应用场景:当要计算某个数乘以2的n次方的时候就用左移,效率最高
0000 0000 0000 0000 0000 0000 0000 0000
100 0000 0000 0000 0000 0000 0000 10010
注意点:左移有可能改变数值的正负性
*/
/*
>> 右移
a >> n 把整数a的二进制位往右边移n位
移出的位砍掉, 缺少的以为最高位是0就补0是1就补1(是在当前操作系统下)
9 >> 1 = 4 9 / 2(1) = 4
9 >> 2 = 2 9 / 2(2) = 2
右移的应用场景:当要计算某个数除以2的N次方的时候就用右移,效率最高
0000 0000 0000 0000 0000 0000 0000 0000
000000 0000 0000 0000 0000 0000 0000 10
*/
// int result = 9 << 2;
int result = 9 >> 2;
printf("result = %d\n", result);
return 0;
}
变量存储细节
int main(int argc, const char * argv[]) {
// 变量为什么要有类型? 每种类型占用的内存空间不一样 int 4, char 1 double 8
// 只要定义变量, 系统就会开辟一块存储空间给我们的变量存储数据, 内存寻址是从大到小
// 越先定义的变量, 内存地址越大
// 变量的地址就是所占的存储空间最小的字节地址
int num;
// 注意: 由于内存寻址是从大到小, 所以存储数据也是从大到小的存储(先存储二进制的高位, 再存储低位)
// 高位 --> 低位
// 00000000 00000000 00000000 00001001
num = 9; // 9 -->二进制 -->存储(补码)
int value;
value = 600; //00000000 00000000 00000010 01011000
// %p是输出地址
// &变量名称, 是取出变量的地址
printf("num = %p\n", &num);
printf("value = %p\n", &value);
// 获取存储的每一位
char *c = &value;
for (int i = 0; i < sizeof(num); i++) {
int result = c[i]; // 取出每个字节中存储的数据
printf("%i\n", result);
}
return 0;
}